Merge "Use stored power state for 2.0 devices" into sc-dev
diff --git a/Android.bp b/Android.bp
index 202e548..1c9eac9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -405,6 +405,7 @@
":framework-statsd-sources",
":framework-tethering-srcs",
":framework-wifi-updatable-sources",
+ ":ike-srcs",
":updatable-media-srcs",
],
visibility: ["//visibility:private"],
@@ -413,6 +414,7 @@
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 @@
name: "framework-all",
installable: false,
static_libs: [
+ "android.net.ipsec.ike.impl",
"framework-minus-apex",
"framework-appsearch.impl",
"framework-graphics.impl",
@@ -536,7 +539,7 @@
"android.hardware.vibrator-V1.3-java",
"android.security.apc-java",
"android.security.authorization-java",
- "android.system.keystore2-java",
+ "android.system.keystore2-V1-java",
"android.system.suspend.control.internal-java",
"cameraprotosnano",
"devicepolicyprotosnano",
@@ -760,6 +763,7 @@
srcs: [
":ipconnectivity-proto-src",
":libstats_atom_enum_protos",
+ ":libtombstone_proto-src",
"core/proto/**/*.proto",
"libs/incident/**/*.proto",
":service-permission-protos",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 49433f1..3f2e898 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -82,12 +82,13 @@
"android.hardware.vibrator-V1.3-java",
"framework-protos",
"stable.core.platform.api.stubs",
- // There are a few classes from modules used as type arguments that
- // need to be resolved by metalava. For now, we can use a previously
- // finalized stub library to resolve them. If a new class gets added,
- // this may be need to be revisited to use a manually maintained stub
- // library with empty classes in order to resolve those references.
- "sdk_system_30_android",
+ // There are a few classes from modules used by the core that
+ // need to be resolved by metalava. We use a prebuilt stub of the
+ // full sdk to ensure we can resolve them. If a new class gets added,
+ // the prebuilts/sdk/current needs to be updated.
+ "sdk_system_current_android",
+ // NOTE: The below can be removed once the prebuilt stub contains IKE.
+ "sdk_system_current_android.net.ipsec.ike",
],
high_mem: true, // Lots of sources => high memory use, see b/170701554
installable: false,
@@ -305,6 +306,7 @@
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",
@@ -327,6 +329,7 @@
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",
@@ -365,6 +368,7 @@
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",
@@ -404,7 +408,11 @@
"android_defaults_stubs_current",
"android_stubs_dists_default",
],
- libs: ["sdk_system_29_android"],
+ libs: [
+ "sdk_system_current_android",
+ // NOTE: The below can be removed once the prebuilt stub contains IKE.
+ "sdk_system_current_android.net.ipsec.ike",
+ ],
static_libs: ["art.module.public.api.stubs"],
dist: {
dir: "apistubs/android/module-lib",
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 2b12da2..6c265bc 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/OWNERS b/apct-tests/perftests/core/src/android/app/OWNERS
new file mode 100644
index 0000000..4f168ce
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/app/OWNERS
@@ -0,0 +1,2 @@
+per-file Overlay* = file:/core/java/android/app/RESOURCES_OWNERS
+per-file Resources* = file:/core/java/android/app/RESOURCES_OWNERS
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
index d3938f4..afd8e29 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 @@
}
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 f4c0a17..45c723b 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 @@
? 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 a3b8013..905000a 100644
--- a/apex/appsearch/framework/api/current.txt
+++ b/apex/appsearch/framework/api/current.txt
@@ -208,8 +208,8 @@
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 @@
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 0fcf061..656608d 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 @@
/** 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 2104198d..198eee8 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 @@
/** 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 0328d54..4869aa3 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 @@
/** 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 @@
/** 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 @@
/** 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 1b4d284..14dd472 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 @@
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 @@
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 @@
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 @@
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 3bbc945..271129b 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.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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+ 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 97b1a8c..5ea2a02 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 @@
/**
* 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 12699b7..6ba5572 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 20fb909..44d5180 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 @@
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/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 18856f7..82e967a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -16,33 +16,43 @@
package com.android.server.job;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.UserSwitchObserver;
import android.app.job.JobInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.UserInfo;
import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseIntArray;
+import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.util.StatLogger;
import com.android.server.JobSchedulerBackgroundThread;
+import com.android.server.LocalServices;
import com.android.server.job.controllers.JobStatus;
import com.android.server.job.controllers.StateController;
+import com.android.server.pm.UserManagerInternal;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -65,13 +75,18 @@
// Try to give higher priority types lower values.
static final int WORK_TYPE_NONE = 0;
static final int WORK_TYPE_TOP = 1 << 0;
- static final int WORK_TYPE_BG = 1 << 1;
- private static final int NUM_WORK_TYPES = 2;
+ static final int WORK_TYPE_EJ = 1 << 1;
+ static final int WORK_TYPE_BG = 1 << 2;
+ static final int WORK_TYPE_BGUSER = 1 << 3;
+ @VisibleForTesting
+ static final int NUM_WORK_TYPES = 4;
@IntDef(prefix = {"WORK_TYPE_"}, flag = true, value = {
WORK_TYPE_NONE,
WORK_TYPE_TOP,
- WORK_TYPE_BG
+ WORK_TYPE_EJ,
+ WORK_TYPE_BG,
+ WORK_TYPE_BGUSER
})
@Retention(RetentionPolicy.SOURCE)
public @interface WorkType {
@@ -94,49 +109,63 @@
private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_ON =
new WorkConfigLimitsPerMemoryTrimLevel(
- new WorkTypeConfig("screen_on_normal", 8,
+ new WorkTypeConfig("screen_on_normal", 11,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 2)),
+ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_EJ, 3),
+ Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 6))),
- new WorkTypeConfig("screen_on_moderate", 8,
+ List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
+ ),
+ new WorkTypeConfig("screen_on_moderate", 9,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2),
+ Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 4))),
- new WorkTypeConfig("screen_on_low", 5,
+ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
+ ),
+ new WorkTypeConfig("screen_on_low", 6,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1),
+ Pair.create(WORK_TYPE_BG, 1)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 1))),
+ List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+ ),
new WorkTypeConfig("screen_on_critical", 5,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 1)))
+ List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+ )
);
private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_OFF =
new WorkConfigLimitsPerMemoryTrimLevel(
- new WorkTypeConfig("screen_off_normal", 10,
+ new WorkTypeConfig("screen_off_normal", 13,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
+ Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 6))),
- new WorkTypeConfig("screen_off_moderate", 10,
+ List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
+ ),
+ new WorkTypeConfig("screen_off_moderate", 13,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
+ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_EJ, 3),
+ Pair.create(WORK_TYPE_BG, 2)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 4))),
- new WorkTypeConfig("screen_off_low", 5,
+ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
+ ),
+ new WorkTypeConfig("screen_off_low", 7,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 2),
+ Pair.create(WORK_TYPE_BG, 1)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 1))),
+ List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+ ),
new WorkTypeConfig("screen_off_critical", 5,
// defaultMin
- List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 1)),
// defaultMax
- List.of(Pair.create(WORK_TYPE_BG, 1)))
+ List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
+ )
);
/**
@@ -171,6 +200,10 @@
"assignJobsToContexts",
"refreshSystemState",
});
+ @VisibleForTesting
+ GracePeriodObserver mGracePeriodObserver;
+ @VisibleForTesting
+ boolean mShouldRestrictBgUser;
interface Stats {
int ASSIGN_JOBS_TO_CONTEXTS = 0;
@@ -182,9 +215,13 @@
JobConcurrencyManager(JobSchedulerService service) {
mService = service;
mLock = mService.mLock;
- mContext = service.getContext();
+ mContext = service.getTestableContext();
mHandler = JobSchedulerBackgroundThread.getHandler();
+
+ mGracePeriodObserver = new GracePeriodObserver(mContext);
+ mShouldRestrictBgUser = mContext.getResources().getBoolean(
+ R.bool.config_jobSchedulerRestrictBackgroundUser);
}
public void onSystemReady() {
@@ -193,10 +230,18 @@
final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
mContext.registerReceiver(mReceiver, filter);
+ try {
+ ActivityManager.getService().registerUserSwitchObserver(mGracePeriodObserver, TAG);
+ } catch (RemoteException e) {
+ }
onInteractiveStateChanged(mPowerManager.isInteractive());
}
+ void onUserRemoved(int userId) {
+ mGracePeriodObserver.onUserRemoved(userId);
+ }
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -224,7 +269,7 @@
Slog.d(TAG, "Interactive: " + interactive);
}
- final long nowRealtime = JobSchedulerService.sElapsedRealtimeClock.millis();
+ final long nowRealtime = sElapsedRealtimeClock.millis();
if (interactive) {
mLastScreenOnRealtime = nowRealtime;
mEffectiveInteractiveState = true;
@@ -261,7 +306,7 @@
if (mLastScreenOnRealtime > mLastScreenOffRealtime) {
return;
}
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+ final long now = sElapsedRealtimeClock.millis();
if ((mLastScreenOffRealtime + mScreenOffAdjustmentDelayMs) > now) {
return;
}
@@ -723,6 +768,10 @@
pw.print(mLastMemoryTrimLevel);
pw.println();
+ pw.print("User Grace Period: ");
+ pw.print(mGracePeriodObserver.mGracePeriodExpiration);
+ pw.println();
+
mStatLogger.dump(pw);
} finally {
pw.decreaseIndent();
@@ -748,15 +797,52 @@
proto.end(token);
}
+ /**
+ * Decides whether a job is from the current foreground user or the equivalent.
+ */
+ @VisibleForTesting
+ boolean shouldRunAsFgUserJob(JobStatus job) {
+ if (!mShouldRestrictBgUser) return true;
+ int userId = job.getSourceUserId();
+ UserManagerInternal um = LocalServices.getService(UserManagerInternal.class);
+ UserInfo userInfo = um.getUserInfo(userId);
+
+ // If the user has a parent user (e.g. a work profile of another user), the user should be
+ // treated equivalent as its parent user.
+ if (userInfo.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
+ && userInfo.profileGroupId != userId) {
+ userId = userInfo.profileGroupId;
+ userInfo = um.getUserInfo(userId);
+ }
+
+ int currentUser = LocalServices.getService(ActivityManagerInternal.class)
+ .getCurrentUserId();
+ // A user is treated as foreground user if any of the followings is true:
+ // 1. The user is current user
+ // 2. The user is primary user
+ // 3. The user's grace period has not expired
+ return currentUser == userId || userInfo.isPrimary()
+ || mGracePeriodObserver.isWithinGracePeriodForUser(userId);
+ }
+
int getJobWorkTypes(@NonNull JobStatus js) {
int classification = 0;
- // TODO(171305774): create dedicated work type for EJ and FGS
- if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP
- || js.shouldTreatAsExpeditedJob()) {
- classification |= WORK_TYPE_TOP;
+ // TODO: create dedicated work type for FGS
+ if (shouldRunAsFgUserJob(js)) {
+ if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
+ classification |= WORK_TYPE_TOP;
+ } else {
+ classification |= WORK_TYPE_BG;
+ }
+
+ if (js.shouldTreatAsExpeditedJob()) {
+ classification |= WORK_TYPE_EJ;
+ }
} else {
- classification |= WORK_TYPE_BG;
+ // TODO(171305774): create dedicated slots for EJs of bg user
+ classification |= WORK_TYPE_BGUSER;
}
+
return classification;
}
@@ -765,9 +851,15 @@
private static final String KEY_PREFIX_MAX_TOTAL =
CONFIG_KEY_PREFIX_CONCURRENCY + "max_total_";
private static final String KEY_PREFIX_MAX_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "max_top_";
+ private static final String KEY_PREFIX_MAX_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "max_ej_";
private static final String KEY_PREFIX_MAX_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "max_bg_";
+ private static final String KEY_PREFIX_MAX_BGUSER =
+ CONFIG_KEY_PREFIX_CONCURRENCY + "max_bguser_";
private static final String KEY_PREFIX_MIN_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "min_top_";
+ private static final String KEY_PREFIX_MIN_EJ = CONFIG_KEY_PREFIX_CONCURRENCY + "min_ej_";
private static final String KEY_PREFIX_MIN_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "min_bg_";
+ private static final String KEY_PREFIX_MIN_BGUSER =
+ CONFIG_KEY_PREFIX_CONCURRENCY + "min_bguser_";
private final String mConfigIdentifier;
private int mMaxTotal;
@@ -811,10 +903,18 @@
properties.getInt(KEY_PREFIX_MAX_TOP + mConfigIdentifier,
mDefaultMaxAllowedSlots.get(WORK_TYPE_TOP, mMaxTotal))));
mMaxAllowedSlots.put(WORK_TYPE_TOP, maxTop);
+ final int maxEj = Math.max(1, Math.min(mMaxTotal,
+ properties.getInt(KEY_PREFIX_MAX_EJ + mConfigIdentifier,
+ mDefaultMaxAllowedSlots.get(WORK_TYPE_EJ, mMaxTotal))));
+ mMaxAllowedSlots.put(WORK_TYPE_EJ, maxEj);
final int maxBg = Math.max(1, Math.min(mMaxTotal,
properties.getInt(KEY_PREFIX_MAX_BG + mConfigIdentifier,
mDefaultMaxAllowedSlots.get(WORK_TYPE_BG, mMaxTotal))));
mMaxAllowedSlots.put(WORK_TYPE_BG, maxBg);
+ final int maxBgUser = Math.max(1, Math.min(mMaxTotal,
+ properties.getInt(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
+ mDefaultMaxAllowedSlots.get(WORK_TYPE_BGUSER, mMaxTotal))));
+ mMaxAllowedSlots.put(WORK_TYPE_BGUSER, maxBgUser);
int remaining = mMaxTotal;
mMinReservedSlots.clear();
@@ -824,11 +924,23 @@
mDefaultMinReservedSlots.get(WORK_TYPE_TOP))));
mMinReservedSlots.put(WORK_TYPE_TOP, minTop);
remaining -= minTop;
+ // Ensure ej is in the range [0, min(maxEj, remaining)]
+ final int minEj = Math.max(0, Math.min(Math.min(maxEj, remaining),
+ properties.getInt(KEY_PREFIX_MIN_EJ + mConfigIdentifier,
+ mDefaultMinReservedSlots.get(WORK_TYPE_EJ))));
+ mMinReservedSlots.put(WORK_TYPE_EJ, minEj);
+ remaining -= minEj;
// Ensure bg is in the range [0, min(maxBg, remaining)]
final int minBg = Math.max(0, Math.min(Math.min(maxBg, remaining),
properties.getInt(KEY_PREFIX_MIN_BG + mConfigIdentifier,
mDefaultMinReservedSlots.get(WORK_TYPE_BG))));
mMinReservedSlots.put(WORK_TYPE_BG, minBg);
+ remaining -= minBg;
+ // Ensure bg user is in the range [0, min(maxBgUser, remaining)]
+ final int minBgUser = Math.max(0, Math.min(Math.min(maxBgUser, remaining),
+ properties.getInt(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
+ mDefaultMinReservedSlots.get(WORK_TYPE_BGUSER, 0))));
+ mMinReservedSlots.put(WORK_TYPE_BGUSER, minBgUser);
}
int getMaxTotal() {
@@ -849,10 +961,18 @@
.println();
pw.print(KEY_PREFIX_MAX_TOP + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_TOP))
.println();
+ pw.print(KEY_PREFIX_MIN_EJ + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_EJ))
+ .println();
+ pw.print(KEY_PREFIX_MAX_EJ + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_EJ))
+ .println();
pw.print(KEY_PREFIX_MIN_BG + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_BG))
.println();
pw.print(KEY_PREFIX_MAX_BG + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_BG))
.println();
+ pw.print(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
+ mMinReservedSlots.get(WORK_TYPE_BGUSER)).println();
+ pw.print(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
+ mMaxAllowedSlots.get(WORK_TYPE_BGUSER)).println();
}
}
@@ -873,6 +993,58 @@
}
/**
+ * This class keeps the track of when a user's grace period expires.
+ */
+ @VisibleForTesting
+ static class GracePeriodObserver extends UserSwitchObserver {
+ // Key is UserId and Value is the time when grace period expires
+ @VisibleForTesting
+ final SparseLongArray mGracePeriodExpiration = new SparseLongArray();
+ private int mCurrentUserId;
+ @VisibleForTesting
+ int mGracePeriod;
+ private final UserManagerInternal mUserManagerInternal;
+ final Object mLock = new Object();
+
+
+ GracePeriodObserver(Context context) {
+ mCurrentUserId = LocalServices.getService(ActivityManagerInternal.class)
+ .getCurrentUserId();
+ mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+ mGracePeriod = Math.max(0, context.getResources().getInteger(
+ R.integer.config_jobSchedulerUserGracePeriod));
+ }
+
+ @Override
+ public void onUserSwitchComplete(int newUserId) {
+ final long expiration = sElapsedRealtimeClock.millis() + mGracePeriod;
+ synchronized (mLock) {
+ if (mCurrentUserId != UserHandle.USER_NULL
+ && mUserManagerInternal.exists(mCurrentUserId)) {
+ mGracePeriodExpiration.append(mCurrentUserId, expiration);
+ }
+ mGracePeriodExpiration.delete(newUserId);
+ mCurrentUserId = newUserId;
+ }
+ }
+
+ void onUserRemoved(int userId) {
+ synchronized (mLock) {
+ mGracePeriodExpiration.delete(userId);
+ }
+ }
+
+ @VisibleForTesting
+ public boolean isWithinGracePeriodForUser(int userId) {
+ synchronized (mLock) {
+ return userId == mCurrentUserId
+ || sElapsedRealtimeClock.millis()
+ < mGracePeriodExpiration.get(userId, Long.MAX_VALUE);
+ }
+ }
+ }
+
+ /**
* This class decides, taking into account the current {@link WorkTypeConfig} and how many jobs
* are running/pending, how many more job can start.
*
@@ -892,20 +1064,21 @@
private final SparseIntArray mNumPendingJobs = new SparseIntArray(NUM_WORK_TYPES);
private final SparseIntArray mNumRunningJobs = new SparseIntArray(NUM_WORK_TYPES);
private final SparseIntArray mNumStartingJobs = new SparseIntArray(NUM_WORK_TYPES);
- private int mNumUnspecialized = 0;
private int mNumUnspecializedRemaining = 0;
void setConfig(@NonNull WorkTypeConfig workTypeConfig) {
mConfigMaxTotal = workTypeConfig.getMaxTotal();
mConfigNumReservedSlots.put(WORK_TYPE_TOP,
workTypeConfig.getMinReserved(WORK_TYPE_TOP));
+ mConfigNumReservedSlots.put(WORK_TYPE_EJ, workTypeConfig.getMinReserved(WORK_TYPE_EJ));
mConfigNumReservedSlots.put(WORK_TYPE_BG, workTypeConfig.getMinReserved(WORK_TYPE_BG));
+ mConfigNumReservedSlots.put(WORK_TYPE_BGUSER,
+ workTypeConfig.getMinReserved(WORK_TYPE_BGUSER));
mConfigAbsoluteMaxSlots.put(WORK_TYPE_TOP, workTypeConfig.getMax(WORK_TYPE_TOP));
+ mConfigAbsoluteMaxSlots.put(WORK_TYPE_EJ, workTypeConfig.getMax(WORK_TYPE_EJ));
mConfigAbsoluteMaxSlots.put(WORK_TYPE_BG, workTypeConfig.getMax(WORK_TYPE_BG));
+ mConfigAbsoluteMaxSlots.put(WORK_TYPE_BGUSER, workTypeConfig.getMax(WORK_TYPE_BGUSER));
- mNumUnspecialized = mConfigMaxTotal;
- mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_TOP);
- mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_BG);
mNumUnspecializedRemaining = mConfigMaxTotal;
for (int i = mNumRunningJobs.size() - 1; i >= 0; --i) {
mNumUnspecializedRemaining -= Math.max(mNumRunningJobs.valueAt(i),
@@ -934,9 +1107,15 @@
if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) {
mNumPendingJobs.put(WORK_TYPE_TOP, mNumPendingJobs.get(WORK_TYPE_TOP) + 1);
}
+ if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) {
+ mNumPendingJobs.put(WORK_TYPE_EJ, mNumPendingJobs.get(WORK_TYPE_EJ) + 1);
+ }
if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
mNumPendingJobs.put(WORK_TYPE_BG, mNumPendingJobs.get(WORK_TYPE_BG) + 1);
}
+ if ((workTypes & WORK_TYPE_BGUSER) == WORK_TYPE_BGUSER) {
+ mNumPendingJobs.put(WORK_TYPE_BGUSER, mNumPendingJobs.get(WORK_TYPE_BGUSER) + 1);
+ }
}
void stageJob(@WorkType int workType) {
@@ -1018,48 +1197,76 @@
void onCountDone() {
// Calculate how many slots to reserve for each work type. "Unspecialized" slots will
- // be reserved for higher importance types first (ie. top before bg).
- mNumUnspecialized = mConfigMaxTotal;
- final int numTop = mNumRunningJobs.get(WORK_TYPE_TOP)
- + mNumPendingJobs.get(WORK_TYPE_TOP);
- int resTop = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_TOP), numTop);
- mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop);
- mNumUnspecialized -= resTop;
- final int numBg = mNumRunningJobs.get(WORK_TYPE_BG) + mNumPendingJobs.get(WORK_TYPE_BG);
- int resBg = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BG), numBg);
- mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg);
- mNumUnspecialized -= resBg;
+ // be reserved for higher importance types first (ie. top before ej before bg).
+ // Steps:
+ // 1. Account for slots for already running jobs
+ // 2. Use remaining unaccounted slots to try and ensure minimum reserved slots
+ // 3. Allocate remaining up to max, based on importance
- mNumUnspecializedRemaining = mNumUnspecialized;
- // Account for already running jobs after we've assigned the minimum number of slots.
- int unspecializedAssigned;
- int extraRunning = (mNumRunningJobs.get(WORK_TYPE_TOP) - resTop);
- if (extraRunning > 0) {
- unspecializedAssigned = Math.max(0,
- Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP) - resTop,
- extraRunning));
- resTop += unspecializedAssigned;
- mNumUnspecializedRemaining -= extraRunning;
- }
- extraRunning = (mNumRunningJobs.get(WORK_TYPE_BG) - resBg);
- if (extraRunning > 0) {
- unspecializedAssigned = Math.max(0,
- Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG) - resBg, extraRunning));
- resBg += unspecializedAssigned;
- mNumUnspecializedRemaining -= extraRunning;
- }
+ mNumUnspecializedRemaining = mConfigMaxTotal;
- // Assign remaining unspecialized based on ranking.
- unspecializedAssigned = Math.max(0,
+ // Step 1
+ int runTop = mNumRunningJobs.get(WORK_TYPE_TOP);
+ int resTop = runTop;
+ mNumUnspecializedRemaining -= resTop;
+ int runEj = mNumRunningJobs.get(WORK_TYPE_EJ);
+ int resEj = runEj;
+ mNumUnspecializedRemaining -= resEj;
+ int runBg = mNumRunningJobs.get(WORK_TYPE_BG);
+ int resBg = runBg;
+ mNumUnspecializedRemaining -= resBg;
+ int runBgUser = mNumRunningJobs.get(WORK_TYPE_BGUSER);
+ int resBgUser = runBgUser;
+ mNumUnspecializedRemaining -= resBgUser;
+
+ // Step 2
+ final int numTop = runTop + mNumPendingJobs.get(WORK_TYPE_TOP);
+ int fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
+ Math.min(numTop, mConfigNumReservedSlots.get(WORK_TYPE_TOP) - resTop)));
+ resTop += fillUp;
+ mNumUnspecializedRemaining -= fillUp;
+ final int numEj = runEj + mNumPendingJobs.get(WORK_TYPE_EJ);
+ fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
+ Math.min(numEj, mConfigNumReservedSlots.get(WORK_TYPE_EJ) - resEj)));
+ resEj += fillUp;
+ mNumUnspecializedRemaining -= fillUp;
+ final int numBg = runBg + mNumPendingJobs.get(WORK_TYPE_BG);
+ fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
+ Math.min(numBg, mConfigNumReservedSlots.get(WORK_TYPE_BG) - resBg)));
+ resBg += fillUp;
+ mNumUnspecializedRemaining -= fillUp;
+ final int numBgUser = runBgUser + mNumPendingJobs.get(WORK_TYPE_BGUSER);
+ fillUp = Math.max(0, Math.min(mNumUnspecializedRemaining,
+ Math.min(numBgUser,
+ mConfigNumReservedSlots.get(WORK_TYPE_BGUSER) - resBgUser)));
+ resBgUser += fillUp;
+ mNumUnspecializedRemaining -= fillUp;
+
+ // Step 3
+ int unspecializedAssigned = Math.max(0,
Math.min(mNumUnspecializedRemaining,
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP), numTop) - resTop));
mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop + unspecializedAssigned);
mNumUnspecializedRemaining -= unspecializedAssigned;
+
+ unspecializedAssigned = Math.max(0,
+ Math.min(mNumUnspecializedRemaining,
+ Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_EJ), numEj) - resEj));
+ mNumActuallyReservedSlots.put(WORK_TYPE_EJ, resEj + unspecializedAssigned);
+ mNumUnspecializedRemaining -= unspecializedAssigned;
+
unspecializedAssigned = Math.max(0,
Math.min(mNumUnspecializedRemaining,
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG), numBg) - resBg));
mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg + unspecializedAssigned);
mNumUnspecializedRemaining -= unspecializedAssigned;
+
+ unspecializedAssigned = Math.max(0,
+ Math.min(mNumUnspecializedRemaining,
+ Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER), numBgUser)
+ - resBgUser));
+ mNumActuallyReservedSlots.put(WORK_TYPE_BGUSER, resBgUser + unspecializedAssigned);
+ mNumUnspecializedRemaining -= unspecializedAssigned;
}
int canJobStart(int workTypes) {
@@ -1072,6 +1279,15 @@
return WORK_TYPE_TOP;
}
}
+ if ((workTypes & WORK_TYPE_EJ) == WORK_TYPE_EJ) {
+ final int maxAllowed = Math.min(
+ mConfigAbsoluteMaxSlots.get(WORK_TYPE_EJ),
+ mNumActuallyReservedSlots.get(WORK_TYPE_EJ) + mNumUnspecializedRemaining);
+ if (mNumRunningJobs.get(WORK_TYPE_EJ) + mNumStartingJobs.get(WORK_TYPE_EJ)
+ < maxAllowed) {
+ return WORK_TYPE_EJ;
+ }
+ }
if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
final int maxAllowed = Math.min(
mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG),
@@ -1081,6 +1297,16 @@
return WORK_TYPE_BG;
}
}
+ if ((workTypes & WORK_TYPE_BGUSER) == WORK_TYPE_BGUSER) {
+ final int maxAllowed = Math.min(
+ mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER),
+ mNumActuallyReservedSlots.get(WORK_TYPE_BGUSER)
+ + mNumUnspecializedRemaining);
+ if (mNumRunningJobs.get(WORK_TYPE_BGUSER) + mNumStartingJobs.get(WORK_TYPE_BGUSER)
+ < maxAllowed) {
+ return WORK_TYPE_BGUSER;
+ }
+ }
return WORK_TYPE_NONE;
}
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 7ce867c..82ee5d8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -743,6 +743,7 @@
mControllers.get(c).onUserRemovedLocked(userId);
}
}
+ mConcurrencyManager.onUserRemoved(userId);
} else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
// Has this package scheduled any jobs, such that we will take action
// if it were to be force-stopped?
@@ -1989,8 +1990,11 @@
}
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/apex/media/framework/api/current.txt b/apex/media/framework/api/current.txt
index 8b9990f..67fa9bb 100644
--- a/apex/media/framework/api/current.txt
+++ b/apex/media/framework/api/current.txt
@@ -29,6 +29,7 @@
}
public class MediaCommunicationManager {
+ method @IntRange(from=1) public int getVersion();
}
public class MediaController2 implements java.lang.AutoCloseable {
diff --git a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
index e1a8596..aefeab6 100644
--- a/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
+++ b/apex/media/framework/java/android/media/ApplicationMediaCapabilities.java
@@ -36,30 +36,46 @@
import java.util.Set;
/**
- * ApplicationMediaCapabilities is an immutable class that encapsulates an application's
- * capabilities for handling newer video codec format and media features.
- *
- * The ApplicationMediaCapabilities class is used by the platform to represent an application's
- * media capabilities as defined in their manifest(TODO: Add link) in order to determine
- * whether modern media files need to be transcoded for that application (TODO: Add link).
- *
- * ApplicationMediaCapabilities objects can also be built by applications at runtime for use with
- * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)} to provide more
- * control over the transcoding that is built into the platform. ApplicationMediaCapabilities
- * provided by applications at runtime like this override the default manifest capabilities for that
- * media access.
- *
- * <h3> Video Codec Support</h3>
- * Newer video codes include HEVC, VP9 and AV1. Application only needs to indicate their support
- * for newer format with this class as they are assumed to support older format like h.264.
- *
- * <h4>Capability of handling HDR(high dynamic range) video</h4>
- * There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform,
- * application will only need to specify individual types they supported.
+ ApplicationMediaCapabilities is an immutable class that encapsulates an application's capabilities
+ for handling newer video codec format and media features.
+
+ <p>
+ Android 12 introduces seamless media transcoding feature. By default, Android assumes apps can
+ support playback of all media formats. Apps that would like to request that media be transcoded
+ into a more compatible format should declare their media capabilities in a media_capabilities
+ .xml resource file and add it as a property tag in the AndroidManifest.xml file. Here is a example:
+ <pre>
+ {@code
+ <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ <format android:name="HEVC" supported="true"/>
+ <format android:name="HDR10" supported="false"/>
+ <format android:name="HDR10Plus" supported="false"/>
+ </media-capabilities>
+ }
+ </pre>
+ The ApplicationMediaCapabilities class is generated from this xml and used by the platform to
+ represent an application's media capabilities in order to determine whether modern media files need
+ to be transcoded for that application.
+ </p>
+
+ <p>
+ ApplicationMediaCapabilities objects can also be built by applications at runtime for use with
+ {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)} to provide more
+ control over the transcoding that is built into the platform. ApplicationMediaCapabilities
+ provided by applications at runtime like this override the default manifest capabilities for that
+ media access.The object could be build either through {@link #createFromXml(XmlPullParser)} or
+ through the builder class {@link ApplicationMediaCapabilities.Builder}
+
+ <h3> Video Codec Support</h3>
+ <p>
+ Newer video codes include HEVC, VP9 and AV1. Application only needs to indicate their support
+ for newer format with this class as they are assumed to support older format like h.264.
+
+ <h3>Capability of handling HDR(high dynamic range) video</h3>
+ <p>
+ There are four types of HDR video(Dolby-Vision, HDR10, HDR10+, HLG) supported by the platform,
+ application will only need to specify individual types they supported.
*/
-// TODO(huang): Correct openTypedAssetFileDescriptor with the new API after it is added.
-// TODO(hkuang): Add a link to seamless transcoding detail when it is published
-// TODO(hkuang): Add code sample on how to build a capability object with MediaCodecList
public final class ApplicationMediaCapabilities implements Parcelable {
private static final String TAG = "ApplicationMediaCapabilities";
@@ -105,9 +121,9 @@
*/
public boolean isVideoMimeTypeSupported(
@NonNull String videoMime) throws FormatNotFoundException {
- if (mUnsupportedVideoMimeTypes.contains(videoMime)) {
+ if (mUnsupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {
return false;
- } else if (mSupportedVideoMimeTypes.contains(videoMime)) {
+ } else if (mSupportedVideoMimeTypes.contains(videoMime.toLowerCase())) {
return true;
} else {
throw new FormatNotFoundException(videoMime);
@@ -262,11 +278,27 @@
/**
* Creates {@link ApplicationMediaCapabilities} from an xml.
+ *
+ * The xml's syntax is the same as the media_capabilities.xml used by the AndroidManifest.xml.
+ * <p> Here is an example:
+ *
+ * <pre>
+ * {@code
+ * <media-capabilities xmlns:android="http://schemas.android.com/apk/res/android">
+ * <format android:name="HEVC" supported="true"/>
+ * <format android:name="HDR10" supported="false"/>
+ * <format android:name="HDR10Plus" supported="false"/>
+ * </media-capabilities>
+ * }
+ * </pre>
+ * <p>
+ *
* @param xmlParser The underlying {@link XmlPullParser} that will read the xml.
* @return An ApplicationMediaCapabilities object.
* @throws UnsupportedOperationException if the capabilities in xml config are invalid or
* incompatible.
*/
+ // TODO: Add developer.android.com link for the format of the xml.
@NonNull
public static ApplicationMediaCapabilities createFromXml(@NonNull XmlPullParser xmlParser) {
ApplicationMediaCapabilities.Builder builder = new ApplicationMediaCapabilities.Builder();
@@ -430,7 +462,7 @@
mIsSlowMotionSupported = isSupported;
break;
default:
- throw new UnsupportedOperationException("Invalid format name " + name);
+ Log.w(TAG, "Invalid format name " + name);
}
// Save the name and isSupported into the map for validate later.
mFormatSupportedMap.put(name, isSupported);
diff --git a/apex/media/framework/java/android/media/MediaCommunicationManager.java b/apex/media/framework/java/android/media/MediaCommunicationManager.java
index b8065ef..e686076 100644
--- a/apex/media/framework/java/android/media/MediaCommunicationManager.java
+++ b/apex/media/framework/java/android/media/MediaCommunicationManager.java
@@ -15,6 +15,7 @@
*/
package android.media;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemService;
import android.content.Context;
@@ -30,6 +31,16 @@
public class MediaCommunicationManager {
private static final String TAG = "MediaCommunicationManager";
+ /**
+ * The manager version used from beginning.
+ */
+ private static final int VERSION_1 = 1;
+
+ /**
+ * Current manager version.
+ */
+ private static final int CURRENT_VERSION = VERSION_1;
+
private final Context mContext;
private final IMediaCommunicationService mService;
@@ -43,7 +54,14 @@
mContext = context;
mService = IMediaCommunicationService.Stub.asInterface(
MediaFrameworkInitializer.getMediaServiceManager()
- .getMediaCommunicationServiceRegisterer()
- .get());
+ .getMediaCommunicationServiceRegisterer()
+ .get());
+ }
+
+ /**
+ * Gets the version of this {@link MediaCommunicationManager}.
+ */
+ public @IntRange(from = 1) int getVersion() {
+ return CURRENT_VERSION;
}
}
diff --git a/api/Android.bp b/api/Android.bp
index 69dce97..d5c6bf6 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -28,6 +28,7 @@
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 {
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 {
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 {
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 {
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 {
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 {
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/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index a7396fa..be82b22 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -43,7 +43,7 @@
#include <android-base/properties.h>
-#include <ui/DisplayConfig.h>
+#include <ui/DisplayMode.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -65,6 +65,8 @@
namespace android {
+using ui::DisplayMode;
+
static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
static const char PRODUCT_BOOTANIMATION_DARK_FILE[] = "/product/media/bootanimation-dark.zip";
static const char PRODUCT_BOOTANIMATION_FILE[] = "/product/media/bootanimation.zip";
@@ -345,14 +347,14 @@
continue;
}
- DisplayConfig displayConfig;
- const status_t error = SurfaceComposerClient::getActiveDisplayConfig(
- mBootAnimation->mDisplayToken, &displayConfig);
+ DisplayMode displayMode;
+ const status_t error = SurfaceComposerClient::getActiveDisplayMode(
+ mBootAnimation->mDisplayToken, &displayMode);
if (error != NO_ERROR) {
- SLOGE("Can't get active display configuration.");
+ SLOGE("Can't get active display mode.");
}
- mBootAnimation->resizeSurface(displayConfig.resolution.getWidth(),
- displayConfig.resolution.getHeight());
+ mBootAnimation->resizeSurface(displayMode.resolution.getWidth(),
+ displayMode.resolution.getHeight());
}
}
} while (numEvents > 0);
@@ -401,15 +403,15 @@
if (mDisplayToken == nullptr)
return NAME_NOT_FOUND;
- DisplayConfig displayConfig;
+ DisplayMode displayMode;
const status_t error =
- SurfaceComposerClient::getActiveDisplayConfig(mDisplayToken, &displayConfig);
+ SurfaceComposerClient::getActiveDisplayMode(mDisplayToken, &displayMode);
if (error != NO_ERROR)
return error;
mMaxWidth = android::base::GetIntProperty("ro.surface_flinger.max_graphics_width", 0);
mMaxHeight = android::base::GetIntProperty("ro.surface_flinger.max_graphics_height", 0);
- ui::Size resolution = displayConfig.resolution;
+ ui::Size resolution = displayMode.resolution;
resolution = limitSurfaceSize(resolution.width, resolution.height);
// create the native surface
sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index e21a6b2..50f4001 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -132,9 +132,6 @@
"tests/XmlParserTests.cpp",
"tests/ZipFileTests.cpp",
],
- required: [
- "idmap2",
- ],
static_libs: ["libgmock"],
target: {
android: {
@@ -163,9 +160,19 @@
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 5147f4e..0000000
--- 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/config/preloaded-classes-denylist b/config/preloaded-classes-denylist
index 43a8a87..da4b255 100644
--- a/config/preloaded-classes-denylist
+++ b/config/preloaded-classes-denylist
@@ -4,7 +4,6 @@
android.os.NullVibrator
android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask
android.widget.Magnifier
-com.android.server.BootReceiver$2
gov.nist.core.net.DefaultNetworkLayer
android.net.rtp.AudioGroup
android.net.rtp.AudioStream
diff --git a/core/api/current.txt b/core/api/current.txt
index 10801b0..f82e000 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -435,6 +435,7 @@
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
@@ -1100,6 +1101,7 @@
field public static final int presentationTheme = 16843712; // 0x10103c0
field public static final int preserveLegacyExternalStorage = 16844308; // 0x1010614
field public static final int previewImage = 16843482; // 0x10102da
+ field public static final int previewLayout = 16844327; // 0x1010627
field public static final int primaryContentAlpha = 16844114; // 0x1010552
field public static final int priority = 16842780; // 0x101001c
field public static final int privateImeOptions = 16843299; // 0x1010223
@@ -2935,12 +2937,12 @@
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
@@ -5553,15 +5555,19 @@
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";
@@ -5593,6 +5599,8 @@
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
@@ -5841,6 +5849,14 @@
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);
@@ -6930,6 +6946,7 @@
method public void addPersistentPreferredActivity(@NonNull android.content.ComponentName, android.content.IntentFilter, @NonNull android.content.ComponentName);
method public void addUserRestriction(@NonNull android.content.ComponentName, String);
method public boolean bindDeviceAdminServiceAsUser(@NonNull android.content.ComponentName, android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle);
+ method public boolean canAdminGrantSensorsPermissions();
method public void clearApplicationUserData(@NonNull android.content.ComponentName, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.app.admin.DevicePolicyManager.OnClearApplicationUserDataListener);
method public void clearCrossProfileIntentFilters(@NonNull android.content.ComponentName);
method @Deprecated public void clearDeviceOwnerApp(String);
@@ -7227,6 +7244,7 @@
field public static final String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI";
field public static final String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR";
field public static final String EXTRA_PROVISIONING_MODE = "android.app.extra.PROVISIONING_MODE";
+ field public static final String EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT = "android.app.extra.PROVISIONING_PERMISSION_GRANT_OPT_OUT";
field public static final String EXTRA_PROVISIONING_SERIAL_NUMBER = "android.app.extra.PROVISIONING_SERIAL_NUMBER";
field public static final String EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS = "android.app.extra.PROVISIONING_SKIP_EDUCATION_SCREENS";
field public static final String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
@@ -8384,6 +8402,7 @@
field public int minResizeWidth;
field public int minWidth;
field public int previewImage;
+ field @IdRes public int previewLayout;
field public android.content.ComponentName provider;
field public int resizeMode;
field public int updatePeriodMillis;
@@ -12298,7 +12317,7 @@
method @Deprecated public abstract void removePackageFromPreferred(@NonNull String);
method public abstract void removePermission(@NonNull String);
method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
- method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
+ method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.pm.PackageManager.OnChecksumsReadyListener) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int);
method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
@@ -12318,7 +12337,6 @@
field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
field public static final int DONT_KILL_APP = 1; // 0x1
- field public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
field public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = "android.software.activities_on_secondary_displays";
@@ -12504,6 +12522,10 @@
ctor public PackageManager.NameNotFoundException(String);
}
+ @java.lang.FunctionalInterface public static interface PackageManager.OnChecksumsReadyListener {
+ method public void onChecksumsReady(@NonNull java.util.List<android.content.pm.ApkChecksum>);
+ }
+
public static final class PackageManager.Property implements android.os.Parcelable {
method public int describeContents();
method public boolean getBoolean();
@@ -15790,6 +15812,7 @@
method @NonNull public static android.graphics.RenderEffect createColorFilterEffect(@NonNull android.graphics.ColorFilter);
method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float);
method @NonNull public static android.graphics.RenderEffect createOffsetEffect(float, float, @NonNull android.graphics.RenderEffect);
+ method @NonNull public static android.graphics.RenderEffect createShaderEffect(@NonNull android.graphics.Shader);
}
public final class RenderNode {
@@ -20472,6 +20495,7 @@
field public static final int QUALITY_480P = 4; // 0x4
field public static final int QUALITY_4KDCI = 10; // 0xa
field public static final int QUALITY_720P = 5; // 0x5
+ field public static final int QUALITY_8KUHD = 13; // 0xd
field public static final int QUALITY_CIF = 3; // 0x3
field public static final int QUALITY_HIGH = 1; // 0x1
field public static final int QUALITY_HIGH_SPEED_1080P = 2004; // 0x7d4
@@ -20493,6 +20517,7 @@
field public static final int QUALITY_TIME_LAPSE_480P = 1004; // 0x3ec
field public static final int QUALITY_TIME_LAPSE_4KDCI = 1010; // 0x3f2
field public static final int QUALITY_TIME_LAPSE_720P = 1005; // 0x3ed
+ field public static final int QUALITY_TIME_LAPSE_8KUHD = 1013; // 0x3f5
field public static final int QUALITY_TIME_LAPSE_CIF = 1003; // 0x3eb
field public static final int QUALITY_TIME_LAPSE_HIGH = 1001; // 0x3e9
field public static final int QUALITY_TIME_LAPSE_LOW = 1000; // 0x3e8
@@ -30584,6 +30609,7 @@
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";
@@ -40328,6 +40354,7 @@
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";
@@ -42053,7 +42080,7 @@
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();
@@ -42719,7 +42746,11 @@
}
public class ImsRcsManager {
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter();
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
field public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN = "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
}
@@ -42908,6 +42939,16 @@
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;
}
@@ -42917,7 +42958,6 @@
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
@@ -42926,9 +42966,9 @@
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);
}
@@ -50673,6 +50713,7 @@
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";
}
@@ -51070,7 +51111,7 @@
method public boolean sendKeyEvent(android.view.KeyEvent);
method public boolean setComposingRegion(int, int);
method public boolean setComposingText(CharSequence, int);
- method public default boolean setImeTemporarilyConsumesInput(boolean);
+ method public default boolean setImeConsumesInput(boolean);
method public boolean setSelection(int, int);
field public static final int CURSOR_UPDATE_IMMEDIATE = 1; // 0x1
field public static final int CURSOR_UPDATE_MONITOR = 2; // 0x2
@@ -53346,7 +53387,7 @@
method public void onSelectedDayChange(@NonNull android.widget.CalendarView, int, int, int);
}
- public class CheckBox extends android.widget.CompoundButton {
+ @android.widget.RemoteViews.RemoteView public class CheckBox extends android.widget.CompoundButton {
ctor public CheckBox(android.content.Context);
ctor public CheckBox(android.content.Context, android.util.AttributeSet);
ctor public CheckBox(android.content.Context, android.util.AttributeSet, int);
@@ -53412,6 +53453,7 @@
method public boolean isChecked();
method public void setButtonDrawable(@DrawableRes int);
method public void setButtonDrawable(@Nullable android.graphics.drawable.Drawable);
+ method public void setButtonIcon(@Nullable android.graphics.drawable.Icon);
method public void setButtonTintBlendMode(@Nullable android.graphics.BlendMode);
method public void setButtonTintList(@Nullable android.content.res.ColorStateList);
method public void setButtonTintMode(@Nullable android.graphics.PorterDuff.Mode);
@@ -54452,14 +54494,14 @@
field protected String[] mExcludeMimes;
}
- public class RadioButton extends android.widget.CompoundButton {
+ @android.widget.RemoteViews.RemoteView public class RadioButton extends android.widget.CompoundButton {
ctor public RadioButton(android.content.Context);
ctor public RadioButton(android.content.Context, android.util.AttributeSet);
ctor public RadioButton(android.content.Context, android.util.AttributeSet, int);
ctor public RadioButton(android.content.Context, android.util.AttributeSet, int, int);
}
- public class RadioGroup extends android.widget.LinearLayout {
+ @android.widget.RemoteViews.RemoteView public class RadioGroup extends android.widget.LinearLayout {
ctor public RadioGroup(android.content.Context);
ctor public RadioGroup(android.content.Context, android.util.AttributeSet);
method public void check(@IdRes int);
@@ -54581,6 +54623,7 @@
method public void setChronometerCountDown(@IdRes int, boolean);
method public void setColor(@IdRes int, @NonNull String, @ColorRes int);
method public void setColorStateList(@IdRes int, @NonNull String, @ColorRes int);
+ method public void setCompoundButtonChecked(@IdRes int, boolean);
method public void setContentDescription(@IdRes int, CharSequence);
method public void setDisplayedChild(@IdRes int, int);
method public void setDouble(@IdRes int, String, double);
@@ -54605,6 +54648,7 @@
method public void setOnClickResponse(@IdRes int, @NonNull android.widget.RemoteViews.RemoteResponse);
method public void setPendingIntentTemplate(@IdRes int, android.app.PendingIntent);
method public void setProgressBar(@IdRes int, int, int, boolean);
+ method public void setRadioGroupChecked(@IdRes int, @IdRes int);
method public void setRelativeScrollPosition(@IdRes int, int);
method @Deprecated public void setRemoteAdapter(int, @IdRes int, android.content.Intent);
method public void setRemoteAdapter(@IdRes int, android.content.Intent);
@@ -54617,6 +54661,8 @@
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);
@@ -54641,6 +54687,12 @@
@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);
@@ -54971,7 +55023,7 @@
ctor public StackView(android.content.Context, android.util.AttributeSet, int, int);
}
- public class Switch extends android.widget.CompoundButton {
+ @android.widget.RemoteViews.RemoteView public class Switch extends android.widget.CompoundButton {
ctor public Switch(android.content.Context);
ctor public Switch(android.content.Context, android.util.AttributeSet);
ctor public Switch(android.content.Context, android.util.AttributeSet, int);
@@ -55002,12 +55054,14 @@
method public void setTextOff(CharSequence);
method public void setTextOn(CharSequence);
method public void setThumbDrawable(android.graphics.drawable.Drawable);
+ method public void setThumbIcon(@Nullable android.graphics.drawable.Icon);
method public void setThumbResource(@DrawableRes int);
method public void setThumbTextPadding(int);
method public void setThumbTintBlendMode(@Nullable android.graphics.BlendMode);
method public void setThumbTintList(@Nullable android.content.res.ColorStateList);
method public void setThumbTintMode(@Nullable android.graphics.PorterDuff.Mode);
method public void setTrackDrawable(android.graphics.drawable.Drawable);
+ method public void setTrackIcon(@Nullable android.graphics.drawable.Icon);
method public void setTrackResource(@DrawableRes int);
method public void setTrackTintBlendMode(@Nullable android.graphics.BlendMode);
method public void setTrackTintList(@Nullable android.content.res.ColorStateList);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index bf70803..4e25625 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -162,6 +162,7 @@
}
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 1977bab..74ccbb1 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -290,6 +290,7 @@
public static final class R.attr {
field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
+ field public static final int hotwordDetectionService = 16844326; // 0x1010626
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int minExtensionVersion = 16844305; // 0x1010611
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
@@ -869,6 +870,7 @@
}
public class DevicePolicyManager {
+ method public boolean canAdminGrantSensorsPermissionsForUser(int);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.content.ComponentName getDeviceOwnerComponentOnAnyUser();
@@ -1988,6 +1990,7 @@
field public static final int UUID_BYTES_128_BIT = 16; // 0x10
field public static final int UUID_BYTES_16_BIT = 2; // 0x2
field public static final int UUID_BYTES_32_BIT = 4; // 0x4
+ field @NonNull public static final android.os.ParcelUuid VOLUME_CONTROL;
}
public final class BufferConstraint implements android.os.Parcelable {
@@ -4664,6 +4667,7 @@
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String, @Nullable String);
method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.UPDATE_APP_OPS_STATS}) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler);
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
+ method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerProviderRequestListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.Listener);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
@@ -4672,6 +4676,7 @@
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle);
method @Deprecated @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setProviderEnabledForUser(@NonNull String, boolean, @NonNull android.os.UserHandle);
method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean unregisterGnssBatchedLocationCallback(@NonNull android.location.BatchedLocationCallback);
+ method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void unregisterProviderRequestListener(@NonNull android.location.provider.ProviderRequest.Listener);
}
public final class LocationRequest implements android.os.Parcelable {
@@ -4821,6 +4826,10 @@
method @NonNull public android.location.provider.ProviderRequest.Builder setWorkSource(@NonNull android.os.WorkSource);
}
+ public static interface ProviderRequest.Listener {
+ method public void onProviderRequestChanged(@NonNull String, @NonNull android.location.provider.ProviderRequest);
+ }
+
}
package android.media {
@@ -4970,6 +4979,7 @@
method public android.media.PlayerProxy getPlayerProxy();
method public int getPlayerState();
method public int getPlayerType();
+ method @IntRange(from=0) public int getSessionId();
method public boolean isActive();
field public static final int PLAYER_STATE_IDLE = 1; // 0x1
field public static final int PLAYER_STATE_PAUSED = 3; // 0x3
@@ -8642,6 +8652,7 @@
method @NonNull public static String formatUid(int);
method public static int getAppId(int);
method public int getIdentifier();
+ method public static int getUid(@NonNull android.os.UserHandle, int);
method @Deprecated public boolean isOwner();
method public boolean isSystem();
method public static int myUserId();
@@ -9509,6 +9520,11 @@
method @Deprecated @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUid(int);
}
+ public abstract class KeyProperties {
+ field public static final int NAMESPACE_APPLICATION = -1; // 0xffffffff
+ field public static final int NAMESPACE_WIFI = 102; // 0x66
+ }
+
}
package android.security.keystore.recovery {
@@ -10331,7 +10347,7 @@
ctor public ExternalStorageService();
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onEndSession(@NonNull String) throws java.io.IOException;
- method public void onFreeCacheRequested(@NonNull java.util.UUID, long);
+ method public void onFreeCache(@NonNull java.util.UUID, long) throws java.io.IOException;
method public abstract void onStartSession(@NonNull String, int, @NonNull android.os.ParcelFileDescriptor, @NonNull java.io.File, @NonNull java.io.File) throws java.io.IOException;
method public abstract void onVolumeStateChanged(@NonNull android.os.storage.StorageVolume) throws java.io.IOException;
field public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 2; // 0x2
@@ -11704,6 +11720,7 @@
method public boolean canManageSubscription(@NonNull android.telephony.SubscriptionInfo, @NonNull String);
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getActiveSubscriptionIdList();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String);
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public byte[] getAllSimSpecificSettingsForBackup();
method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getCompleteActiveSubscriptionIdList();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
@@ -11711,6 +11728,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int);
method public void requestEmbeddedSubscriptionInfoListRefresh();
method public void requestEmbeddedSubscriptionInfoListRefresh(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[]);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultDataSubId(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultSmsSubId(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int);
@@ -11851,6 +11869,7 @@
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);
@@ -11975,6 +11994,9 @@
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
@@ -12941,10 +12963,31 @@
method @Deprecated public void onRegistering(int);
}
+ public class ImsRcsManager {
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addOnAvailabilityChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsRcsManager.OnAvailabilityChangedListener) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(int, int) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(int, int) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnAvailabilityChangedListener(@NonNull android.telephony.ims.ImsRcsManager.OnAvailabilityChangedListener);
+ }
+
+ public static interface ImsRcsManager.OnAvailabilityChangedListener {
+ method public void onAvailabilityChanged(int);
+ }
+
public final class ImsReasonInfo implements android.os.Parcelable {
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);
@@ -13581,11 +13624,24 @@
ctor public RcsFeature(@NonNull java.util.concurrent.Executor);
method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.CapabilityExchangeEventListener);
+ method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
method public void onFeatureReady();
method public void onFeatureRemoved();
+ method public boolean queryCapabilityConfiguration(int, int);
+ method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus();
method public void removeCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase);
}
+ public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
+ ctor public RcsFeature.RcsImsCapabilities(int);
+ method public void addCapabilities(int);
+ method public boolean isCapable(int);
+ method public void removeCapabilities(int);
+ field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
+ field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
+ field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
+ }
+
}
package android.telephony.ims.stub {
@@ -13715,7 +13771,9 @@
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/api/test-current.txt b/core/api/test-current.txt
index f011983..e39b2b8 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -12,6 +12,7 @@
field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS";
+ field public static final String CONTROL_DEVICE_STATE = "android.permission.CONTROL_DEVICE_STATE";
field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
field @Deprecated public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
field public static final String MANAGE_ACTIVITY_TASKS = "android.permission.MANAGE_ACTIVITY_TASKS";
@@ -392,11 +393,10 @@
method public boolean isFactoryResetProtectionPolicySupported();
method @NonNull public static String operationToString(int);
method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
+ method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void resetDefaultCrossProfileIntentFilters(int);
method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int);
method @NonNull public static String unsafeOperationReasonToString(int);
field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
- field public static final String ACTION_MANAGED_PROFILE_CREATED = "android.app.action.MANAGED_PROFILE_CREATED";
- field public static final String ACTION_PROVISIONED_MANAGED_DEVICE = "android.app.action.PROVISIONED_MANAGED_DEVICE";
field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
field public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb
field public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd
@@ -464,6 +464,7 @@
}
public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {
+ method public boolean canDeviceOwnerGrantSensorsPermissions();
method public int describeContents();
method @NonNull public android.content.ComponentName getDeviceAdminComponentName();
method public long getLocalTime();
@@ -478,6 +479,7 @@
public static final class FullyManagedDeviceProvisioningParams.Builder {
ctor public FullyManagedDeviceProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams build();
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setDeviceOwnerCanGrantSensorsPermissions(boolean);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocalTime(long);
method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocale(@Nullable java.util.Locale);
@@ -901,6 +903,40 @@
}
+package android.hardware.devicestate {
+
+ public final class DeviceStateManager {
+ method public void addDeviceStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener);
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelRequest(@NonNull android.hardware.devicestate.DeviceStateRequest);
+ method @NonNull public int[] getSupportedStates();
+ method public void removeDeviceStateListener(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateListener);
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
+ }
+
+ public static interface DeviceStateManager.DeviceStateListener {
+ method public void onDeviceStateChanged(int);
+ }
+
+ public final class DeviceStateRequest {
+ method public int getFlags();
+ method public int getState();
+ method @NonNull public static android.hardware.devicestate.DeviceStateRequest.Builder newBuilder(int);
+ field public static final int FLAG_CANCEL_WHEN_BASE_CHANGES = 1; // 0x1
+ }
+
+ public static final class DeviceStateRequest.Builder {
+ method @NonNull public android.hardware.devicestate.DeviceStateRequest build();
+ method @NonNull public android.hardware.devicestate.DeviceStateRequest.Builder setFlags(int);
+ }
+
+ public static interface DeviceStateRequest.Callback {
+ method public default void onRequestActivated(@NonNull android.hardware.devicestate.DeviceStateRequest);
+ method public default void onRequestCanceled(@NonNull android.hardware.devicestate.DeviceStateRequest);
+ method public default void onRequestSuspended(@NonNull android.hardware.devicestate.DeviceStateRequest);
+ }
+
+}
+
package android.hardware.display {
public class AmbientDisplayConfiguration {
diff --git a/core/java/Android.bp b/core/java/Android.bp
index fb27f74..af5df76 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -7,3 +7,8 @@
name: "IDropBoxManagerService.aidl",
srcs: ["com/android/internal/os/IDropBoxManagerService.aidl"],
}
+
+filegroup {
+ name: "ITracingServiceProxy.aidl",
+ srcs: ["android/tracing/ITracingServiceProxy.aidl"],
+}
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index 4b2d741..768ec38 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -20,12 +20,12 @@
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 @@
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 @@
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 1fa7fa2..dab4a5d 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -445,8 +445,8 @@
/** 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/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 9b6f4b4..c31c22c 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -538,4 +538,10 @@
* @return mBootTimeTempAllowlistDuration of ActivityManagerConstants.
*/
public abstract long getBootTimeTempAllowListDuration();
+
+ /** Register an {@link AnrController} to control the ANR dialog behavior */
+ public abstract void registerAnrController(AnrController controller);
+
+ /** Unregister an {@link AnrController} */
+ public abstract void unregisterAnrController(AnrController controller);
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e5a04c9..bb6a774 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2289,11 +2289,12 @@
* 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 @@
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
@@ -5685,6 +5689,13 @@
final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull(
amOverrideConfig, contextThemeWrapperOverrideConfig);
mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId);
+ final Resources res = activity.getResources();
+ if (res.hasOverrideDisplayAdjustments()) {
+ // If fixed rotation is applied while the activity is visible (e.g. PiP), the rotated
+ // configuration of activity may be sent later than the adjustments. In this case, the
+ // adjustments need to be updated for the consistency of display info.
+ res.getDisplayAdjustments().getConfiguration().updateFrom(finalOverrideConfig);
+ }
activity.mConfigChangeFlags = 0;
activity.mCurrentConfig = new Configuration(newConfig);
diff --git a/core/java/android/app/AnrController.java b/core/java/android/app/AnrController.java
new file mode 100644
index 0000000..cfc9d27
--- /dev/null
+++ b/core/java/android/app/AnrController.java
@@ -0,0 +1,29 @@
+/*
+ * 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.app;
+
+/**
+ * Interface to control the ANR dialog within the activity manager
+ * {@hide}
+ */
+public interface AnrController {
+ /**
+ * Returns the delay in milliseconds for an ANR dialog that is about to be shown for
+ * {@code packageName}.
+ */
+ long getAnrDelayMillis(String packageName, int uid);
+}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index b51d4ac..062cab4 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -39,11 +39,13 @@
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApkChecksum;
import android.content.pm.ApplicationInfo;
import android.content.pm.ChangedPackages;
import android.content.pm.Checksum;
import android.content.pm.ComponentInfo;
import android.content.pm.FeatureInfo;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageManager;
@@ -880,10 +882,10 @@
@Override
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
- @NonNull IntentSender statusReceiver)
+ @NonNull OnChecksumsReadyListener onChecksumsReadyListener)
throws CertificateEncodingException, NameNotFoundException {
Objects.requireNonNull(packageName);
- Objects.requireNonNull(statusReceiver);
+ Objects.requireNonNull(onChecksumsReadyListener);
Objects.requireNonNull(trustedInstallers);
try {
if (trustedInstallers == TRUST_ALL) {
@@ -895,8 +897,17 @@
"trustedInstallers has to be one of TRUST_ALL/TRUST_NONE or a non-empty "
+ "list of certificates.");
}
+ IOnChecksumsReadyListener onChecksumsReadyListenerDelegate =
+ new IOnChecksumsReadyListener.Stub() {
+ @Override
+ public void onChecksumsReady(List<ApkChecksum> checksums)
+ throws RemoteException {
+ onChecksumsReadyListener.onChecksumsReady(checksums);
+ }
+ };
mPM.requestChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required,
- encodeCertificates(trustedInstallers), statusReceiver, getUserId());
+ encodeCertificates(trustedInstallers), onChecksumsReadyListenerDelegate,
+ getUserId());
} catch (ParcelableException e) {
e.maybeRethrow(PackageManager.NameNotFoundException.class);
throw new RuntimeException(e);
@@ -1717,7 +1728,7 @@
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 4ddeb8f..9a20e0f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2345,6 +2345,7 @@
pi.getResDir(),
splitResDirs,
pi.getOverlayDirs(),
+ pi.getOverlayPaths(),
pi.getApplicationInfo().sharedLibraryFiles,
overrideDisplayId,
overrideConfig,
@@ -2442,6 +2443,7 @@
mPackageInfo.getResDir(),
paths,
mPackageInfo.getOverlayDirs(),
+ mPackageInfo.getOverlayPaths(),
mPackageInfo.getApplicationInfo().sharedLibraryFiles,
mForceDisplayOverrideInResources ? getDisplayId() : null,
null,
@@ -2558,7 +2560,8 @@
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 @@
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 @@
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 8b6570f..ac1fa1e 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 @@
/**
* 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 @@
/**
* 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 c01b5a3..be426aa 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -113,7 +113,8 @@
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 @@
mSplitAppDirs = null;
mSplitResDirs = null;
mSplitClassLoaderNames = null;
- mOverlayDirs = null;
+ mLegacyOverlayDirs = null;
+ mOverlayPaths = null;
mDataDir = null;
mDataDirFile = null;
mDeviceProtectedDataDirFile = null;
@@ -364,8 +366,8 @@
}
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 @@
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 @@
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 @@
}
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 49f508d..c242fd4 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -22,7 +22,10 @@
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.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 @@
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 @@
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 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 @@
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 @@
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 @@
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 @@
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 @@
: 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 @@
}
}
+
+
+ /**
+ * 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 @@
boolean mHideActions;
boolean mHideProgress;
boolean mPromotePicture;
+ boolean mAllowActionIcons;
CharSequence title;
CharSequence text;
CharSequence headerTextSecondary;
@@ -11392,6 +11891,7 @@
mHideActions = false;
mHideProgress = false;
mPromotePicture = false;
+ mAllowActionIcons = false;
title = null;
text = null;
summaryText = null;
@@ -11431,6 +11931,11 @@
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/OWNERS b/core/java/android/app/OWNERS
index e6aa7a7..1ff64db 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -59,7 +59,7 @@
per-file InstantAppResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS
# ResourcesManager
-per-file ResourcesManager.java = rtmitchell@google.com, toddke@google.com
+per-file ResourcesManager.java = file:RESOURCES_OWNERS
# VoiceInteraction
per-file *VoiceInteract* = file:/core/java/android/service/voice/OWNERS
diff --git a/core/java/android/app/RESOURCES_OWNERS b/core/java/android/app/RESOURCES_OWNERS
new file mode 100644
index 0000000..21c39a8
--- /dev/null
+++ b/core/java/android/app/RESOURCES_OWNERS
@@ -0,0 +1,2 @@
+rtmitchell@google.com
+toddke@google.com
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 772833cc..ac8d3a2 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -39,6 +39,7 @@
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.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 @@
* 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 @@
}
}
- 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 @@
/**
* 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 @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 @@
final ResourcesKey key = new ResourcesKey(
resDir,
splitResDirs,
- overlayDirs,
+ combinedOverlayPaths(legacyOverlayDirs, overlayPaths),
libDirs,
displayId,
overrideConfig,
@@ -1043,7 +1048,8 @@
* @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 @@
@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 @@
final ResourcesKey key = new ResourcesKey(
resDir,
splitResDirs,
- overlayDirs,
+ combinedOverlayPaths(legacyOverlayDirs, overlayPaths),
libDirs,
overrideDisplayId != null ? overrideDisplayId : INVALID_DISPLAY,
overrideConfig,
@@ -1250,7 +1257,7 @@
// 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 @@
updatedResourceKeys.put(impl, new ResourcesKey(
key.mResDir,
key.mSplitResDirs,
- key.mOverlayDirs,
+ key.mOverlayPaths,
newLibAssets,
key.mDisplayId,
key.mOverrideConfiguration,
@@ -1423,7 +1430,8 @@
// 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 @@
}
}
+ /**
+ * 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 @@
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/WindowTokenClient.java b/core/java/android/app/WindowTokenClient.java
index 9092ef36..29792ac 100644
--- a/core/java/android/app/WindowTokenClient.java
+++ b/core/java/android/app/WindowTokenClient.java
@@ -44,8 +44,8 @@
* Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
* can only attach one {@link Context}.
* <p>This method must be called before invoking
- * {@link android.view.IWindowManager#addWindowTokenWithOptions(IBinder, int, int, Bundle,
- * String)}.<p/>
+ * {@link android.view.IWindowManager#registerWindowContextListener(IBinder, int, int,
+ * Bundle, boolean)}.<p/>
*
* @param context context to be attached
* @throws IllegalStateException if attached context has already existed.
diff --git a/core/java/android/app/admin/DevicePolicyCache.java b/core/java/android/app/admin/DevicePolicyCache.java
index 8b0c706..9c07f85 100644
--- a/core/java/android/app/admin/DevicePolicyCache.java
+++ b/core/java/android/app/admin/DevicePolicyCache.java
@@ -57,6 +57,13 @@
public abstract int getPermissionPolicy(@UserIdInt int userHandle);
/**
+ * Caches {@link DevicePolicyManager#canAdminGrantSensorsPermissionsForUser(int)} for the
+ * given user.
+ */
+ public abstract boolean canAdminGrantSensorsPermissionsForUser(@UserIdInt int userHandle);
+
+
+ /**
* Empty implementation.
*/
private static class EmptyDevicePolicyCache extends DevicePolicyCache {
@@ -77,5 +84,10 @@
public int getPermissionPolicy(int userHandle) {
return DevicePolicyManager.PERMISSION_POLICY_PROMPT;
}
+
+ @Override
+ public boolean canAdminGrantSensorsPermissionsForUser(int userHandle) {
+ return false;
+ }
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 642bce4..ff41d1c8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -980,6 +980,19 @@
= "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM";
/**
+ * A boolean extra indicating the admin of a fully-managed device opts out of controlling
+ * permission grants for sensor-related permissions,
+ * see {@link #setPermissionGrantState(ComponentName, String, String, int)}.
+ *
+ * The default for this extra is {@code false} - by default, the admin of a fully-managed
+ * device has the ability to grant sensors-related permissions.
+ *
+ * <p>Use with {@link #ACTION_PROVISION_MANAGED_DEVICE} only.
+ */
+ public static final String EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT =
+ "android.app.extra.PROVISIONING_PERMISSION_GRANT_OPT_OUT";
+
+ /**
* A String extra holding the URL-safe base64 encoded SHA-256 checksum of any signature of the
* android package archive at the download location specified in {@link
* #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}.
@@ -1730,8 +1743,12 @@
* Broadcast action to notify ManagedProvisioning that
* {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction has changed.
* @hide
+ * @deprecated No longer needed as ManagedProvisioning no longer handles
+ * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction changing.
*/
+ // TODO(b/177221010): Remove when Managed Provisioning no longer depend on it.
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @Deprecated
public static final String ACTION_DATA_SHARING_RESTRICTION_CHANGED =
"android.app.action.DATA_SHARING_RESTRICTION_CHANGED";
@@ -5469,26 +5486,6 @@
"android.app.action.ACTION_SHOW_NEW_USER_DISCLAIMER";
/**
- * Broadcast action: notify managed provisioning that the device has been provisioned.
- *
- * @hide
- */
- @TestApi
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_PROVISIONED_MANAGED_DEVICE =
- "android.app.action.PROVISIONED_MANAGED_DEVICE";
-
- /**
- * Broadcast action: notify managed provisioning that a new managed profile is created.
- *
- * @hide
- */
- @TestApi
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_MANAGED_PROFILE_CREATED =
- "android.app.action.MANAGED_PROFILE_CREATED";
-
- /**
* Widgets are enabled in keyguard
*/
public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0;
@@ -10536,6 +10533,13 @@
* As this policy only acts on runtime permission requests, it only applies to applications
* built with a {@code targetSdkVersion} of {@link android.os.Build.VERSION_CODES#M} or later.
*
+ * <p>
+ * NOTE: On devices running {@link android.os.Build.VERSION_CODES#S} and above, an auto-grant
+ * policy will not apply to certain sensors-related permissions on some configurations.
+ * See {@link #setPermissionGrantState(ComponentName, String, String, int)} for the list of
+ * permissions affected, and the behavior change for managed profiles and fully-managed
+ * devices.
+ *
* @param admin Which profile or device owner this request is associated with.
* @param policy One of the policy constants {@link #PERMISSION_POLICY_PROMPT},
* {@link #PERMISSION_POLICY_AUTO_GRANT} and {@link #PERMISSION_POLICY_AUTO_DENY}.
@@ -10594,6 +10598,31 @@
* application built with a {@code targetSdkVersion} <
* {@link android.os.Build.VERSION_CODES#M} the app-op matching the permission is set to
* {@link android.app.AppOpsManager#MODE_IGNORED}, but the permission stays granted.
+ * <p>
+ * NOTE: On devices running {@link android.os.Build.VERSION_CODES#S} and above, control over
+ * the following, sensors-related, permissions is restricted:
+ * <ul>
+ * <li>Manifest.permission.ACCESS_FINE_LOCATION</li>
+ * <li>Manifest.permission.ACCESS_BACKGROUND_LOCATION</li>
+ * <li>Manifest.permission.ACCESS_COARSE_LOCATION</li>
+ * <li>Manifest.permission.CAMERA</li>
+ * <li>Manifest.permission.RECORD_AUDIO</li>
+ * <li>Manifest.permission.RECORD_BACKGROUND_AUDIO</li>
+ * <li>Manifest.permission.ACTIVITY_RECOGNITION</li>
+ * <li>Manifest.permission.BODY_SENSORS</li>
+ * </ul>
+ * <p>
+ * A profile owner may not grant these permissions (i.e. call this method with any of the
+ * permissions listed above and {@code grantState} of {@code #PERMISSION_GRANT_STATE_GRANTED}),
+ * but may deny them.
+ * <p>
+ * A device owner, by default, may continue granting these permissions. However, for increased
+ * user control, the admin may opt out of controlling grants for these permissions by including
+ * {@link #EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT} in the provisioning parameters. In that
+ * case the device owner's control will be limited do denying these permissions.
+ * <p>
+ * Attempts by the admin to grant these permissions, when the admin is restricted from doing
+ * so, will be silently ignored (no exception will be thrown).
*
* @param admin Which profile or device owner this request is associated with.
* @param packageName The application to grant or revoke a permission to.
@@ -13268,4 +13297,58 @@
}
}
}
+
+ /**
+ * Resets the default cross profile intent filters that were set during
+ * {@link #createAndProvisionManagedProfile} between {@code userId} and all it's managed
+ * profiles if any.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {
+ if (mService != null) {
+ try {
+ mService.resetDefaultCrossProfileIntentFilters(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ }
+ /**
+ * Returns true if the caller is running on a device where the admin can grant
+ * permissions related to device sensors.
+ * This is a signal that the device is a fully-managed device where personal usage is
+ * discouraged.
+ * The list of permissions is listed in
+ * {@link #setPermissionGrantState(ComponentName, String, String, int)}.
+ *
+ * May be called by any app.
+ * @return true if the app can grant device sensors-related permissions, false otherwise.
+ */
+ public boolean canAdminGrantSensorsPermissions() {
+ throwIfParentInstance("canAdminGrantSensorsPermissions");
+ return canAdminGrantSensorsPermissionsForUser(myUserId());
+ }
+
+ /**
+ * Returns true if the admin can control grants of sensors-related permissions, for
+ * a given user.
+ *
+ * @hide
+ * @param userId The ID of the user to check.
+ * @return if the admin may grant these permissions, false otherwise.
+ */
+ @SystemApi
+ public boolean canAdminGrantSensorsPermissionsForUser(int userId) {
+ if (mService == null) {
+ return false;
+ }
+ try {
+ return mService.canAdminGrantSensorsPermissionsForUser(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
index 83af019..5e1cbad 100644
--- a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
+++ b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
@@ -42,6 +42,7 @@
private final long mLocalTime;
@SuppressLint("UseIcu")
@Nullable private final Locale mLocale;
+ private final boolean mDeviceOwnerCanGrantSensorsPermissions;
private FullyManagedDeviceProvisioningParams(
@NonNull ComponentName deviceAdminComponentName,
@@ -49,13 +50,16 @@
boolean leaveAllSystemAppsEnabled,
@Nullable String timeZone,
long localTime,
- @Nullable @SuppressLint("UseIcu") Locale locale) {
+ @Nullable @SuppressLint("UseIcu") Locale locale,
+ boolean deviceOwnerCanGrantSensorsPermissions) {
this.mDeviceAdminComponentName = requireNonNull(deviceAdminComponentName);
this.mOwnerName = requireNonNull(ownerName);
this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled;
this.mTimeZone = timeZone;
this.mLocalTime = localTime;
this.mLocale = locale;
+ this.mDeviceOwnerCanGrantSensorsPermissions =
+ deviceOwnerCanGrantSensorsPermissions;
}
private FullyManagedDeviceProvisioningParams(
@@ -64,13 +68,15 @@
boolean leaveAllSystemAppsEnabled,
@Nullable String timeZone,
long localTime,
- @Nullable String localeStr) {
+ @Nullable String localeStr,
+ boolean deviceOwnerCanGrantSensorsPermissions) {
this(deviceAdminComponentName,
ownerName,
leaveAllSystemAppsEnabled,
timeZone,
localTime,
- getLocale(localeStr));
+ getLocale(localeStr),
+ deviceOwnerCanGrantSensorsPermissions);
}
@Nullable
@@ -107,6 +113,14 @@
}
/**
+ * @return true if the device owner can control sensor-related permission grants, false
+ * if the device owner has opted out of it.
+ */
+ public boolean canDeviceOwnerGrantSensorsPermissions() {
+ return mDeviceOwnerCanGrantSensorsPermissions;
+ }
+
+ /**
* Builder class for {@link FullyManagedDeviceProvisioningParams} objects.
*/
public static final class Builder {
@@ -117,6 +131,8 @@
private long mLocalTime;
@SuppressLint("UseIcu")
@Nullable private Locale mLocale;
+ // Default to allowing control over sensor permission grants.
+ boolean mDeviceOwnerCanGrantSensorsPermissions = true;
/**
* Initialize a new {@link Builder} to construct a
@@ -181,6 +197,17 @@
}
/**
+ * Marks that the Device Owner may grant permissions related to device sensors.
+ * See {@link DevicePolicyManager#EXTRA_PROVISIONING_PERMISSION_GRANT_OPT_OUT}.
+ */
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setDeviceOwnerCanGrantSensorsPermissions(boolean mayGrant) {
+ mDeviceOwnerCanGrantSensorsPermissions = mayGrant;
+ return this;
+ }
+
+ /**
* Combines all of the attributes that have been set on this {@code Builder}
*
* @return a new {@link FullyManagedDeviceProvisioningParams} object.
@@ -193,7 +220,8 @@
mLeaveAllSystemAppsEnabled,
mTimeZone,
mLocalTime,
- mLocale);
+ mLocale,
+ mDeviceOwnerCanGrantSensorsPermissions);
}
}
@@ -211,6 +239,8 @@
+ ", mTimeZone=" + (mTimeZone == null ? "null" : mTimeZone)
+ ", mLocalTime=" + mLocalTime
+ ", mLocale=" + (mLocale == null ? "null" : mLocale)
+ + ", mDeviceOwnerCanGrantSensorsPermissions="
+ + mDeviceOwnerCanGrantSensorsPermissions
+ '}';
}
@@ -222,6 +252,7 @@
dest.writeString(mTimeZone);
dest.writeLong(mLocalTime);
dest.writeString(mLocale == null ? null : mLocale.toLanguageTag());
+ dest.writeBoolean(mDeviceOwnerCanGrantSensorsPermissions);
}
@NonNull
@@ -235,6 +266,7 @@
String timeZone = in.readString();
long localtime = in.readLong();
String locale = in.readString();
+ boolean deviceOwnerCanGrantSensorsPermissions = in.readBoolean();
return new FullyManagedDeviceProvisioningParams(
componentName,
@@ -242,7 +274,8 @@
leaveAllSystemAppsEnabled,
timeZone,
localtime,
- locale);
+ locale,
+ deviceOwnerCanGrantSensorsPermissions);
}
@Override
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index f9ee153..89f30cc 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -498,4 +498,7 @@
UserHandle createAndProvisionManagedProfile(in ManagedProfileProvisioningParams provisioningParams, in String callerPackage);
void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams, in String callerPackage);
+
+ void resetDefaultCrossProfileIntentFilters(int userId);
+ boolean canAdminGrantSensorsPermissionsForUser(int userId);
}
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 587e883..742d05c 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -41,6 +41,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
/**
@@ -460,24 +461,40 @@
Set<PathWithRequiredFlags> excludes,
Map<String, Set<PathWithRequiredFlags>> includes)
throws IOException, XmlPullParserException {
+ verifyTopLevelTag(parser, "full-backup-content");
+
+ parseRules(parser, excludes, includes, Optional.empty());
+
+ logParsingResults(excludes, includes);
+ }
+
+ private void verifyTopLevelTag(XmlPullParser parser, String tag)
+ throws XmlPullParserException, IOException {
int event = parser.getEventType(); // START_DOCUMENT
while (event != XmlPullParser.START_TAG) {
event = parser.next();
}
- if (!"full-backup-content".equals(parser.getName())) {
+ if (!tag.equals(parser.getName())) {
throw new XmlPullParserException("Xml file didn't start with correct tag" +
- " (<full-backup-content>). Found \"" + parser.getName() + "\"");
+ " (" + tag + " ). Found \"" + parser.getName() + "\"");
}
if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
Log.v(TAG_XML_PARSER, "\n");
Log.v(TAG_XML_PARSER, "====================================================");
- Log.v(TAG_XML_PARSER, "Found valid fullBackupContent; parsing xml resource.");
+ Log.v(TAG_XML_PARSER, "Found valid " + tag + "; parsing xml resource.");
Log.v(TAG_XML_PARSER, "====================================================");
Log.v(TAG_XML_PARSER, "");
}
+ }
+ private void parseRules(XmlPullParser parser,
+ Set<PathWithRequiredFlags> excludes,
+ Map<String, Set<PathWithRequiredFlags>> includes,
+ Optional<Integer> maybeRequiredFlags)
+ throws IOException, XmlPullParserException {
+ int event;
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
switch (event) {
case XmlPullParser.START_TAG:
@@ -498,13 +515,7 @@
break;
}
- int requiredFlags = 0; // no transport flags are required by default
- if (TAG_INCLUDE.equals(parser.getName())) {
- // requiredFlags are only supported for <include /> tag, for <exclude />
- // we should always leave them as the default = 0
- requiredFlags = getRequiredFlagsFromString(
- parser.getAttributeValue(null, "requireFlags"));
- }
+ int requiredFlags = getRequiredFlagsForRule(parser, maybeRequiredFlags);
// retrieve the include/exclude set we'll be adding this rule to
Set<PathWithRequiredFlags> activeSet = parseCurrentTagForDomain(
@@ -542,7 +553,7 @@
// Special case for sharedpref files (not dirs) also add ".xml" suffix file.
if ("sharedpref".equals(domainFromXml) && !canonicalFile.isDirectory() &&
- !canonicalFile.getCanonicalPath().endsWith(".xml")) {
+ !canonicalFile.getCanonicalPath().endsWith(".xml")) {
final String canonicalXmlPath =
canonicalFile.getCanonicalPath() + ".xml";
activeSet.add(new PathWithRequiredFlags(canonicalXmlPath,
@@ -554,6 +565,10 @@
}
}
}
+ }
+
+ private void logParsingResults(Set<PathWithRequiredFlags> excludes,
+ Map<String, Set<PathWithRequiredFlags>> includes) {
if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
Log.v(TAG_XML_PARSER, "\n");
Log.v(TAG_XML_PARSER, "Xml resource parsing complete.");
@@ -613,6 +628,24 @@
return flags;
}
+ private int getRequiredFlagsForRule(XmlPullParser parser,
+ Optional<Integer> maybeRequiredFlags) {
+ if (maybeRequiredFlags.isPresent()) {
+ // This is the new config format where required flags are specified for the whole
+ // section, not per rule.
+ return maybeRequiredFlags.get();
+ }
+
+ if (TAG_INCLUDE.equals(parser.getName())) {
+ // In the legacy config, requiredFlags are only supported for <include /> tag,
+ // for <exclude /> we should always leave them as the default = 0.
+ return getRequiredFlagsFromString(
+ parser.getAttributeValue(null, "requireFlags"));
+ }
+
+ return 0;
+ }
+
private Set<PathWithRequiredFlags> parseCurrentTagForDomain(XmlPullParser parser,
Set<PathWithRequiredFlags> excludes,
Map<String, Set<PathWithRequiredFlags>> includes, String domain)
diff --git a/core/java/android/app/smartspace/SmartspaceTargetEvent.java b/core/java/android/app/smartspace/SmartspaceTargetEvent.java
index 1e0653d..920b9fe 100644
--- a/core/java/android/app/smartspace/SmartspaceTargetEvent.java
+++ b/core/java/android/app/smartspace/SmartspaceTargetEvent.java
@@ -138,6 +138,15 @@
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 2c1e951..30ea5c4 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -30,7 +30,7 @@
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 f74d16e..31781ec 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -23,6 +23,7 @@
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 @@
* @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/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index e96e22c4..42214d0 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -247,12 +247,29 @@
* A preview of what the AppWidget will look like after it's configured.
* If not supplied, the AppWidget's icon will be used.
*
- * <p>This field corresponds to the <code>android:previewImage</code> attribute in
- * the <code><receiver></code> element in the AndroidManifest.xml file.
+ * <p>This field corresponds to the <code>android:previewImage</code> attribute in the AppWidget
+ * meta-data file.
*/
public int previewImage;
/**
+ * The layout resource id of a preview of what the AppWidget will look like after it's
+ * configured.
+ *
+ * <p>Unlike previewImage, previewLayout can better showcase AppWidget in different locales,
+ * system themes, display sizes & density etc.
+ *
+ * <p>If supplied, this will take precedence over the previewImage on supported widget hosts.
+ * Otherwise, previewImage will be used.
+ *
+ * <p>This field corresponds to the <code>android:previewLayout</code> attribute in the
+ * AppWidget meta-data file.
+ */
+ @SuppressLint("MutableBareField")
+ @IdRes
+ public int previewLayout;
+
+ /**
* The rules by which a widget can be resized. See {@link #RESIZE_NONE},
* {@link #RESIZE_NONE}, {@link #RESIZE_HORIZONTAL},
* {@link #RESIZE_VERTICAL}, {@link #RESIZE_BOTH}.
@@ -320,6 +337,7 @@
this.label = in.readString();
this.icon = in.readInt();
this.previewImage = in.readInt();
+ this.previewLayout = in.readInt();
this.autoAdvanceViewId = in.readInt();
this.resizeMode = in.readInt();
this.widgetCategory = in.readInt();
@@ -429,6 +447,7 @@
out.writeString(this.label);
out.writeInt(this.icon);
out.writeInt(this.previewImage);
+ out.writeInt(this.previewLayout);
out.writeInt(this.autoAdvanceViewId);
out.writeInt(this.resizeMode);
out.writeInt(this.widgetCategory);
@@ -453,6 +472,7 @@
that.label = this.label;
that.icon = this.icon;
that.previewImage = this.previewImage;
+ that.previewLayout = this.previewLayout;
that.autoAdvanceViewId = this.autoAdvanceViewId;
that.resizeMode = this.resizeMode;
that.widgetCategory = this.widgetCategory;
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 7eda50e..ea7e5ea 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -3198,6 +3198,61 @@
}
/**
+ * Register a callback to receive events whenever the bluetooth stack goes down and back up,
+ * e.g. in the event the bluetooth is turned off/on via settings.
+ *
+ * If the bluetooth stack is currently up, there will not be an initial callback call.
+ * You can use the return value as an indication of this being the case.
+ *
+ * Callbacks will be delivered on a binder thread.
+ *
+ * @return whether bluetooth is already up currently
+ *
+ * @hide
+ */
+ public boolean registerServiceLifecycleCallback(ServiceLifecycleCallback callback) {
+ return getBluetoothService(callback.mRemote) != null;
+ }
+
+ /**
+ * Unregister a callback registered via {@link #registerServiceLifecycleCallback}
+ *
+ * @hide
+ */
+ public void unregisterServiceLifecycleCallback(ServiceLifecycleCallback callback) {
+ removeServiceStateCallback(callback.mRemote);
+ }
+
+ /**
+ * A callback for {@link #registerServiceLifecycleCallback}
+ *
+ * @hide
+ */
+ public abstract static class ServiceLifecycleCallback {
+
+ /** Called when the bluetooth stack is up */
+ public abstract void onBluetoothServiceUp();
+
+ /** Called when the bluetooth stack is down */
+ public abstract void onBluetoothServiceDown();
+
+ IBluetoothManagerCallback mRemote = new IBluetoothManagerCallback.Stub() {
+ @Override
+ public void onBluetoothServiceUp(IBluetooth bluetoothService) {
+ ServiceLifecycleCallback.this.onBluetoothServiceUp();
+ }
+
+ @Override
+ public void onBluetoothServiceDown() {
+ ServiceLifecycleCallback.this.onBluetoothServiceDown();
+ }
+
+ @Override
+ public void onBrEdrDown() {}
+ };
+ }
+
+ /**
* Starts a scan for Bluetooth LE devices.
*
* <p>Results of the scan are reported using the
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index c0736a6..d82cf19 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -167,6 +167,11 @@
/** @hide */
@NonNull
@SystemApi
+ public static final ParcelUuid VOLUME_CONTROL =
+ ParcelUuid.fromString("00001844-0000-1000-8000-00805F9B34FB");
+ /** @hide */
+ @NonNull
+ @SystemApi
public static final ParcelUuid BASE_UUID =
ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
diff --git a/core/java/android/companion/Association.java b/core/java/android/companion/Association.java
index 17bf11b..9007d9d 100644
--- a/core/java/android/companion/Association.java
+++ b/core/java/android/companion/Association.java
@@ -23,6 +23,7 @@
import com.android.internal.util.DataClass;
+import java.util.Date;
import java.util.Objects;
/**
@@ -38,16 +39,23 @@
private final @NonNull String mDeviceMacAddress;
private final @NonNull String mPackageName;
private final @Nullable String mDeviceProfile;
- private final boolean mKeepProfilePrivilegesWhenDeviceAway;
+ 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.21.
+
+
+
+ // Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -71,7 +79,8 @@
@NonNull String deviceMacAddress,
@NonNull String packageName,
@Nullable String deviceProfile,
- boolean keepProfilePrivilegesWhenDeviceAway) {
+ boolean notifyOnDeviceNearby,
+ long timeApprovedMs) {
this.mUserId = userId;
com.android.internal.util.AnnotationValidations.validate(
UserIdInt.class, null, mUserId);
@@ -82,7 +91,8 @@
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPackageName);
this.mDeviceProfile = deviceProfile;
- this.mKeepProfilePrivilegesWhenDeviceAway = keepProfilePrivilegesWhenDeviceAway;
+ this.mNotifyOnDeviceNearby = notifyOnDeviceNearby;
+ this.mTimeApprovedMs = timeApprovedMs;
// onConstructed(); // You can define this method to get a callback
}
@@ -103,8 +113,13 @@
}
@DataClass.Generated.Member
- public boolean isKeepProfilePrivilegesWhenDeviceAway() {
- return mKeepProfilePrivilegesWhenDeviceAway;
+ public boolean isNotifyOnDeviceNearby() {
+ return mNotifyOnDeviceNearby;
+ }
+
+ @DataClass.Generated.Member
+ public long getTimeApprovedMs() {
+ return mTimeApprovedMs;
}
@Override
@@ -118,7 +133,8 @@
"deviceMacAddress = " + mDeviceMacAddress + ", " +
"packageName = " + mPackageName + ", " +
"deviceProfile = " + mDeviceProfile + ", " +
- "keepProfilePrivilegesWhenDeviceAway = " + mKeepProfilePrivilegesWhenDeviceAway +
+ "notifyOnDeviceNearby = " + mNotifyOnDeviceNearby + ", " +
+ "timeApprovedMs = " + timeApprovedMsToString() +
" }";
}
@@ -139,7 +155,8 @@
&& Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
&& Objects.equals(mPackageName, that.mPackageName)
&& Objects.equals(mDeviceProfile, that.mDeviceProfile)
- && mKeepProfilePrivilegesWhenDeviceAway == that.mKeepProfilePrivilegesWhenDeviceAway;
+ && mNotifyOnDeviceNearby == that.mNotifyOnDeviceNearby
+ && mTimeApprovedMs == that.mTimeApprovedMs;
}
@Override
@@ -153,7 +170,8 @@
_hash = 31 * _hash + Objects.hashCode(mDeviceMacAddress);
_hash = 31 * _hash + Objects.hashCode(mPackageName);
_hash = 31 * _hash + Objects.hashCode(mDeviceProfile);
- _hash = 31 * _hash + Boolean.hashCode(mKeepProfilePrivilegesWhenDeviceAway);
+ _hash = 31 * _hash + Boolean.hashCode(mNotifyOnDeviceNearby);
+ _hash = 31 * _hash + Long.hashCode(mTimeApprovedMs);
return _hash;
}
@@ -164,13 +182,14 @@
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
- if (mKeepProfilePrivilegesWhenDeviceAway) flg |= 0x10;
+ if (mNotifyOnDeviceNearby) flg |= 0x10;
if (mDeviceProfile != null) flg |= 0x8;
dest.writeByte(flg);
dest.writeInt(mUserId);
dest.writeString(mDeviceMacAddress);
dest.writeString(mPackageName);
if (mDeviceProfile != null) dest.writeString(mDeviceProfile);
+ dest.writeLong(mTimeApprovedMs);
}
@Override
@@ -185,11 +204,12 @@
// static FieldType unparcelFieldName(Parcel in) { ... }
byte flg = in.readByte();
- boolean keepProfilePrivilegesWhenDeviceAway = (flg & 0x10) != 0;
+ boolean notifyOnDeviceNearby = (flg & 0x10) != 0;
int userId = in.readInt();
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(
@@ -201,7 +221,8 @@
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPackageName);
this.mDeviceProfile = deviceProfile;
- this.mKeepProfilePrivilegesWhenDeviceAway = keepProfilePrivilegesWhenDeviceAway;
+ this.mNotifyOnDeviceNearby = notifyOnDeviceNearby;
+ this.mTimeApprovedMs = timeApprovedMs;
// onConstructed(); // You can define this method to get a callback
}
@@ -221,10 +242,10 @@
};
@DataClass.Generated(
- time = 1606940835778L,
- codegenVersion = "1.0.21",
+ 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 mKeepProfilePrivilegesWhenDeviceAway\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 527d8df..95d3515 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -49,4 +49,6 @@
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/Context.java b/core/java/android/content/Context.java
index 5d28216..2a402b2 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3552,6 +3552,7 @@
//@hide: NETWORK_SCORE_SERVICE,
USAGE_STATS_SERVICE,
MEDIA_SESSION_SERVICE,
+ MEDIA_COMMUNICATION_SERVICE,
BATTERY_SERVICE,
JOB_SCHEDULER_SERVICE,
//@hide: PERSISTENT_DATA_BLOCK_SERVICE,
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 6ec1169..01ff432 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -923,6 +923,14 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* @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/tools/hiddenapi/Android.bp b/core/java/android/content/pm/IOnChecksumsReadyListener.aidl
similarity index 64%
copy from tools/hiddenapi/Android.bp
copy to core/java/android/content/pm/IOnChecksumsReadyListener.aidl
index e0eb06cb..7963ce1 100644
--- a/tools/hiddenapi/Android.bp
+++ b/core/java/android/content/pm/IOnChecksumsReadyListener.aidl
@@ -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,17 +14,14 @@
* limitations under the License.
*/
-python_binary_host {
- name: "merge_csv",
- main: "merge_csv.py",
- srcs: ["merge_csv.py"],
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- embedded_launcher: true
- },
- },
+package android.content.pm;
+
+import android.content.pm.ApkChecksum;
+
+/**
+ * Listener that gets notified when checksums are available.
+ * {@hide}
+ */
+oneway interface IOnChecksumsReadyListener {
+ void onChecksumsReady(in List<ApkChecksum> checksums);
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index b8829bb..a46876e 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -27,6 +27,7 @@
import android.content.pm.FeatureInfo;
import android.content.pm.IDexModuleRegisterCallback;
import android.content.pm.InstallSourceInfo;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDeleteObserver2;
@@ -754,7 +755,7 @@
void notifyPackagesReplacedReceived(in String[] packages);
- void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId);
+ void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IOnChecksumsReadyListener onChecksumsReadyListener, int userId);
//------------------------------------------------------------------------
//
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 6292575..0c0e402 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -450,6 +450,7 @@
FLAG_MATCH_PINNED,
FLAG_MATCH_MANIFEST,
FLAG_MATCH_CACHED,
+ FLAG_MATCH_PINNED_BY_ANY_LAUNCHER,
FLAG_GET_KEY_FIELDS_ONLY,
FLAG_GET_PERSONS_DATA,
})
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d09d83f..b95b991b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3858,13 +3858,6 @@
public static final String EXTRA_FAILURE_EXISTING_PERMISSION
= "android.content.pm.extra.FAILURE_EXISTING_PERMISSION";
- /**
- * Extra field name for the ID of a package pending verification. Passed to
- * a package verifier and is used to call back to
- * @see #requestChecksums
- */
- public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
-
/**
* Permission flag: The permission is set in its current state
* by the user and apps can still request it at runtime.
@@ -8709,9 +8702,20 @@
*/
public static final @NonNull List<Certificate> TRUST_NONE = Collections.singletonList(null);
+ /** Listener that gets notified when checksums are available. */
+ @FunctionalInterface
+ public interface OnChecksumsReadyListener {
+ /**
+ * Called when the checksums are available.
+ *
+ * @param checksums array of checksums.
+ */
+ void onChecksumsReady(@NonNull List<ApkChecksum> checksums);
+ }
+
/**
* Requesting the checksums for APKs within a package.
- * The checksums will be returned asynchronously via statusReceiver.
+ * The checksums will be returned asynchronously via onChecksumsReadyListener.
*
* By default returns all readily available checksums:
* - enforced by platform,
@@ -8730,15 +8734,14 @@
* {@link #TRUST_ALL} will return checksums from any installer,
* {@link #TRUST_NONE} disables optimized installer-enforced checksums,
* otherwise the list has to be non-empty list of certificates.
- * @param statusReceiver called once when the results are available as
- * {@link #EXTRA_CHECKSUMS} of type {@link ApkChecksum}[].
+ * @param onChecksumsReadyListener called once when the results are available.
* @throws CertificateEncodingException if an encoding error occurs for trustedInstallers.
* @throws IllegalArgumentException if the list of trusted installer certificates is empty.
* @throws NameNotFoundException if a package with the given name cannot be found on the system.
*/
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
- @NonNull IntentSender statusReceiver)
+ @NonNull OnChecksumsReadyListener onChecksumsReadyListener)
throws CertificateEncodingException, NameNotFoundException {
throw new UnsupportedOperationException("requestChecksums not implemented in subclass");
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e6c0f6a..0819d17 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -54,6 +54,7 @@
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 @@
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 @@
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 5cc74c0..e115597 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -31,6 +31,7 @@
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 java.io.IOException;
import java.util.Arrays;
-import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
@@ -85,9 +85,10 @@
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 @@
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 String[] getOverlayPaths() {
+ @Nullable
+ public OverlayPaths getOverlayPaths() {
return overlayPaths;
}
- public void setOverlayPaths(String[] paths) {
- overlayPaths = paths;
- cachedOverlayPaths = null;
- }
-
- public Map<String, String[]> getSharedLibraryOverlayPaths() {
+ @Nullable
+ public Map<String, OverlayPaths> getSharedLibraryOverlayPaths() {
return sharedLibraryOverlayPaths;
}
- public void setSharedLibraryOverlayPaths(String library, String[] paths) {
+ /**
+ * 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;
+ }
+
+ /**
+ * 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 @@
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 0000000..a4db733
--- /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 b7365b3..fb0d904 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.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 @@
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/pm/parsing/component/ParsedPermissionUtils.java b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
index cbd2c55..9012b5ce 100644
--- a/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedPermissionUtils.java
@@ -108,17 +108,14 @@
permission.protectionLevel = PermissionInfo.fixProtectionLevel(permission.protectionLevel);
- if (permission.getProtectionFlags() != 0) {
- if ((permission.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
- && (permission.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY)
- == 0
- && (permission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
- != PermissionInfo.PROTECTION_SIGNATURE
- && (permission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
- != PermissionInfo.PROTECTION_INTERNAL) {
- return input.error("<permission> protectionLevel specifies a non-instant flag "
- + "but is not based on signature or internal type");
- }
+ final int otherProtectionFlags = permission.getProtectionFlags()
+ & ~(PermissionInfo.PROTECTION_FLAG_APPOP | PermissionInfo.PROTECTION_FLAG_INSTANT
+ | PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY);
+ if (otherProtectionFlags != 0
+ && permission.getProtection() != PermissionInfo.PROTECTION_SIGNATURE
+ && permission.getProtection() != PermissionInfo.PROTECTION_INTERNAL) {
+ return input.error("<permission> protectionLevel specifies a non-instant, non-appop,"
+ + " non-runtimeOnly flag but is not based on signature or internal type");
}
return ComponentParseUtils.parseAllMetaData(pkg, res, parser, tag, permission, input);
diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java
index 05769dd..99b56a8 100644
--- a/core/java/android/content/res/ResourcesKey.java
+++ b/core/java/android/content/res/ResourcesKey.java
@@ -38,7 +38,7 @@
public final String[] mSplitResDirs;
@Nullable
- public final String[] mOverlayDirs;
+ public final String[] mOverlayPaths;
@Nullable
public final String[] mLibDirs;
@@ -67,7 +67,7 @@
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 @@
@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 @@
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 @@
@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 @@
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 @@
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 @@
}
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/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 4145a72..08b1e24 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -29,7 +29,6 @@
import android.annotation.TestApi;
import android.content.Context;
import android.os.RemoteException;
-import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.util.Slog;
@@ -47,6 +46,13 @@
private static final String TAG = "BiometricManager";
/**
+ * An ID that should match any biometric sensor on the device.
+ *
+ * @hide
+ */
+ public static final int SENSOR_ID_ANY = -1;
+
+ /**
* No error detected.
*/
public static final int BIOMETRIC_SUCCESS =
@@ -139,7 +145,7 @@
*
* <p>This corresponds to {@link KeyProperties#AUTH_BIOMETRIC_STRONG} during key generation.
*
- * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)
+ * @see android.security.keystore.KeyGenParameterSpec.Builder
*/
int BIOMETRIC_STRONG = 0x000F;
@@ -182,7 +188,7 @@
* <p>This corresponds to {@link KeyProperties#AUTH_DEVICE_CREDENTIAL} during key
* generation.
*
- * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)
+ * @see android.security.keystore.KeyGenParameterSpec.Builder
*/
int DEVICE_CREDENTIAL = 1 << 15;
}
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 76cf9b9..4f6a7c7 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -36,7 +36,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.security.identity.IdentityCredential;
-import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.text.TextUtils;
import android.util.Log;
@@ -325,7 +324,7 @@
* request authentication with the proper set of authenticators (e.g. match the
* authenticators specified during key generation).
*
- * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)
+ * @see android.security.keystore.KeyGenParameterSpec.Builder
* @see KeyProperties#AUTH_BIOMETRIC_STRONG
* @see KeyProperties#AUTH_DEVICE_CREDENTIAL
*
@@ -365,6 +364,21 @@
}
/**
+ * If set, authenticate using the biometric sensor with the given ID.
+ *
+ * @param sensorId The ID of a biometric sensor, or -1 to allow any sensor (default).
+ * @return This builder.
+ *
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ @NonNull
+ public Builder setSensorId(int sensorId) {
+ mPromptInfo.setSensorId(sensorId);
+ return this;
+ }
+
+ /**
* Creates a {@link BiometricPrompt}.
*
* @return An instance of {@link BiometricPrompt}.
@@ -589,7 +603,8 @@
*
* <p>Cryptographic operations in Android can be split into two categories: auth-per-use and
* time-based. This is specified during key creation via the timeout parameter of the
- * {@link KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)} API.
+ * {@code setUserAuthenticationParameters(int, int)} method of {@link
+ * android.security.keystore.KeyGenParameterSpec.Builder}.
*
* <p>CryptoObjects are used to unlock auth-per-use keys via
* {@link BiometricPrompt#authenticate(CryptoObject, CancellationSignal, Executor,
@@ -778,6 +793,27 @@
@NonNull @CallbackExecutor Executor executor,
@NonNull AuthenticationCallback callback,
int userId) {
+ authenticateUserForOperation(cancel, executor, callback, userId, 0 /* operationId */);
+ }
+
+ /**
+ * Authenticates for the given user and keystore operation.
+ *
+ * @param cancel An object that can be used to cancel authentication
+ * @param executor An executor to handle callback events
+ * @param callback An object to receive authentication events
+ * @param userId The user to authenticate
+ * @param operationId The keystore operation associated with authentication
+ *
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void authenticateUserForOperation(
+ @NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AuthenticationCallback callback,
+ int userId,
+ long operationId) {
if (cancel == null) {
throw new IllegalArgumentException("Must supply a cancellation signal");
}
@@ -787,7 +823,7 @@
if (callback == null) {
throw new IllegalArgumentException("Must supply a callback");
}
- authenticateInternal(null /* crypto */, cancel, executor, callback, userId);
+ authenticateInternal(operationId, cancel, executor, callback, userId);
}
/**
@@ -912,11 +948,31 @@
}
}
- private void authenticateInternal(@Nullable CryptoObject crypto,
+ private void authenticateInternal(
+ @Nullable CryptoObject crypto,
@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
@NonNull AuthenticationCallback callback,
int userId) {
+
+ mCryptoObject = crypto;
+ final long operationId = crypto != null ? crypto.getOpId() : 0L;
+ authenticateInternal(operationId, cancel, executor, callback, userId);
+ }
+
+ private void authenticateInternal(
+ long operationId,
+ @NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AuthenticationCallback callback,
+ int userId) {
+
+ // Ensure we don't return the wrong crypto object as an auth result.
+ if (mCryptoObject != null && mCryptoObject.getOpId() != operationId) {
+ Log.w(TAG, "CryptoObject operation ID does not match argument; setting field to null");
+ mCryptoObject = null;
+ }
+
try {
if (cancel.isCanceled()) {
Log.w(TAG, "Authentication already canceled");
@@ -925,13 +981,11 @@
cancel.setOnCancelListener(new OnAuthenticationCancelListener());
}
- mCryptoObject = crypto;
mExecutor = executor;
mAuthenticationCallback = callback;
- final long operationId = crypto != null ? crypto.getOpId() : 0;
final PromptInfo promptInfo;
- if (crypto != null) {
+ if (operationId != 0L) {
// Allowed authenticators should default to BIOMETRIC_STRONG for crypto auth.
// Note that we use a new PromptInfo here so as to not overwrite the application's
// preference, since it is possible that the same prompt configuration be used
@@ -952,10 +1006,9 @@
} catch (RemoteException e) {
Log.e(TAG, "Remote exception while authenticating", e);
- mExecutor.execute(() -> {
- callback.onAuthenticationError(BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE,
- mContext.getString(R.string.biometric_error_hw_unavailable));
- });
+ mExecutor.execute(() -> callback.onAuthenticationError(
+ BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ mContext.getString(R.string.biometric_error_hw_unavailable)));
}
}
}
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index c2eff7d..0e99f31 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -40,6 +40,7 @@
private @BiometricManager.Authenticators.Types int mAuthenticators;
private boolean mDisallowBiometricsIfPolicyExists;
private boolean mReceiveSystemEvents;
+ private int mSensorId = -1;
public PromptInfo() {
@@ -59,6 +60,7 @@
mAuthenticators = in.readInt();
mDisallowBiometricsIfPolicyExists = in.readBoolean();
mReceiveSystemEvents = in.readBoolean();
+ mSensorId = in.readInt();
}
public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -93,6 +95,7 @@
dest.writeInt(mAuthenticators);
dest.writeBoolean(mDisallowBiometricsIfPolicyExists);
dest.writeBoolean(mReceiveSystemEvents);
+ dest.writeInt(mSensorId);
}
public boolean containsPrivateApiConfigurations() {
@@ -166,6 +169,10 @@
mReceiveSystemEvents = receiveSystemEvents;
}
+ public void setSensorId(int sensorId) {
+ mSensorId = sensorId;
+ }
+
// Getters
public CharSequence getTitle() {
@@ -226,4 +233,8 @@
public boolean isReceiveSystemEvents() {
return mReceiveSystemEvents;
}
+
+ public int getSensorId() {
+ return mSensorId;
+ }
}
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index 29a6ee2..f175e7b 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -16,8 +16,12 @@
package android.hardware.devicestate;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.Context;
import java.util.concurrent.Executor;
@@ -28,13 +32,19 @@
*
* @hide
*/
+@TestApi
@SystemService(Context.DEVICE_STATE_SERVICE)
public final class DeviceStateManager {
- /** Invalid device state. */
+ /**
+ * Invalid device state.
+ *
+ * @hide
+ */
public static final int INVALID_DEVICE_STATE = -1;
- private DeviceStateManagerGlobal mGlobal;
+ private final DeviceStateManagerGlobal mGlobal;
+ /** @hide */
public DeviceStateManager() {
DeviceStateManagerGlobal global = DeviceStateManagerGlobal.getInstance();
if (global == null) {
@@ -45,23 +55,73 @@
}
/**
+ * Returns the list of device states that are supported and can be requested with
+ * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ */
+ @NonNull
+ public int[] getSupportedStates() {
+ return mGlobal.getSupportedStates();
+ }
+
+ /**
+ * Submits a {@link DeviceStateRequest request} to modify the device state.
+ * <p>
+ * By default, the request is kept active until a call to
+ * {@link #cancelRequest(DeviceStateRequest)} or until one of the following occurs:
+ * <ul>
+ * <li>Another processes submits a request succeeding this request in which case the request
+ * will be suspended until the interrupting request is canceled.
+ * <li>The requested state has become unsupported.
+ * <li>The process submitting the request dies.
+ * </ul>
+ * However, this behavior can be changed by setting flags on the {@link DeviceStateRequest}.
+ *
+ * @throws IllegalArgumentException if the requested state is unsupported.
+ * @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE}
+ * permission is not held.
+ *
+ * @see DeviceStateRequest
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+ public void requestState(@NonNull DeviceStateRequest request,
+ @Nullable @CallbackExecutor Executor executor,
+ @Nullable DeviceStateRequest.Callback callback) {
+ mGlobal.requestState(request, callback, executor);
+ }
+
+ /**
+ * Cancels a {@link DeviceStateRequest request} previously submitted with a call to
+ * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ * <p>
+ * This method is noop if the {@code request} has not been submitted with a call to
+ * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ *
+ * @throws SecurityException if the {@link android.Manifest.permission#CONTROL_DEVICE_STATE}
+ * permission is not held.
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+ public void cancelRequest(@NonNull DeviceStateRequest request) {
+ mGlobal.cancelRequest(request);
+ }
+
+ /**
* Registers a listener to receive notifications about changes in device state.
*
- * @param listener the listener to register.
* @param executor the executor to process notifications.
+ * @param listener the listener to register.
*
* @see DeviceStateListener
*/
- public void registerDeviceStateListener(@NonNull DeviceStateListener listener,
- @NonNull Executor executor) {
+ public void addDeviceStateListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull DeviceStateListener listener) {
mGlobal.registerDeviceStateListener(listener, executor);
}
/**
* Unregisters a listener previously registered with
- * {@link #registerDeviceStateListener(DeviceStateListener, Executor)}.
+ * {@link #addDeviceStateListener(Executor, DeviceStateListener)}.
*/
- public void unregisterDeviceStateListener(@NonNull DeviceStateListener listener) {
+ public void removeDeviceStateListener(@NonNull DeviceStateListener listener) {
mGlobal.unregisterDeviceStateListener(listener);
}
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index c8905038..b9ae88e 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -20,9 +20,11 @@
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager.DeviceStateListener;
+import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.ArrayMap;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -67,6 +69,9 @@
@GuardedBy("mLock")
private final ArrayList<DeviceStateListenerWrapper> mListeners = new ArrayList<>();
+ @GuardedBy("mLock")
+ private final ArrayMap<IBinder, DeviceStateRequestWrapper> mRequests = new ArrayMap<>();
+
@Nullable
@GuardedBy("mLock")
private Integer mLastReceivedState;
@@ -77,9 +82,84 @@
}
/**
+ * Returns the set of supported device states.
+ *
+ * @see DeviceStateManager#getSupportedStates()
+ */
+ public int[] getSupportedStates() {
+ try {
+ return mDeviceStateManager.getSupportedDeviceStates();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Submits a {@link DeviceStateRequest request} to modify the device state.
+ *
+ * @see DeviceStateManager#requestState(DeviceStateRequest,
+ * Executor, DeviceStateRequest.Callback)
+ * @see DeviceStateRequest
+ */
+ public void requestState(@NonNull DeviceStateRequest request,
+ @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
+ if (callback == null && executor != null) {
+ throw new IllegalArgumentException("Callback must be supplied with executor.");
+ } else if (executor == null && callback != null) {
+ throw new IllegalArgumentException("Executor must be supplied with callback.");
+ }
+
+ synchronized (mLock) {
+ registerCallbackIfNeededLocked();
+
+ if (findRequestTokenLocked(request) != null) {
+ // This request has already been submitted.
+ return;
+ }
+
+ // Add the request wrapper to the mRequests array before requesting the state as the
+ // callback could be triggered immediately if the mDeviceStateManager IBinder is in the
+ // same process as this instance.
+ IBinder token = new Binder();
+ mRequests.put(token, new DeviceStateRequestWrapper(request, callback, executor));
+
+ try {
+ mDeviceStateManager.requestState(token, request.getState(), request.getFlags());
+ } catch (RemoteException ex) {
+ mRequests.remove(token);
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Cancels a {@link DeviceStateRequest request} previously submitted with a call to
+ * {@link #requestState(DeviceStateRequest, DeviceStateRequest.Callback, Executor)}.
+ *
+ * @see DeviceStateManager#cancelRequest(DeviceStateRequest)
+ */
+ public void cancelRequest(@NonNull DeviceStateRequest request) {
+ synchronized (mLock) {
+ registerCallbackIfNeededLocked();
+
+ final IBinder token = findRequestTokenLocked(request);
+ if (token == null) {
+ // This request has not been submitted.
+ return;
+ }
+
+ try {
+ mDeviceStateManager.cancelRequest(token);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* Registers a listener to receive notifications about changes in device state.
*
- * @see DeviceStateManager#registerDeviceStateListener(DeviceStateListener, Executor)
+ * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener)
*/
@VisibleForTesting(visibility = Visibility.PACKAGE)
public void registerDeviceStateListener(@NonNull DeviceStateListener listener,
@@ -112,7 +192,7 @@
* Unregisters a listener previously registered with
* {@link #registerDeviceStateListener(DeviceStateListener, Executor)}.
*
- * @see DeviceStateManager#registerDeviceStateListener(DeviceStateListener, Executor)
+ * @see DeviceStateManager#addDeviceStateListener(Executor, DeviceStateListener)
*/
@VisibleForTesting(visibility = Visibility.PACKAGE)
public void unregisterDeviceStateListener(DeviceStateListener listener) {
@@ -144,6 +224,17 @@
return -1;
}
+ @Nullable
+ private IBinder findRequestTokenLocked(@NonNull DeviceStateRequest request) {
+ for (int i = 0; i < mRequests.size(); i++) {
+ if (mRequests.valueAt(i).mRequest.equals(request)) {
+ return mRequests.keyAt(i);
+ }
+ }
+ return null;
+ }
+
+ /** Handles a call from the server that the device state has changed. */
private void handleDeviceStateChanged(int newDeviceState) {
ArrayList<DeviceStateListenerWrapper> listeners;
synchronized (mLock) {
@@ -156,11 +247,68 @@
}
}
+ /**
+ * Handles a call from the server that a request for the supplied {@code token} has become
+ * active.
+ */
+ private void handleRequestActive(IBinder token) {
+ DeviceStateRequestWrapper request;
+ synchronized (mLock) {
+ request = mRequests.get(token);
+ }
+ if (request != null) {
+ request.notifyRequestActive();
+ }
+ }
+
+ /**
+ * Handles a call from the server that a request for the supplied {@code token} has become
+ * suspended.
+ */
+ private void handleRequestSuspended(IBinder token) {
+ DeviceStateRequestWrapper request;
+ synchronized (mLock) {
+ request = mRequests.get(token);
+ }
+ if (request != null) {
+ request.notifyRequestSuspended();
+ }
+ }
+
+ /**
+ * Handles a call from the server that a request for the supplied {@code token} has become
+ * canceled.
+ */
+ private void handleRequestCanceled(IBinder token) {
+ DeviceStateRequestWrapper request;
+ synchronized (mLock) {
+ request = mRequests.remove(token);
+ }
+ if (request != null) {
+ request.notifyRequestCanceled();
+ }
+ }
+
private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub {
@Override
public void onDeviceStateChanged(int deviceState) {
handleDeviceStateChanged(deviceState);
}
+
+ @Override
+ public void onRequestActive(IBinder token) {
+ handleRequestActive(token);
+ }
+
+ @Override
+ public void onRequestSuspended(IBinder token) {
+ handleRequestSuspended(token);
+ }
+
+ @Override
+ public void onRequestCanceled(IBinder token) {
+ handleRequestCanceled(token);
+ }
}
private static final class DeviceStateListenerWrapper {
@@ -176,4 +324,43 @@
mExecutor.execute(() -> mDeviceStateListener.onDeviceStateChanged(newDeviceState));
}
}
+
+ private static final class DeviceStateRequestWrapper {
+ private final DeviceStateRequest mRequest;
+ @Nullable
+ private final DeviceStateRequest.Callback mCallback;
+ @Nullable
+ private final Executor mExecutor;
+
+ DeviceStateRequestWrapper(@NonNull DeviceStateRequest request,
+ @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
+ mRequest = request;
+ mCallback = callback;
+ mExecutor = executor;
+ }
+
+ void notifyRequestActive() {
+ if (mCallback == null) {
+ return;
+ }
+
+ mExecutor.execute(() -> mCallback.onRequestActivated(mRequest));
+ }
+
+ void notifyRequestSuspended() {
+ if (mCallback == null) {
+ return;
+ }
+
+ mExecutor.execute(() -> mCallback.onRequestSuspended(mRequest));
+ }
+
+ void notifyRequestCanceled() {
+ if (mCallback == null) {
+ return;
+ }
+
+ mExecutor.execute(() -> mCallback.onRequestSuspended(mRequest));
+ }
+ }
}
diff --git a/core/java/android/hardware/devicestate/DeviceStateRequest.java b/core/java/android/hardware/devicestate/DeviceStateRequest.java
new file mode 100644
index 0000000..70f7002
--- /dev/null
+++ b/core/java/android/hardware/devicestate/DeviceStateRequest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.hardware.devicestate;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * A request to alter the state of the device managed by {@link DeviceStateManager}.
+ * <p>
+ * Once constructed, a {@link DeviceStateRequest request} can be submitted with a call to
+ * {@link DeviceStateManager#requestState(DeviceStateRequest, Executor,
+ * DeviceStateRequest.Callback)}.
+ * <p>
+ * By default, the request is kept active until a call to
+ * {@link DeviceStateManager#cancelRequest(DeviceStateRequest)} or until one of the following
+ * occurs:
+ * <ul>
+ * <li>Another processes submits a request succeeding this request in which case the request
+ * will be suspended until the interrupting request is canceled.
+ * <li>The requested state has become unsupported.
+ * <li>The process submitting the request dies.
+ * </ul>
+ * However, this behavior can be changed by setting flags on the request. For example, the
+ * {@link #FLAG_CANCEL_WHEN_BASE_CHANGES} flag will extend this behavior to also cancel the
+ * request whenever the base (non-override) device state changes.
+ *
+ * @see DeviceStateManager
+ *
+ * @hide
+ */
+@TestApi
+public final class DeviceStateRequest {
+ /**
+ * Flag that indicates the request should be canceled automatically when the base
+ * (non-override) device state changes. Useful when the requestor only wants the request to
+ * remain active while the base state remains constant and automatically cancel when the user
+ * manipulates the device into a different state.
+ */
+ public static final int FLAG_CANCEL_WHEN_BASE_CHANGES = 1 << 0;
+
+ /** @hide */
+ @IntDef(prefix = {"FLAG_"}, flag = true, value = {
+ FLAG_CANCEL_WHEN_BASE_CHANGES,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RequestFlags {}
+
+ /**
+ * Creates a new {@link Builder} for a {@link DeviceStateRequest}. Must be one of the supported
+ * states for the device which can be queried with a call to
+ * {@link DeviceStateManager#getSupportedStates()}.
+ *
+ * @param requestedState the device state being requested.
+ */
+ @NonNull
+ public static Builder newBuilder(int requestedState) {
+ return new Builder(requestedState);
+ }
+
+ /**
+ * Builder for {@link DeviceStateRequest}. An instance can be obtained through
+ * {@link #newBuilder(int)}.
+ */
+ public static final class Builder {
+ private final int mRequestedState;
+ private int mFlags;
+
+ private Builder(int requestedState) {
+ mRequestedState = requestedState;
+ }
+
+ /**
+ * Sets the flag bits provided within {@code flags} with all other bits remaining
+ * unchanged.
+ */
+ @NonNull
+ public Builder setFlags(@RequestFlags int flags) {
+ mFlags |= flags;
+ return this;
+ }
+
+ /**
+ * Returns a new {@link DeviceStateRequest} object whose state matches the state set on the
+ * builder.
+ */
+ @NonNull
+ public DeviceStateRequest build() {
+ return new DeviceStateRequest(mRequestedState, mFlags);
+ }
+ }
+
+ /** Callback to track the status of a request. */
+ public interface Callback {
+ /**
+ * Called to indicate the request has become active and the device state will match the
+ * requested state.
+ * <p>
+ * Guaranteed to be called after a call to
+ * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)} with a state
+ * matching the requested state.
+ */
+ default void onRequestActivated(@NonNull DeviceStateRequest request) {}
+
+ /**
+ * Called to indicate the request has been temporarily suspended.
+ * <p>
+ * Guaranteed to be called before a call to
+ * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}.
+ */
+ default void onRequestSuspended(@NonNull DeviceStateRequest request) {}
+
+ /**
+ * Called to indicate the request has been canceled. The request can be resubmitted with
+ * another call to {@link DeviceStateManager#requestState(DeviceStateRequest, Executor,
+ * DeviceStateRequest.Callback)}.
+ * <p>
+ * Guaranteed to be called before a call to
+ * {@link DeviceStateManager.DeviceStateListener#onDeviceStateChanged(int)}.
+ * <p>
+ * Note: A call to {@link #onRequestSuspended(DeviceStateRequest)} is not guaranteed to
+ * occur before this method.
+ */
+ default void onRequestCanceled(@NonNull DeviceStateRequest request) {}
+ }
+
+ private final int mRequestedState;
+ @RequestFlags
+ private final int mFlags;
+
+ private DeviceStateRequest(int requestedState, @RequestFlags int flags) {
+ mRequestedState = requestedState;
+ mFlags = flags;
+ }
+
+ public int getState() {
+ return mRequestedState;
+ }
+
+ @RequestFlags
+ public int getFlags() {
+ return mFlags;
+ }
+}
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
index a157b33..323ad21 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
@@ -20,5 +20,45 @@
/** @hide */
interface IDeviceStateManager {
+ /**
+ * Registers a callback to receive notifications from the device state manager. Only one
+ * callback can be registered per-process.
+ * <p>
+ * As the callback mechanism is used to alert the caller of changes to request status a callback
+ * <b>MUST</b> be registered before calling {@link #requestState(IBinder, int, int)} or
+ * {@link #cancelRequest(IBinder)}. Otherwise an exception will be thrown.
+ *
+ * @throws SecurityException if a callback is already registered for the calling process.
+ */
void registerCallback(in IDeviceStateManagerCallback callback);
+
+ /** Returns the array of supported device state identifiers. */
+ int[] getSupportedDeviceStates();
+
+ /**
+ * Requests that the device enter the supplied {@code state}. A callback <b>MUST</b> have been
+ * previously registered with {@link #registerCallback(IDeviceStateManagerCallback)} before a
+ * call to this method.
+ *
+ * @param token the request token previously registered with
+ * {@link #requestState(IBinder, int, int)}
+ *
+ * @throws IllegalStateException if a callback has not yet been registered for the calling
+ * process.
+ * @throws IllegalStateException if the supplied {@code token} has already been registered.
+ * @throws IllegalArgumentException if the supplied {@code state} is not supported.
+ */
+ void requestState(IBinder token, int state, int flags);
+
+ /**
+ * Cancels a request previously submitted with a call to
+ * {@link #requestState(IBinder, int, int)}.
+ *
+ * @param token the request token previously registered with
+ * {@link #requestState(IBinder, int, int)}
+ *
+ * @throws IllegalStateException if the supplied {@code token} has not been previously
+ * requested.
+ */
+ void cancelRequest(IBinder token);
}
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
index d1c5813..ee2a071 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManagerCallback.aidl
@@ -18,5 +18,42 @@
/** @hide */
interface IDeviceStateManagerCallback {
+ /**
+ * Called in response to a change in device state. Guaranteed to be called once with the initial
+ * value on registration of the callback.
+ *
+ * @param deviceState the new state of the device.
+ */
oneway void onDeviceStateChanged(int deviceState);
+
+ /**
+ * Called to notify the callback that a request has become active. Guaranteed to be called
+ * after a subsequent call to {@link #onDeviceStateChanged(int)} if the request becoming active
+ * resulted in a device state change.
+ *
+ * @param token the request token previously registered with
+ * {@link IDeviceStateManager#requestState(IBinder, int, int)}
+ */
+ oneway void onRequestActive(IBinder token);
+
+ /**
+ * Called to notify the callback that a request has become suspended. Guaranteed to be called
+ * before a subsequent call to {@link #onDeviceStateChanged(int)} if the request becoming
+ * suspended resulted in a device state change.
+ *
+ * @param token the request token previously registered with
+ * {@link IDeviceStateManager#requestState(IBinder, int, int)}
+ */
+ oneway void onRequestSuspended(IBinder token);
+
+ /**
+ * Called to notify the callback that a request has become canceled. No further callbacks will
+ * be triggered for this request. Guaranteed to be called before a subsequent call to
+ * {@link #onDeviceStateChanged(int)} if the request becoming canceled resulted in a device
+ * state change.
+ *
+ * @param token the request token previously registered with
+ * {@link IDeviceStateManager#requestState(IBinder, int, int)}
+ */
+ oneway void onRequestCanceled(IBinder token);
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 9bae1ff..bbf421d 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -854,8 +854,8 @@
*
* @hide Requires signature permission.
*/
- public void setTemporaryBrightness(float brightness) {
- mGlobal.setTemporaryBrightness(brightness);
+ public void setTemporaryBrightness(int displayId, float brightness) {
+ mGlobal.setTemporaryBrightness(displayId, brightness);
}
/**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 77ae947..60fe582 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -636,13 +636,13 @@
* Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
* </p>
*
- * @param brightness The brightness value from 0 to 255.
+ * @param brightness The brightness value from 0.0f to 1.0f.
*
* @hide Requires signature permission.
*/
- public void setTemporaryBrightness(float brightness) {
+ public void setTemporaryBrightness(int displayId, float brightness) {
try {
- mDm.setTemporaryBrightness(brightness);
+ mDm.setTemporaryBrightness(displayId, brightness);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index a9f78fa..ff8a720 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -116,7 +116,7 @@
boolean isMinimalPostProcessingRequested(int displayId);
// Temporarily sets the display brightness.
- void setTemporaryBrightness(float brightness);
+ void setTemporaryBrightness(int displayId, float brightness);
// Temporarily sets the auto brightness adjustment factor.
void setTemporaryAutoBrightnessAdjustment(float adjustment);
diff --git a/tools/hiddenapi/Android.bp b/core/java/android/hardware/face/FaceAuthenticationFrame.aidl
similarity index 68%
copy from tools/hiddenapi/Android.bp
copy to core/java/android/hardware/face/FaceAuthenticationFrame.aidl
index e0eb06cb..4dc41f1 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/FaceAuthenticationFrame.java b/core/java/android/hardware/face/FaceAuthenticationFrame.java
new file mode 100644
index 0000000..f39d634
--- /dev/null
+++ b/core/java/android/hardware/face/FaceAuthenticationFrame.java
@@ -0,0 +1,75 @@
+/*
+ * 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.hardware.face;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data model for a frame captured during face authentication.
+ *
+ * @hide
+ */
+public final class FaceAuthenticationFrame implements Parcelable {
+ @NonNull private final FaceDataFrame mData;
+
+ /**
+ * Data model for a frame captured during face authentication.
+ *
+ * @param data Information about the current frame.
+ */
+ public FaceAuthenticationFrame(@NonNull FaceDataFrame data) {
+ mData = data;
+ }
+
+ /**
+ * @return Information about the current frame.
+ */
+ @NonNull
+ public FaceDataFrame getData() {
+ return mData;
+ }
+
+ private FaceAuthenticationFrame(@NonNull Parcel source) {
+ mData = source.readParcelable(FaceDataFrame.class.getClassLoader());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mData, flags);
+ }
+
+ public static final Creator<FaceAuthenticationFrame> CREATOR =
+ new Creator<FaceAuthenticationFrame>() {
+
+ @Override
+ public FaceAuthenticationFrame createFromParcel(Parcel source) {
+ return new FaceAuthenticationFrame(source);
+ }
+
+ @Override
+ public FaceAuthenticationFrame[] newArray(int size) {
+ return new FaceAuthenticationFrame[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/face/FaceDataFrame.java b/core/java/android/hardware/face/FaceDataFrame.java
new file mode 100644
index 0000000..092359c
--- /dev/null
+++ b/core/java/android/hardware/face/FaceDataFrame.java
@@ -0,0 +1,167 @@
+/*
+ * 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.hardware.face;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A container for data common to {@link FaceAuthenticationFrame} and {@link FaceEnrollFrame}.
+ *
+ * @hide
+ */
+public final class FaceDataFrame implements Parcelable {
+ private final int mAcquiredInfo;
+ private final int mVendorCode;
+ private final float mPan;
+ private final float mTilt;
+ private final float mDistance;
+ private final boolean mIsCancellable;
+
+ /**
+ * 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}.
+ * @param pan The horizontal pan of the detected face. Values in the range [-1, 1] indicate a
+ * good capture.
+ * @param tilt The vertical tilt of the detected face. Values in the range [-1, 1] indicate a
+ * good capture.
+ * @param distance The distance of the detected face from the device. Values in the range
+ * [-1, 1] indicate a good capture.
+ * @param isCancellable Whether the ongoing face operation should be canceled.
+ */
+ public FaceDataFrame(
+ int acquiredInfo,
+ int vendorCode,
+ float pan,
+ float tilt,
+ float distance,
+ boolean isCancellable) {
+ mAcquiredInfo = acquiredInfo;
+ mVendorCode = vendorCode;
+ mPan = pan;
+ mTilt = tilt;
+ mDistance = distance;
+ mIsCancellable = isCancellable;
+ }
+
+ /**
+ * 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
+ */
+ public int getAcquiredInfo() {
+ return mAcquiredInfo;
+ }
+
+ /**
+ * @return An integer representing a custom vendor-specific message. Ignored unless
+ * {@code acquiredInfo} is {@link
+ * android.hardware.biometrics.BiometricFaceConstants#FACE_ACQUIRED_VENDOR}.
+ *
+ * @see android.hardware.biometrics.BiometricFaceConstants
+ */
+ public int getVendorCode() {
+ return mVendorCode;
+ }
+
+ /**
+ * @return The horizontal pan of the detected face. Values in the range [-1, 1] indicate a good
+ * capture.
+ */
+ public float getPan() {
+ return mPan;
+ }
+
+ /**
+ * @return The vertical tilt of the detected face. Values in the range [-1, 1] indicate a good
+ * capture.
+ */
+ public float getTilt() {
+ return mTilt;
+ }
+
+ /**
+ * @return The distance of the detected face from the device. Values in the range [-1, 1]
+ * indicate a good capture.
+ */
+ public float getDistance() {
+ return mDistance;
+ }
+
+ /**
+ * @return Whether the ongoing face operation should be canceled.
+ */
+ public boolean isCancellable() {
+ return mIsCancellable;
+ }
+
+ private FaceDataFrame(@NonNull Parcel source) {
+ mAcquiredInfo = source.readInt();
+ mVendorCode = source.readInt();
+ mPan = source.readFloat();
+ mTilt = source.readFloat();
+ mDistance = source.readFloat();
+ mIsCancellable = source.readBoolean();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mAcquiredInfo);
+ dest.writeInt(mVendorCode);
+ dest.writeFloat(mPan);
+ dest.writeFloat(mTilt);
+ dest.writeFloat(mDistance);
+ dest.writeBoolean(mIsCancellable);
+ }
+
+ public static final Creator<FaceDataFrame> CREATOR = new Creator<FaceDataFrame>() {
+ @Override
+ public FaceDataFrame createFromParcel(Parcel source) {
+ return new FaceDataFrame(source);
+ }
+
+ @Override
+ public FaceDataFrame[] newArray(int size) {
+ return new FaceDataFrame[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/face/FaceEnrollCell.java b/core/java/android/hardware/face/FaceEnrollCell.java
new file mode 100644
index 0000000..8415419
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollCell.java
@@ -0,0 +1,96 @@
+/*
+ * 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.hardware.face;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A matrix cell, corresponding to a desired face image, that may be captured during enrollment.
+ *
+ * @hide
+ */
+public final class FaceEnrollCell implements Parcelable {
+ private final int mX;
+ private final int mY;
+ private final int mZ;
+
+ /**
+ * A matrix cell, corresponding to a desired face image, that may be captured during enrollment.
+ *
+ * @param x The horizontal coordinate of this cell.
+ * @param y The vertical coordinate of this cell.
+ * @param z The depth coordinate of this cell.
+ */
+ public FaceEnrollCell(int x, int y, int z) {
+ mX = x;
+ mY = y;
+ mZ = z;
+ }
+
+ /**
+ * @return The horizontal coordinate of this cell.
+ */
+ public int getX() {
+ return mX;
+ }
+
+ /**
+ * @return The vertical coordinate of this cell.
+ */
+ public int getY() {
+ return mY;
+ }
+
+ /**
+ * @return The depth coordinate of this cell.
+ */
+ public int getZ() {
+ return mZ;
+ }
+
+ private FaceEnrollCell(@NonNull Parcel source) {
+ mX = source.readInt();
+ mY = source.readInt();
+ mZ = source.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mX);
+ dest.writeInt(mY);
+ dest.writeInt(mZ);
+ }
+
+ public static final Creator<FaceEnrollCell> CREATOR = new Creator<FaceEnrollCell>() {
+ @Override
+ public FaceEnrollCell createFromParcel(Parcel source) {
+ return new FaceEnrollCell(source);
+ }
+
+ @Override
+ public FaceEnrollCell[] newArray(int size) {
+ return new FaceEnrollCell[size];
+ }
+ };
+}
diff --git a/tools/hiddenapi/Android.bp b/core/java/android/hardware/face/FaceEnrollFrame.aidl
similarity index 68%
copy from tools/hiddenapi/Android.bp
copy to core/java/android/hardware/face/FaceEnrollFrame.aidl
index e0eb06cb..b854681 100644
--- a/tools/hiddenapi/Android.bp
+++ b/core/java/android/hardware/face/FaceEnrollFrame.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 FaceEnrollFrame;
diff --git a/core/java/android/hardware/face/FaceEnrollFrame.java b/core/java/android/hardware/face/FaceEnrollFrame.java
new file mode 100644
index 0000000..551139d
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollFrame.java
@@ -0,0 +1,103 @@
+/*
+ * 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.hardware.face;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data model for a frame captured during face enrollment.
+ *
+ * @hide
+ */
+public final class FaceEnrollFrame implements Parcelable {
+ @Nullable private final FaceEnrollCell mCell;
+ @FaceEnrollStage private final int mStage;
+ @NonNull private final FaceDataFrame mData;
+
+ /**
+ * Data model for a frame captured during face enrollment.
+ *
+ * @param cell The cell captured during this frame of enrollment, if any.
+ * @param stage An integer representing the current stage of enrollment.
+ * @param data Information about the current frame.
+ */
+ public FaceEnrollFrame(
+ @Nullable FaceEnrollCell cell,
+ @FaceEnrollStage int stage,
+ @NonNull FaceDataFrame data) {
+ mCell = cell;
+ mStage = stage;
+ mData = data;
+ }
+
+ /**
+ * @return The cell captured during this frame of enrollment, if any.
+ */
+ @Nullable
+ public FaceEnrollCell getCell() {
+ return mCell;
+ }
+
+ /**
+ * @return An integer representing the current stage of enrollment.
+ */
+ @FaceEnrollStage
+ public int getStage() {
+ return mStage;
+ }
+
+ /**
+ * @return Information about the current frame.
+ */
+ @NonNull
+ public FaceDataFrame getData() {
+ return mData;
+ }
+
+ private FaceEnrollFrame(@NonNull Parcel source) {
+ mCell = source.readParcelable(FaceEnrollCell.class.getClassLoader());
+ mStage = source.readInt();
+ mData = source.readParcelable(FaceDataFrame.class.getClassLoader());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mCell, flags);
+ dest.writeInt(mStage);
+ dest.writeParcelable(mData, flags);
+ }
+
+ public static final Creator<FaceEnrollFrame> CREATOR = new Creator<FaceEnrollFrame>() {
+ @Override
+ public FaceEnrollFrame createFromParcel(Parcel source) {
+ return new FaceEnrollFrame(source);
+ }
+
+ @Override
+ public FaceEnrollFrame[] newArray(int size) {
+ return new FaceEnrollFrame[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/face/FaceEnrollStage.java b/core/java/android/hardware/face/FaceEnrollStage.java
new file mode 100644
index 0000000..de717fb
--- /dev/null
+++ b/core/java/android/hardware/face/FaceEnrollStage.java
@@ -0,0 +1,74 @@
+/*
+ * 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.hardware.face;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A stage that may occur during face enrollment.
+ *
+ * @hide
+ */
+@Retention(RetentionPolicy.SOURCE)
+@IntDef({
+ FaceEnrollStage.UNKNOWN,
+ FaceEnrollStage.FIRST_FRAME_RECEIVED,
+ FaceEnrollStage.WAITING_FOR_CENTERING,
+ FaceEnrollStage.HOLD_STILL_IN_CENTER,
+ FaceEnrollStage.ENROLLING_MOVEMENT_1,
+ FaceEnrollStage.ENROLLING_MOVEMENT_2,
+ FaceEnrollStage.ENROLLMENT_FINISHED
+})
+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;
+
+ /**
+ * The user must center their face in the frame.
+ */
+ int WAITING_FOR_CENTERING = 1;
+
+ /**
+ * The user must keep their face centered in the frame.
+ */
+ int HOLD_STILL_IN_CENTER = 2;
+
+ /**
+ * The user must follow a first set of movement instructions.
+ */
+ int ENROLLING_MOVEMENT_1 = 3;
+
+ /**
+ * The user must follow a second set of movement instructions.
+ */
+ int ENROLLING_MOVEMENT_2 = 4;
+
+ /**
+ * Enrollment has completed. No more action is needed from the user.
+ */
+ int ENROLLMENT_FINISHED = 5;
+}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 588bc01..f3da6a9 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -71,17 +71,19 @@
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 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 @@
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 @@
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 bd4d3a0..2ef1430 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 @@
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/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index d932865..188a2a4 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -93,17 +93,26 @@
private static final int MSG_UDFPS_POINTER_UP = 109;
/**
- * Request authentication with any single sensor.
* @hide
*/
- public static final int SENSOR_ID_ANY = -1;
+ public static final int ENROLL_FIND_SENSOR = 1;
+ /**
+ * @hide
+ */
+ public static final int ENROLL_ENROLL = 2;
/**
* @hide
*/
- @IntDef({SENSOR_ID_ANY})
+ @IntDef({ENROLL_FIND_SENSOR, ENROLL_ENROLL})
@Retention(RetentionPolicy.SOURCE)
- public @interface SensorId {}
+ public @interface EnrollReason {}
+
+ /**
+ * Request authentication with any single sensor.
+ * @hide
+ */
+ public static final int SENSOR_ID_ANY = -1;
private IFingerprintService mService;
private Context mContext;
@@ -508,8 +517,8 @@
*/
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
- @NonNull AuthenticationCallback callback, Handler handler, @SensorId int sensorId,
- int userId) {
+ @NonNull AuthenticationCallback callback, Handler handler, int sensorId, int userId) {
+
if (callback == null) {
throw new IllegalArgumentException("Must supply an authentication callback");
}
@@ -590,7 +599,7 @@
*/
@RequiresPermission(MANAGE_FINGERPRINT)
public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId,
- EnrollmentCallback callback, boolean shouldLogMetrics) {
+ EnrollmentCallback callback, @EnrollReason int enrollReason) {
if (userId == UserHandle.USER_CURRENT) {
userId = getCurrentUserId();
}
@@ -611,7 +620,7 @@
try {
mEnrollmentCallback = callback;
mService.enroll(mToken, hardwareAuthToken, userId, mServiceReceiver,
- mContext.getOpPackageName(), shouldLogMetrics);
+ mContext.getOpPackageName(), enrollReason);
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception in enroll: ", e);
// Though this may not be a hardware issue, it will cause apps to give up or try
@@ -653,15 +662,12 @@
*/
@RequiresPermission(MANAGE_FINGERPRINT)
public void generateChallenge(int userId, GenerateChallengeCallback callback) {
- final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties =
- getSensorPropertiesInternal();
- if (fingerprintSensorProperties.isEmpty()) {
+ final FingerprintSensorPropertiesInternal sensorProps = getFirstFingerprintSensor();
+ if (sensorProps == null) {
Slog.e(TAG, "No sensors");
return;
}
-
- final int sensorId = fingerprintSensorProperties.get(0).sensorId;
- generateChallenge(sensorId, userId, callback);
+ generateChallenge(sensorProps.sensorId, userId, callback);
}
/**
@@ -681,18 +687,18 @@
*/
@RequiresPermission(MANAGE_FINGERPRINT)
public void revokeChallenge(int userId, long challenge) {
- if (mService != null) try {
- final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties =
- getSensorPropertiesInternal();
- if (fingerprintSensorProperties.isEmpty()) {
- Slog.e(TAG, "No sensors");
- return;
+ if (mService != null) {
+ try {
+ final FingerprintSensorPropertiesInternal sensorProps = getFirstFingerprintSensor();
+ if (sensorProps == null) {
+ Slog.e(TAG, "No sensors");
+ return;
+ }
+ mService.revokeChallenge(mToken, sensorProps.sensorId, userId,
+ mContext.getOpPackageName(), challenge);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- final int sensorId = fingerprintSensorProperties.get(0).sensorId;
- mService.revokeChallenge(mToken, sensorId, userId, mContext.getOpPackageName(),
- challenge);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
}
}
@@ -1161,6 +1167,12 @@
}
}
+ @Nullable
+ private FingerprintSensorPropertiesInternal getFirstFingerprintSensor() {
+ final List<FingerprintSensorPropertiesInternal> allSensors = getSensorPropertiesInternal();
+ return allSensors.isEmpty() ? null : allSensors.get(0);
+ }
+
private void cancelEnrollment() {
if (mService != null) try {
mService.cancelEnrollment(mToken);
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
index f097651..663a704 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorPropertiesInternal.java
@@ -109,9 +109,10 @@
this.sensorLocationY = props[1];
this.sensorRadius = props[2];
} else {
- this.sensorLocationX = 0;
- this.sensorLocationY = 0;
- this.sensorRadius = 0;
+ // Fake coordinates that could be used for the fake UDFPS mode.
+ this.sensorLocationX = 540;
+ this.sensorLocationY = 1636;
+ this.sensorRadius = 130;
}
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 3657a83..8888247 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -78,7 +78,7 @@
// Start fingerprint enrollment
void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
- String opPackageName, boolean shouldLogMetrics);
+ String opPackageName, int enrollReason);
// Cancel enrollment in progress
void cancelEnrollment(IBinder token);
diff --git a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
index c093489..81c7d89 100644
--- a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
+++ b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
@@ -21,10 +21,11 @@
*/
oneway interface IUdfpsOverlayController {
const int REASON_UNKNOWN = 0;
- const int REASON_ENROLL = 1;
- const int REASON_AUTH_BP = 2; // BiometricPrompt
- const int REASON_AUTH_FPM_KEYGUARD = 3; // FingerprintManager usage from Keyguard
- const int REASON_AUTH_FPM_OTHER = 4; // Other FingerprintManager usage
+ const int REASON_ENROLL_FIND_SENSOR = 1;
+ const int REASON_ENROLL_ENROLLING = 2;
+ const int REASON_AUTH_BP = 3; // BiometricPrompt
+ const int REASON_AUTH_FPM_KEYGUARD = 4; // FingerprintManager usage from Keyguard
+ const int REASON_AUTH_FPM_OTHER = 5; // Other FingerprintManager usage
// Shows the overlay.
void showUdfpsOverlay(int sensorId, int reason);
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index c69c47f..eaa38f3 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -26,6 +26,7 @@
import android.hardware.input.IInputSensorEventListener;
import android.hardware.input.InputSensorInfo;
import android.os.IBinder;
+import android.os.IVibratorStateListener;
import android.os.VibrationEffect;
import android.view.InputDevice;
import android.view.InputEvent;
@@ -92,6 +93,8 @@
void cancelVibrate(int deviceId, IBinder token);
int[] getVibratorIds(int deviceId);
boolean isVibrating(int deviceId);
+ boolean registerVibratorStateListener(int deviceId, in IVibratorStateListener listener);
+ boolean unregisterVibratorStateListener(int deviceId, in IVibratorStateListener listener);
// Input device battery query.
int getBatteryStatus(int deviceId);
diff --git a/core/java/android/hardware/input/InputDeviceVibrator.java b/core/java/android/hardware/input/InputDeviceVibrator.java
index c60d6ce..f4d8a65 100644
--- a/core/java/android/hardware/input/InputDeviceVibrator.java
+++ b/core/java/android/hardware/input/InputDeviceVibrator.java
@@ -18,10 +18,18 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
+import android.app.ActivityThread;
+import android.content.Context;
import android.os.Binder;
+import android.os.IVibratorStateListener;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
import java.util.concurrent.Executor;
@@ -29,12 +37,18 @@
* Vibrator implementation that communicates with the input device vibrators.
*/
final class InputDeviceVibrator extends Vibrator {
+ private static final String TAG = "InputDeviceVibrator";
+
// mDeviceId represents InputDevice ID the vibrator belongs to
private final int mDeviceId;
private final int mVibratorId;
private final Binder mToken;
private final InputManager mInputManager;
+ @GuardedBy("mDelegates")
+ private final ArrayMap<OnVibratorStateChangedListener,
+ OnVibratorStateChangedListenerDelegate> mDelegates = new ArrayMap<>();
+
InputDeviceVibrator(InputManager inputManager, int deviceId, int vibratorId) {
mInputManager = inputManager;
mDeviceId = deviceId;
@@ -42,6 +56,23 @@
mToken = new Binder();
}
+ private class OnVibratorStateChangedListenerDelegate extends
+ IVibratorStateListener.Stub {
+ private final Executor mExecutor;
+ private final OnVibratorStateChangedListener mListener;
+
+ OnVibratorStateChangedListenerDelegate(@NonNull OnVibratorStateChangedListener listener,
+ @NonNull Executor executor) {
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ @Override
+ public void onVibrating(boolean isVibrating) {
+ mExecutor.execute(() -> mListener.onVibratorStateChanged(isVibrating));
+ }
+ }
+
@Override
public boolean hasVibrator() {
return true;
@@ -52,25 +83,73 @@
return mInputManager.isVibrating(mDeviceId);
}
- /* TODO: b/161634264 Support Vibrator listener API in input devices */
+ /**
+ * Adds a listener for vibrator state changes. Callbacks will be executed on the main thread.
+ * If the listener was previously added and not removed, this call will be ignored.
+ *
+ * @param listener listener to be added
+ */
@Override
public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
- throw new UnsupportedOperationException(
- "addVibratorStateListener not supported in InputDeviceVibrator");
+ Preconditions.checkNotNull(listener);
+ Context context = ActivityThread.currentApplication();
+ addVibratorStateListener(context.getMainExecutor(), listener);
}
+ /**
+ * Adds a listener for vibrator state change. If the listener was previously added and not
+ * removed, this call will be ignored.
+ *
+ * @param listener Listener to be added.
+ * @param executor The {@link Executor} on which the listener's callbacks will be executed on.
+ */
@Override
public void addVibratorStateListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull OnVibratorStateChangedListener listener) {
- throw new UnsupportedOperationException(
- "addVibratorStateListener not supported in InputDeviceVibrator");
+ Preconditions.checkNotNull(listener);
+ Preconditions.checkNotNull(executor);
+
+ synchronized (mDelegates) {
+ // If listener is already registered, reject and return.
+ if (mDelegates.containsKey(listener)) {
+ Log.w(TAG, "Listener already registered.");
+ return;
+ }
+
+ final OnVibratorStateChangedListenerDelegate delegate =
+ new OnVibratorStateChangedListenerDelegate(listener, executor);
+ if (!mInputManager.registerVibratorStateListener(mDeviceId, delegate)) {
+ Log.w(TAG, "Failed to register vibrate state listener");
+ return;
+ }
+ mDelegates.put(listener, delegate);
+
+ }
}
+ /**
+ * Removes the listener for vibrator state changes. If the listener was not previously
+ * registered, this call will do nothing.
+ *
+ * @param listener Listener to be removed.
+ */
@Override
public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) {
- throw new UnsupportedOperationException(
- "removeVibratorStateListener not supported in InputDeviceVibrator");
+ Preconditions.checkNotNull(listener);
+
+ synchronized (mDelegates) {
+ // Check if the listener is registered, otherwise will return.
+ if (mDelegates.containsKey(listener)) {
+ final OnVibratorStateChangedListenerDelegate delegate = mDelegates.get(listener);
+
+ if (!mInputManager.unregisterVibratorStateListener(mDeviceId, delegate)) {
+ Log.w(TAG, "Failed to unregister vibrate state listener");
+ return;
+ }
+ mDelegates.remove(listener);
+ }
+ }
}
@Override
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 185c59d..8a01c66 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -35,6 +35,7 @@
import android.os.CombinedVibrationEffect;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IVibratorStateListener;
import android.os.InputEventInjectionSync;
import android.os.Looper;
import android.os.Message;
@@ -1484,6 +1485,32 @@
}
/**
+ * Register input device vibrator state listener
+ *
+ * @hide
+ */
+ public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+ try {
+ return mIm.registerVibratorStateListener(deviceId, listener);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregister input device vibrator state listener
+ *
+ * @hide
+ */
+ public boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+ try {
+ return mIm.unregisterVibratorStateListener(deviceId, listener);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets a sensor manager service associated with an input device, always create a new instance.
* @return The sensor manager, never null.
* @hide
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 44a2e97..7e2be01 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1732,7 +1732,7 @@
// If app window has portrait orientation, regardless of what display orientation
// is, IME shouldn't use fullscreen-mode.
|| (mInputEditorInfo.internalImeOptions
- & EditorInfo.IME_FLAG_APP_WINDOW_PORTRAIT) != 0) {
+ & EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT) != 0) {
return false;
}
return true;
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 5d8122b..32b19a4 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -32,7 +32,7 @@
/**
* Network definition that includes strong identity. Analogous to combining
- * {@link NetworkInfo} and an IMSI.
+ * {@link NetworkCapabilities} and an IMSI.
*
* @hide
*/
@@ -160,7 +160,7 @@
*/
public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state,
boolean defaultNetwork, @NetworkType int subType) {
- final int type = state.networkInfo.getType();
+ final int legacyType = state.legacyNetworkType;
String subscriberId = null;
String networkId = null;
@@ -171,7 +171,7 @@
subscriberId = state.subscriberId;
- if (type == TYPE_WIFI) {
+ if (legacyType == TYPE_WIFI) {
if (state.networkCapabilities.getSsid() != null) {
networkId = state.networkCapabilities.getSsid();
if (networkId == null) {
@@ -184,7 +184,7 @@
}
}
- return new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered,
+ return new NetworkIdentity(legacyType, subType, subscriberId, networkId, roaming, metered,
defaultNetwork);
}
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index e1ef8b5..e466d2e 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -41,6 +42,7 @@
public final Network network;
public final String subscriberId;
public final String networkId;
+ public final int legacyNetworkType;
private NetworkState() {
networkInfo = null;
@@ -49,17 +51,35 @@
network = null;
subscriberId = null;
networkId = null;
+ legacyNetworkType = 0;
}
+ public NetworkState(int legacyNetworkType, @NonNull LinkProperties linkProperties,
+ @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network,
+ @Nullable String subscriberId, @Nullable String networkId) {
+ this(legacyNetworkType, new NetworkInfo(legacyNetworkType, 0, null, null), linkProperties,
+ networkCapabilities, network, subscriberId, networkId);
+ }
+
+ // Constructor that used internally in ConnectivityService mainline module.
public NetworkState(@NonNull NetworkInfo networkInfo, @NonNull LinkProperties linkProperties,
@NonNull NetworkCapabilities networkCapabilities, @NonNull Network network,
String subscriberId, String networkId) {
+ this(networkInfo.getType(), networkInfo, linkProperties,
+ networkCapabilities, network, subscriberId, networkId);
+ }
+
+ public NetworkState(int legacyNetworkType, @NonNull NetworkInfo networkInfo,
+ @NonNull LinkProperties linkProperties,
+ @NonNull NetworkCapabilities networkCapabilities, @NonNull Network network,
+ String subscriberId, String networkId) {
this.networkInfo = networkInfo;
this.linkProperties = linkProperties;
this.networkCapabilities = networkCapabilities;
this.network = network;
this.subscriberId = subscriberId;
this.networkId = networkId;
+ this.legacyNetworkType = legacyNetworkType;
// This object is an atomic view of a network, so the various components
// should always agree on roaming state.
@@ -80,6 +100,7 @@
network = in.readParcelable(null);
subscriberId = in.readString();
networkId = in.readString();
+ legacyNetworkType = in.readInt();
}
@Override
@@ -95,6 +116,7 @@
out.writeParcelable(network, flags);
out.writeString(subscriberId);
out.writeString(networkId);
+ out.writeInt(legacyNetworkType);
}
@UnsupportedAppUsage
diff --git a/core/java/android/net/OemNetworkPreferences.java b/core/java/android/net/OemNetworkPreferences.java
index 6a8e3f9..5e56164 100644
--- a/core/java/android/net/OemNetworkPreferences.java
+++ b/core/java/android/net/OemNetworkPreferences.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.
@@ -18,14 +18,14 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.os.Bundle;
import android.os.Parcelable;
-import android.util.SparseArray;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
import java.util.Collections;
-import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
/** @hide */
@@ -60,16 +60,16 @@
public static final int OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY = 4;
@NonNull
- private final SparseArray<List<String>> mNetworkMappings;
+ private final Bundle mNetworkMappings;
@NonNull
- public SparseArray<List<String>> getNetworkPreferences() {
- return mNetworkMappings.clone();
+ public Map<String, Integer> getNetworkPreferences() {
+ return convertToUnmodifiableMap(mNetworkMappings);
}
- private OemNetworkPreferences(@NonNull SparseArray<List<String>> networkMappings) {
+ private OemNetworkPreferences(@NonNull final Bundle networkMappings) {
Objects.requireNonNull(networkMappings);
- mNetworkMappings = networkMappings.clone();
+ mNetworkMappings = (Bundle) networkMappings.clone();
}
@Override
@@ -99,26 +99,45 @@
* @hide
*/
public static final class Builder {
- private final SparseArray<List<String>> mNetworkMappings;
+ private final Bundle mNetworkMappings;
public Builder() {
- mNetworkMappings = new SparseArray<>();
+ mNetworkMappings = new Bundle();
+ }
+
+ public Builder(@NonNull final OemNetworkPreferences preferences) {
+ Objects.requireNonNull(preferences);
+ mNetworkMappings = (Bundle) preferences.mNetworkMappings.clone();
}
/**
- * Add a network preference for a list of packages.
+ * Add a network preference for a given package. Previously stored values for the given
+ * package will be overwritten.
*
- * @param preference the desired network preference to use
- * @param packages full package names (e.g.: "com.google.apps.contacts") for apps to use
- * the given preference
+ * @param packageName full package name (e.g.: "com.google.apps.contacts") of the app
+ * to use the given preference
+ * @param preference the desired network preference to use
* @return The builder to facilitate chaining.
*/
@NonNull
- public Builder addNetworkPreference(@OemNetworkPreference final int preference,
- @NonNull List<String> packages) {
- Objects.requireNonNull(packages);
- mNetworkMappings.put(preference,
- Collections.unmodifiableList(new ArrayList<>(packages)));
+ public Builder addNetworkPreference(@NonNull final String packageName,
+ @OemNetworkPreference final int preference) {
+ Objects.requireNonNull(packageName);
+ mNetworkMappings.putInt(packageName, preference);
+ return this;
+ }
+
+ /**
+ * Remove a network preference for a given package.
+ *
+ * @param packageName full package name (e.g.: "com.google.apps.contacts") of the app to
+ * remove a preference for.
+ * @return The builder to facilitate chaining.
+ */
+ @NonNull
+ public Builder removeNetworkPreference(@NonNull final String packageName) {
+ Objects.requireNonNull(packageName);
+ mNetworkMappings.remove(packageName);
return this;
}
@@ -131,6 +150,14 @@
}
}
+ private static Map<String, Integer> convertToUnmodifiableMap(@NonNull final Bundle bundle) {
+ final Map<String, Integer> networkPreferences = new HashMap<>();
+ for (final String key : bundle.keySet()) {
+ networkPreferences.put(key, bundle.getInt(key));
+ }
+ return Collections.unmodifiableMap(networkPreferences);
+ }
+
/** @hide */
@IntDef(prefix = "OEM_NETWORK_PREFERENCE_", value = {
OEM_NETWORK_PREFERENCE_DEFAULT,
@@ -168,7 +195,7 @@
@Override
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
- dest.writeSparseArray(mNetworkMappings);
+ dest.writeBundle(mNetworkMappings);
}
@Override
@@ -187,7 +214,7 @@
@Override
public OemNetworkPreferences createFromParcel(@NonNull android.os.Parcel in) {
return new OemNetworkPreferences(
- in.readSparseArray(getClass().getClassLoader()));
+ in.readBundle(getClass().getClassLoader()));
}
};
}
diff --git a/core/java/android/net/VpnTransportInfo.java b/core/java/android/net/VpnTransportInfo.java
new file mode 100644
index 0000000..082fa58
--- /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 fa090f5..1a38338 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -28,8 +28,10 @@
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 @@
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 @@
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 af8e8de..305815f 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -33,20 +33,33 @@
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 @@
return mSystemBatteryConsumers;
}
+ @NonNull
+ public List<UserBatteryConsumer> getUserBatteryConsumers() {
+ return mUserBatteryConsumers;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -85,6 +103,8 @@
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 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 @@
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 @@
/**
* Sets the battery discharge amount since BatteryStats reset as percentage of the full
* charge.
- *
*/
@SuppressLint("PercentageInt") // See b/174188159
@NonNull
@@ -173,8 +195,8 @@
}
/**
- * 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 @@
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 48e7389..5b5fe1d 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -18,6 +18,7 @@
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 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 @@
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 static final class Builder {
private int mFlags;
+ private IntArray mUserIds;
/**
* Builds a read-only BatteryUsageStatsQuery object.
@@ -105,6 +126,18 @@
}
/**
+ * 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 9584bc7..6a76da2 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -2557,6 +2557,14 @@
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 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 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 518e29d..6295124 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -18,6 +18,7 @@
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 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 @@
* <li>{@link #DIRECTORY_DCIM}
* <li>{@link #DIRECTORY_DOCUMENTS}
* <li>{@link #DIRECTORY_AUDIOBOOKS}
+ * <li>{@link #DIRECTORY_RECORDINGS}
* </ul>
* @hide
*/
@@ -866,6 +883,7 @@
DIRECTORY_DCIM,
DIRECTORY_DOCUMENTS,
DIRECTORY_AUDIOBOOKS,
+ DIRECTORY_RECORDINGS,
};
/**
@@ -891,6 +909,7 @@
/** {@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 @@
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 39e3e14..8068c87 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -1442,7 +1442,7 @@
*
* @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 @@
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 3ffaa9e..d1ca6a5 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -96,6 +96,7 @@
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 @@
}
/**
- * 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 @@
}
/**
- * 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 0000000..94e567f
--- /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/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 7e50ebc..2119e7b 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -326,6 +326,19 @@
}
/**
+ * Returns the uid that is composed from the userHandle and the appId.
+ *
+ * @param userHandle the UserHandle to compose the uid
+ * @param appId the AppId to compose the uid
+ * @return the uid that is composed from the userHandle and the appId
+ * @hide
+ */
+ @SystemApi
+ public static int getUid(@NonNull UserHandle userHandle, @AppIdInt int appId) {
+ return getUid(userHandle.getIdentifier(), appId);
+ }
+
+ /**
* Returns the app id (or base uid) for a given uid, stripping out the user id from it.
* @hide
*/
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 7e7057f..0ff68fc 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -304,20 +304,16 @@
}
/**
- * Called when a callback wants to stop listen to the loading progress of an installed package.
- * Decrease the count of the callbacks on the associated to the corresponding storage.
- * If the count becomes zero, unregister the storage listener.
+ * Called to stop all listeners from listening to loading progress of an installed package.
* @param codePath Path of the installed package
- * @return True if the package name and associated storage id are valid. False otherwise.
*/
- public boolean unregisterLoadingProgressCallback(@NonNull String codePath,
- @NonNull IPackageLoadingProgressCallback callback) {
+ public void unregisterLoadingProgressCallbacks(@NonNull String codePath) {
final IncrementalStorage storage = openStorage(codePath);
if (storage == null) {
// storage does not exist, package not installed
- return false;
+ return;
}
- return mLoadingProgressCallbacks.unregisterCallback(storage, callback);
+ mLoadingProgressCallbacks.cleanUpCallbacks(storage);
}
private static class LoadingProgressCallbacks extends IStorageLoadingProgressListener.Stub {
@@ -325,7 +321,6 @@
private final SparseArray<RemoteCallbackList<IPackageLoadingProgressCallback>> mCallbacks =
new SparseArray<>();
- // TODO(b/165841827): disable callbacks when app state changes to fully loaded
public void cleanUpCallbacks(@NonNull IncrementalStorage storage) {
final int storageId = storage.getId();
final RemoteCallbackList<IPackageLoadingProgressCallback> callbacksForStorage;
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index faa90d9..86b20c4 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -350,6 +350,9 @@
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 9603f4d..ef81ed7 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -44,6 +44,7 @@
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.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.Map;
import java.util.Objects;
import java.util.Set;
-
/**
* The Settings provider contains global system-level device preferences.
*/
@@ -2490,7 +2493,7 @@
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 @@
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 @@
@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 @@
}
}
+ 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 @@
}
/**
+ * 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 @@
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 @@
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 @@
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 @@
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 @@
+ " 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 @@
* 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 @@
* Is advanced settings mode turned on. 0 == no, 1 == yes
* @hide
*/
+ @Readable
public static final String ADVANCED_SETTINGS = "advanced_settings";
/**
@@ -3835,6 +3923,7 @@
* @deprecated Use {@link WifiManager} instead
*/
@Deprecated
+ @Readable
public static final String WIFI_USE_STATIC_IP = "wifi_use_static_ip";
/**
@@ -3845,6 +3934,7 @@
* @deprecated Use {@link WifiManager} instead
*/
@Deprecated
+ @Readable
public static final String WIFI_STATIC_IP = "wifi_static_ip";
/**
@@ -3855,6 +3945,7 @@
* @deprecated Use {@link WifiManager} instead
*/
@Deprecated
+ @Readable
public static final String WIFI_STATIC_GATEWAY = "wifi_static_gateway";
/**
@@ -3865,6 +3956,7 @@
* @deprecated Use {@link WifiManager} instead
*/
@Deprecated
+ @Readable
public static final String WIFI_STATIC_NETMASK = "wifi_static_netmask";
/**
@@ -3875,6 +3967,7 @@
* @deprecated Use {@link WifiManager} instead
*/
@Deprecated
+ @Readable
public static final String WIFI_STATIC_DNS1 = "wifi_static_dns1";
/**
@@ -3885,6 +3978,7 @@
* @deprecated Use {@link WifiManager} instead
*/
@Deprecated
+ @Readable
public static final String WIFI_STATIC_DNS2 = "wifi_static_dns2";
/**
@@ -3895,6 +3989,7 @@
* 1 -- connectable but not discoverable
* 0 -- neither connectable nor discoverable
*/
+ @Readable
public static final String BLUETOOTH_DISCOVERABILITY =
"bluetooth_discoverability";
@@ -3903,6 +3998,7 @@
* 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 @@
* @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 @@
* instead.
* @hide
*/
+ @Readable
public static final String SYSTEM_LOCALES = "system_locales";
@@ -3977,12 +4076,14 @@
* @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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String SCREEN_AUTO_BRIGHTNESS_ADJ = "screen_auto_brightness_adj";
/**
@@ -4073,6 +4184,8 @@
* @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 @@
* 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 @@
* 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 @@
*
* @hide
*/
+ @Readable
public static final String VIBRATE_INPUT_DEVICES = "vibrate_input_devices";
/**
@@ -4142,6 +4259,7 @@
* 3 - Strong vibrations
* @hide
*/
+ @Readable
public static final String NOTIFICATION_VIBRATION_INTENSITY =
"notification_vibration_intensity";
/**
@@ -4158,6 +4276,7 @@
* 3 - Strong vibrations
* @hide
*/
+ @Readable
public static final String RING_VIBRATION_INTENSITY =
"ring_vibration_intensity";
@@ -4175,6 +4294,7 @@
* 3 - Strong vibrations
* @hide
*/
+ @Readable
public static final String HAPTIC_FEEDBACK_INTENSITY =
"haptic_feedback_intensity";
@@ -4184,6 +4304,7 @@
*
* @removed Not used by anything since API 2.
*/
+ @Readable
public static final String VOLUME_RING = "volume_ring";
/**
@@ -4192,6 +4313,7 @@
*
* @removed Not used by anything since API 2.
*/
+ @Readable
public static final String VOLUME_SYSTEM = "volume_system";
/**
@@ -4200,6 +4322,7 @@
*
* @removed Not used by anything since API 2.
*/
+ @Readable
public static final String VOLUME_VOICE = "volume_voice";
/**
@@ -4208,6 +4331,7 @@
*
* @removed Not used by anything since API 2.
*/
+ @Readable
public static final String VOLUME_MUSIC = "volume_music";
/**
@@ -4216,6 +4340,7 @@
*
* @removed Not used by anything since API 2.
*/
+ @Readable
public static final String VOLUME_ALARM = "volume_alarm";
/**
@@ -4224,6 +4349,7 @@
*
* @removed Not used by anything since API 2.
*/
+ @Readable
public static final String VOLUME_NOTIFICATION = "volume_notification";
/**
@@ -4232,6 +4358,7 @@
*
* @removed Not used by anything since API 2.
*/
+ @Readable
public static final String VOLUME_BLUETOOTH_SCO = "volume_bluetooth_sco";
/**
@@ -4239,12 +4366,14 @@
* 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 @@
*
* @hide
*/
+ @Readable
public static final String VOLUME_MASTER = "volume_master";
/**
@@ -4260,6 +4390,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String MASTER_MONO = "master_mono";
/**
@@ -4267,6 +4398,7 @@
*
* @hide
*/
+ @Readable
public static final String MASTER_BALANCE = "master_balance";
/**
@@ -4284,6 +4416,7 @@
* @deprecated
*/
@Deprecated
+ @Readable
public static final String NOTIFICATIONS_USE_RING_VOLUME =
"notifications_use_ring_volume";
@@ -4300,6 +4433,7 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String VIBRATE_IN_SILENT = "vibrate_in_silent";
/**
@@ -4335,6 +4469,7 @@
*
* @removed Not used by anything since API 2.
*/
+ @Readable
public static final String APPEND_FOR_LAST_AUDIBLE = "_last_audible";
/**
@@ -4346,6 +4481,7 @@
*
* @see #DEFAULT_RINGTONE_URI
*/
+ @Readable
public static final String RINGTONE = "ringtone";
/**
@@ -4359,6 +4495,7 @@
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 @@
* @see #RINGTONE
* @see #DEFAULT_NOTIFICATION_URI
*/
+ @Readable
public static final String NOTIFICATION_SOUND = "notification_sound";
/**
@@ -4380,6 +4518,7 @@
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 @@
* @see #RINGTONE
* @see #DEFAULT_ALARM_ALERT_URI
*/
+ @Readable
public static final String ALARM_ALERT = "alarm_alert";
/**
@@ -4401,6 +4541,7 @@
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 @@
*
* @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 @@
* @deprecated Use {@link WallpaperManager} instead.
*/
@Deprecated
+ @Readable
public static final String WALLPAPER_ACTIVITY = "wallpaper_activity";
/**
@@ -4463,6 +4611,7 @@
* 12
* 24
*/
+ @Readable
public static final String TIME_12_24 = "time_12_24";
/**
@@ -4471,6 +4620,7 @@
* dd/mm/yyyy
* yyyy/mm/dd
*/
+ @Readable
public static final String DATE_FORMAT = "date_format";
/**
@@ -4480,6 +4630,7 @@
* 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 @@
* 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 @@
*
* @see Display#getRotation
*/
+ @Readable
public static final String USER_ROTATION = "user_rotation";
/**
@@ -4540,6 +4693,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY =
"hide_rotation_lock_toggle_for_accessibility";
@@ -4553,6 +4707,7 @@
* 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 @@
* {@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 @@
* 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 @@
* 0 = Normal
* 1 = Long
*/
+ @Readable
public static final String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type";
/**
@@ -4583,6 +4741,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String HEARING_AID = "hearing_aid";
/**
@@ -4595,18 +4754,21 @@
* @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 @@
* setting for this.
*/
@Deprecated
+ @Readable
public static final String SHOW_WEB_SUGGESTIONS = "show_web_suggestions";
/**
@@ -4622,6 +4785,7 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String NOTIFICATION_LIGHT_PULSE = "notification_light_pulse";
/**
@@ -4631,6 +4795,7 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String POINTER_LOCATION = "pointer_location";
/**
@@ -4640,6 +4805,7 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String SHOW_TOUCHES = "show_touches";
/**
@@ -4650,6 +4816,7 @@
* 1 = yes
* @hide
*/
+ @Readable
public static final String WINDOW_ORIENTATION_LISTENER_LOG =
"window_orientation_listener_log";
@@ -4675,12 +4842,14 @@
* @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 @@
* 1 = yes
* @hide
*/
+ @Readable
public static final String SIP_RECEIVE_CALLS = "sip_receive_calls";
/**
@@ -4759,18 +4929,21 @@
* "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 @@
* @hide
*/
@Deprecated
+ @Readable
public static final String SIP_ASK_ME_EACH_TIME = "SIP_ASK_ME_EACH_TIME";
/**
@@ -4792,12 +4966,14 @@
* @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 @@
* Backward-compatible with <code>PrefGetPreference(prefAllowEasterEggs)</code>.
* @hide
*/
+ @Readable
public static final String EGG_MODE = "egg_mode";
/**
@@ -4815,6 +4992,7 @@
* 1 - Show percentage
* @hide
*/
+ @Readable
public static final String SHOW_BATTERY_PERCENT = "status_bar_show_battery_percent";
/**
@@ -4823,6 +5001,7 @@
* 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 @@
* @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 @@
* instead
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE =
Secure.WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE;
@@ -5187,6 +5368,7 @@
* {@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 @@
* {@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 @@
* instead
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS =
Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS;
@@ -5212,6 +5396,7 @@
* {@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 @@
* instead
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_PING_TIMEOUT_MS =
Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS;
@@ -5295,7 +5481,8 @@
CONTENT_URI,
CALL_METHOD_GET_SECURE,
CALL_METHOD_PUT_SECURE,
- sProviderHolder);
+ sProviderHolder,
+ Secure.class);
private static ILockSettings sLockSettings = null;
@@ -5433,6 +5620,11 @@
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 @@
* Control whether to enable adaptive sleep mode.
* @hide
*/
+ @Readable
public static final String ADAPTIVE_SLEEP = "adaptive_sleep";
/**
@@ -5935,6 +6128,7 @@
*
* @hide
*/
+ @Readable
public static final String CAMERA_AUTOROTATE = "camera_autorotate";
/**
@@ -5952,6 +6146,7 @@
* @hide
*/
@Deprecated
+ @Readable
public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu";
/**
@@ -5969,6 +6164,7 @@
* @deprecated This settings is not used anymore.
*/
@Deprecated
+ @Readable
public static final String ALLOW_MOCK_LOCATION = "mock_location";
/**
@@ -5977,6 +6173,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String ODI_CAPTIONS_ENABLED = "odi_captions_enabled";
/**
@@ -6016,6 +6213,7 @@
* 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 @@
* 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 @@
* 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 @@
* @hide
*/
@TestApi
+ @Readable
public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
/**
@@ -6069,6 +6272,7 @@
* @hide
*/
@TestApi
+ @Readable
public static final String AUTOFILL_SERVICE = "autofill_service";
/**
@@ -6079,6 +6283,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION =
"autofill_field_classification";
@@ -6087,6 +6292,7 @@
*
* @hide
*/
+ @Readable
public static final String DARK_MODE_DIALOG_SEEN =
"dark_mode_dialog_seen";
@@ -6095,6 +6301,7 @@
* 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 @@
* 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 @@
* @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 @@
* @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 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT =
"autofill_user_data_max_category_count";
@@ -6141,6 +6352,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH =
"autofill_user_data_max_value_length";
@@ -6150,6 +6362,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH =
"autofill_user_data_min_value_length";
@@ -6162,6 +6375,7 @@
* @hide
*/
@TestApi
+ @Readable
public static final String CONTENT_CAPTURE_ENABLED = "content_capture_enabled";
/**
@@ -6179,6 +6393,7 @@
*
* @hide
*/
+ @Readable
public static final String MANAGED_PROVISIONING_DPC_DOWNLOADED =
"managed_provisioning_dpc_downloaded";
@@ -6189,6 +6404,7 @@
* <p>
* Type: int (0 for false, 1 for true)
*/
+ @Readable
public static final String SECURE_FRP_MODE = "secure_frp_mode";
/**
@@ -6199,6 +6415,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String USER_SETUP_COMPLETE = "user_setup_complete";
/**
@@ -6254,6 +6471,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String USER_SETUP_PERSONALIZATION_STATE =
"user_setup_personalization_state";
@@ -6264,6 +6482,7 @@
*
* @hide
*/
+ @Readable
public static final String TV_USER_SETUP_COMPLETE = "tv_user_setup_complete";
/**
@@ -6275,6 +6494,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category.";
/**
@@ -6285,6 +6505,7 @@
* 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 @@
* by ':'.
* @hide
*/
+ @Readable
public static final String DISABLED_SYSTEM_INPUT_METHODS = "disabled_system_input_methods";
/**
@@ -6301,6 +6523,7 @@
* @hide
*/
@TestApi
+ @Readable
@SuppressLint("NoSettingsProvider")
public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
@@ -6318,6 +6541,7 @@
*
* @hide
*/
+ @Readable
public static final String ALWAYS_ON_VPN_APP = "always_on_vpn_app";
/**
@@ -6326,6 +6550,7 @@
*
* @hide
*/
+ @Readable
public static final String ALWAYS_ON_VPN_LOCKDOWN = "always_on_vpn_lockdown";
/**
@@ -6335,6 +6560,7 @@
*
* @hide
*/
+ @Readable
public static final String ALWAYS_ON_VPN_LOCKDOWN_WHITELIST =
"always_on_vpn_lockdown_whitelist";
@@ -6348,6 +6574,8 @@
* {@link PackageManager#canRequestPackageInstalls()}
* @see PackageManager#canRequestPackageInstalls()
*/
+ @Deprecated
+ @Readable
public static final String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
/**
@@ -6359,6 +6587,7 @@
*
* @hide
*/
+ @Readable
public static final String UNKNOWN_SOURCES_DEFAULT_REVERSED =
"unknown_sources_default_reversed";
@@ -6372,6 +6601,7 @@
* instead.
*/
@Deprecated
+ @Readable
public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
/**
@@ -6383,12 +6613,14 @@
* {@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 @@
* 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 @@
*
* @hide
*/
+ @Readable
public static final String LOCATION_COARSE_ACCURACY_M = "locationCoarseAccuracy";
/**
@@ -6473,6 +6707,7 @@
* @hide
*/
@Deprecated
+ @Readable
public static final String LOCK_BIOMETRIC_WEAK_FLAGS =
"lock_biometric_weak_flags";
@@ -6480,6 +6715,7 @@
* 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 @@
* {@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 @@
* {@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 @@
* {@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 @@
* @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 @@
* @deprecated
*/
@Deprecated
+ @Readable
public static final String LOCK_SCREEN_OWNER_INFO = "lock_screen_owner_info";
/**
@@ -6537,6 +6778,7 @@
* @hide
*/
@Deprecated
+ @Readable
public static final String LOCK_SCREEN_APPWIDGET_IDS =
"lock_screen_appwidget_ids";
@@ -6545,6 +6787,7 @@
* @hide
*/
@Deprecated
+ @Readable
public static final String LOCK_SCREEN_FALLBACK_APPWIDGET_ID =
"lock_screen_fallback_appwidget_id";
@@ -6553,6 +6796,7 @@
* @hide
*/
@Deprecated
+ @Readable
public static final String LOCK_SCREEN_STICKY_APPWIDGET =
"lock_screen_sticky_appwidget";
@@ -6563,6 +6807,7 @@
*/
@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 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS =
"lock_screen_allow_private_notifications";
@@ -6583,6 +6829,7 @@
* without having to unlock
* @hide
*/
+ @Readable
public static final String LOCK_SCREEN_ALLOW_REMOTE_INPUT =
"lock_screen_allow_remote_input";
@@ -6592,12 +6839,14 @@
* {"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 @@
* the lockscreen notification policy.
* @hide
*/
+ @Readable
public static final String SHOW_NOTE_ABOUT_NOTIFICATION_HIDING =
"show_note_about_notification_hiding";
@@ -6612,6 +6862,7 @@
* 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 @@
* many collisions. It should not be used.
*/
@Deprecated
+ @Readable
public static final String LOGGING_ID = "logging_id";
/**
@@ -6633,16 +6885,19 @@
/**
* 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 @@
* and new Settings apps.
*/
// TODO: 881807
+ @Readable
public static final String SETTINGS_CLASSNAME = "settings_classname";
/**
@@ -6668,12 +6924,14 @@
/**
* 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 @@
* 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 @@
*/
@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 @@
* accessibility feature.
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_BUTTON_TARGET_COMPONENT =
"accessibility_button_target_component";
@@ -6717,6 +6978,7 @@
* accessibility feature.
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_BUTTON_TARGETS = "accessibility_button_targets";
/**
@@ -6725,17 +6987,20 @@
*
* @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 @@
*
* @hide
*/
+ @Readable
public static final String TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES =
"touch_exploration_granted_accessibility_services";
@@ -6752,12 +7018,14 @@
* 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 @@
* 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 @@
* @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 @@
* 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 @@
* 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 @@
* boolean (1 or 0).
* @hide
*/
+ @Readable
public static final String IN_CALL_NOTIFICATION_ENABLED = "in_call_notification_enabled";
/**
@@ -6843,6 +7122,7 @@
*
* @hide
*/
+ @Readable
public static final String KEYGUARD_SLICE_URI = "keyguard_slice_uri";
/**
@@ -6855,6 +7135,7 @@
*
* @hide
*/
+ @Readable
public static final String FONT_WEIGHT_ADJUSTMENT = "font_weight_adjustment";
/**
@@ -6865,6 +7146,7 @@
* 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 @@
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED =
"high_text_contrast_enabled";
@@ -6885,6 +7168,7 @@
*/
@UnsupportedAppUsage
@TestApi
+ @Readable
public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED =
"accessibility_display_magnification_enabled";
@@ -6900,6 +7184,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED =
"accessibility_display_magnification_navbar_enabled";
@@ -6913,6 +7198,7 @@
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE =
"accessibility_display_magnification_scale";
@@ -6923,6 +7209,7 @@
* @deprecated
*/
@Deprecated
+ @Readable
public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE =
"accessibility_display_magnification_auto_update";
@@ -6932,6 +7219,7 @@
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_SOFT_KEYBOARD_MODE =
"accessibility_soft_keyboard_mode";
@@ -6965,6 +7253,7 @@
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_ENABLED =
"accessibility_captioning_enabled";
@@ -6975,6 +7264,7 @@
* @see java.util.Locale#toString
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_LOCALE =
"accessibility_captioning_locale";
@@ -6989,6 +7279,7 @@
* @see java.util.Locale#toString
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_PRESET =
"accessibility_captioning_preset";
@@ -6999,6 +7290,7 @@
* @see android.graphics.Color#argb
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR =
"accessibility_captioning_background_color";
@@ -7009,6 +7301,7 @@
* @see android.graphics.Color#argb
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR =
"accessibility_captioning_foreground_color";
@@ -7023,6 +7316,7 @@
* @see #ACCESSIBILITY_CAPTIONING_EDGE_COLOR
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_EDGE_TYPE =
"accessibility_captioning_edge_type";
@@ -7034,6 +7328,7 @@
* @see android.graphics.Color#argb
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_EDGE_COLOR =
"accessibility_captioning_edge_color";
@@ -7044,6 +7339,7 @@
* @see android.graphics.Color#argb
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_WINDOW_COLOR =
"accessibility_captioning_window_color";
@@ -7060,6 +7356,7 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String ACCESSIBILITY_CAPTIONING_TYPEFACE =
"accessibility_captioning_typeface";
@@ -7068,12 +7365,14 @@
*
* @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 @@
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED =
"accessibility_display_daltonizer_enabled";
@@ -7100,6 +7400,7 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String ACCESSIBILITY_DISPLAY_DALTONIZER =
"accessibility_display_daltonizer";
@@ -7110,6 +7411,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String ACCESSIBILITY_AUTOCLICK_ENABLED =
"accessibility_autoclick_enabled";
@@ -7120,6 +7422,7 @@
* @see #ACCESSIBILITY_AUTOCLICK_ENABLED
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_AUTOCLICK_DELAY =
"accessibility_autoclick_delay";
@@ -7130,6 +7433,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String ACCESSIBILITY_LARGE_POINTER_ICON =
"accessibility_large_pointer_icon";
@@ -7138,6 +7442,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String LONG_PRESS_TIMEOUT = "long_press_timeout";
/**
@@ -7145,6 +7450,7 @@
* 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 @@
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS =
"accessibility_non_interactive_ui_timeout_ms";
@@ -7162,6 +7469,7 @@
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS =
"accessibility_interactive_ui_timeout_ms";
@@ -7172,6 +7480,7 @@
*
* @hide
*/
+ @Readable
public static final String REDUCE_BRIGHT_COLORS_ACTIVATED =
"reduce_bright_colors_activated";
@@ -7181,6 +7490,7 @@
*
* @hide
*/
+ @Readable
public static final String REDUCE_BRIGHT_COLORS_LEVEL =
"reduce_bright_colors_level";
@@ -7189,6 +7499,7 @@
*
* @hide
*/
+ @Readable
public static final String REDUCE_BRIGHT_COLORS_PERSIST_ACROSS_REBOOTS =
"reduce_bright_colors_persist_across_reboots";
@@ -7201,6 +7512,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String ENABLED_PRINT_SERVICES =
"enabled_print_services";
@@ -7210,6 +7522,7 @@
* @hide
*/
@TestApi
+ @Readable
public static final String DISABLED_PRINT_SERVICES =
"disabled_print_services";
@@ -7220,6 +7533,7 @@
*
* @hide
*/
+ @Readable
public static final String DISPLAY_DENSITY_FORCED = "display_density_forced";
/**
@@ -7232,21 +7546,25 @@
* 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 @@
* locale. {@link TextToSpeech#getLanguage()}.
*/
@Deprecated
+ @Readable
public static final String TTS_DEFAULT_LANG = "tts_default_lang";
/**
@@ -7269,6 +7588,7 @@
* locale. {@link TextToSpeech#getLanguage()}.
*/
@Deprecated
+ @Readable
public static final String TTS_DEFAULT_COUNTRY = "tts_default_country";
/**
@@ -7280,6 +7600,7 @@
* locale that is in use {@link TextToSpeech#getLanguage()}.
*/
@Deprecated
+ @Readable
public static final String TTS_DEFAULT_VARIANT = "tts_default_variant";
/**
@@ -7294,11 +7615,13 @@
*
* @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 @@
* @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 @@
* @deprecated This setting is not used.
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_AP_COUNT = "wifi_watchdog_ap_count";
/**
@@ -7354,6 +7679,7 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @deprecated This setting is not used.
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_WATCH_LIST = "wifi_watchdog_watch_list";
/**
@@ -7413,6 +7745,7 @@
* @deprecated This setting is not used.
*/
@Deprecated
+ @Readable
public static final String WIFI_WATCHDOG_PING_COUNT = "wifi_watchdog_ping_count";
/**
@@ -7420,6 +7753,7 @@
* @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 @@
* @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 @@
*
* @hide
*/
+ @Readable
public static final String CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS =
"connectivity_release_pending_intent_delay_ms";
@@ -7464,12 +7800,14 @@
* 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 @@
* 3 = TTY VCO
* @hide
*/
+ @Readable
public static final String PREFERRED_TTY_MODE =
"preferred_tty_mode";
@@ -7489,6 +7828,7 @@
* 1 = enhanced voice privacy
* @hide
*/
+ @Readable
public static final String ENHANCED_VOICE_PRIVACY_ENABLED = "enhanced_voice_privacy_enabled";
/**
@@ -7497,6 +7837,7 @@
* 1 = enabled
* @hide
*/
+ @Readable
public static final String TTY_MODE_ENABLED = "tty_mode_enabled";
/**
@@ -7505,6 +7846,7 @@
* 0 = OFF
* 1 = ON
*/
+ @Readable
public static final String RTT_CALLING_MODE = "rtt_calling_mode";
/**
@@ -7514,6 +7856,7 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String BACKUP_ENABLED = "backup_enabled";
/**
@@ -7523,6 +7866,7 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String BACKUP_AUTO_RESTORE = "backup_auto_restore";
/**
@@ -7531,6 +7875,7 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String BACKUP_PROVISIONED = "backup_provisioned";
/**
@@ -7538,6 +7883,7 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String BACKUP_TRANSPORT = "backup_transport";
/**
@@ -7547,6 +7893,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String LAST_SETUP_SHOWN = "last_setup_shown";
/**
@@ -7569,6 +7916,7 @@
*
* @hide
*/
+ @Readable
public static final String SEARCH_GLOBAL_SEARCH_ACTIVITY =
"search_global_search_activity";
@@ -7576,21 +7924,25 @@
* 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 @@
* 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 @@
* 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 @@
* (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 @@
*/
@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 @@
* @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 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String VOICE_RECOGNITION_SERVICE = "voice_recognition_service";
/**
@@ -7741,6 +8114,7 @@
*/
@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 @@
*/
@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 @@
*
* @hide
*/
+ @Readable
public static final String SPELL_CHECKER_ENABLED = "spell_checker_enabled";
/**
@@ -7774,6 +8150,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String INCALL_POWER_BUTTON_BEHAVIOR = "incall_power_button_behavior";
/**
@@ -7788,6 +8165,7 @@
*
* @hide
*/
+ @Readable
public static final String MINIMAL_POST_PROCESSING_ALLOWED =
"minimal_post_processing_allowed";
@@ -7832,6 +8210,7 @@
* @see #MATCH_CONTENT_FRAMERATE_ALWAYS
* @hide
*/
+ @Readable
public static final String MATCH_CONTENT_FRAME_RATE =
"match_content_frame_rate";
@@ -7863,6 +8242,7 @@
*
* @hide
*/
+ @Readable
public static final String INCALL_BACK_BUTTON_BEHAVIOR = "incall_back_button_behavior";
/**
@@ -7888,6 +8268,7 @@
* 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 @@
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String DOZE_ENABLED = "doze_enabled";
/**
@@ -7905,36 +8287,42 @@
* @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 @@
* {@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 @@
* 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 @@
* 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 @@
* UiModeManager.
* @hide
*/
+ @Readable
public static final String UI_NIGHT_MODE = "ui_night_mode";
/**
@@ -8075,12 +8484,14 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
*/
@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 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String SMS_DEFAULT_APPLICATION = "sms_default_application";
/**
@@ -8152,6 +8572,7 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Readable
public static final String DIALER_DEFAULT_APPLICATION = "dialer_default_application";
/**
@@ -8159,6 +8580,7 @@
* application
* @hide
*/
+ @Readable
public static final String CALL_SCREENING_DEFAULT_COMPONENT =
"call_screening_default_component";
@@ -8169,6 +8591,7 @@
*
* @hide
*/
+ @Readable
public static final String EMERGENCY_ASSISTANCE_APPLICATION = "emergency_assistance_application";
/**
@@ -8177,6 +8600,7 @@
*
* @hide
*/
+ @Readable
public static final String ASSIST_STRUCTURE_ENABLED = "assist_structure_enabled";
/**
@@ -8185,6 +8609,7 @@
*
* @hide
*/
+ @Readable
public static final String ASSIST_SCREENSHOT_ENABLED = "assist_screenshot_enabled";
/**
@@ -8196,6 +8621,7 @@
*
* @hide
*/
+ @Readable
public static final String ASSIST_DISCLOSURE_ENABLED = "assist_disclosure_enabled";
/**
@@ -8207,7 +8633,7 @@
*
* @hide
*/
-
+ @Readable
public static final String SHOW_ROTATION_SUGGESTIONS = "show_rotation_suggestions";
/**
@@ -8234,6 +8660,7 @@
* introduced to rotation suggestions.
* @hide
*/
+ @Readable
public static final String NUM_ROTATION_SUGGESTIONS_ACCEPTED =
"num_rotation_suggestions_accepted";
@@ -8246,6 +8673,7 @@
* @hide
*/
@Deprecated
+ @Readable
public static final String ENABLED_NOTIFICATION_ASSISTANT =
"enabled_notification_assistant";
@@ -8259,6 +8687,7 @@
*/
@Deprecated
@UnsupportedAppUsage
+ @Readable
public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
/**
@@ -8270,6 +8699,7 @@
*/
@Deprecated
@TestApi
+ @Readable
public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES =
"enabled_notification_policy_access_packages";
@@ -8283,6 +8713,7 @@
* @hide
*/
@TestApi
+ @Readable
@RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
@@ -8291,6 +8722,7 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
+ @Readable
public static final String IMMERSIVE_MODE_CONFIRMATIONS = "immersive_mode_confirmations";
/**
@@ -8298,6 +8730,7 @@
*
* @hide
*/
+ @Readable
public static final String PRINT_SERVICE_SEARCH_URI = "print_service_search_uri";
/**
@@ -8305,6 +8738,7 @@
*
* @hide
*/
+ @Readable
public static final String PAYMENT_SERVICE_SEARCH_URI = "payment_service_search_uri";
/**
@@ -8312,6 +8746,7 @@
*
* @hide
*/
+ @Readable
public static final String AUTOFILL_SERVICE_SEARCH_URI = "autofill_service_search_uri";
/**
@@ -8320,6 +8755,7 @@
* <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 @@
*
* @hide
*/
+ @Readable
public static final String UNSAFE_VOLUME_MUSIC_ACTIVE_MS = "unsafe_volume_music_active_ms";
/**
@@ -8337,6 +8774,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS =
"lock_screen_show_notifications";
@@ -8347,6 +8785,7 @@
*
* @hide
*/
+ @Readable
public static final String LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS =
"lock_screen_show_silent_notifications";
@@ -8357,6 +8796,7 @@
*
* @hide
*/
+ @Readable
public static final String SHOW_NOTIFICATION_SNOOZE = "show_notification_snooze";
/**
@@ -8365,6 +8805,7 @@
* {@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 @@
* 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 @@
*
* @hide
*/
+ @Readable
public static final String TV_APP_USES_NON_SYSTEM_INPUTS = "tv_app_uses_non_system_inputs";
/**
@@ -8399,6 +8842,7 @@
*
* @hide
*/
+ @Readable
public static final String USB_AUDIO_AUTOMATIC_ROUTING_DISABLED =
"usb_audio_automatic_routing_disabled";
@@ -8414,6 +8858,7 @@
*
* @hide
*/
+ @Readable
public static final String SLEEP_TIMEOUT = "sleep_timeout";
/**
@@ -8428,12 +8873,14 @@
*
* @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 @@
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String ASSISTANT = "assistant";
/**
@@ -8454,6 +8902,7 @@
*
* @hide
*/
+ @Readable
public static final String CAMERA_GESTURE_DISABLED = "camera_gesture_disabled";
/**
@@ -8461,6 +8910,7 @@
*
* @hide
*/
+ @Readable
public static final String EMERGENCY_GESTURE_ENABLED = "emergency_gesture_enabled";
/**
@@ -8468,6 +8918,7 @@
*
* @hide
*/
+ @Readable
public static final String EMERGENCY_GESTURE_SOUND_ENABLED =
"emergency_gesture_sound_enabled";
@@ -8477,6 +8928,7 @@
*
* @hide
*/
+ @Readable
public static final String CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED =
"camera_double_tap_power_gesture_disabled";
@@ -8486,6 +8938,7 @@
*
* @hide
*/
+ @Readable
public static final String CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED =
"camera_double_twist_to_flip_enabled";
@@ -8495,6 +8948,7 @@
*
* @hide
*/
+ @Readable
public static final String CAMERA_LIFT_TRIGGER_ENABLED = "camera_lift_trigger_enabled";
/**
@@ -8510,6 +8964,7 @@
*
* @hide
*/
+ @Readable
public static final String FLASHLIGHT_AVAILABLE = "flashlight_available";
/**
@@ -8517,18 +8972,21 @@
*
* @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 @@
* 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 @@
* truth is obtained through the HAL.
* @hide
*/
+ @Readable
public static final String FACE_UNLOCK_ATTENTION_REQUIRED =
"face_unlock_attention_required";
@@ -8552,6 +9012,7 @@
* 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 @@
* 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 @@
* setConfirmationRequired API.
* @hide
*/
+ @Readable
public static final String FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION =
"face_unlock_always_require_confirmation";
@@ -8583,12 +9046,14 @@
*
* @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 @@
*
* @hide
*/
+ @Readable
public static final String ASSIST_GESTURE_ENABLED = "assist_gesture_enabled";
/**
@@ -8604,6 +9070,7 @@
*
* @hide
*/
+ @Readable
public static final String ASSIST_GESTURE_SENSITIVITY = "assist_gesture_sensitivity";
/**
@@ -8611,6 +9078,7 @@
*
* @hide
*/
+ @Readable
public static final String ASSIST_GESTURE_SILENCE_ALERTS_ENABLED =
"assist_gesture_silence_alerts_enabled";
@@ -8619,6 +9087,7 @@
*
* @hide
*/
+ @Readable
public static final String ASSIST_GESTURE_WAKE_ENABLED =
"assist_gesture_wake_enabled";
@@ -8630,36 +9099,42 @@
* @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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
+ @Readable
public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
/**
@@ -8710,6 +9190,7 @@
*
* @hide
*/
+ @Readable
public static final String VR_DISPLAY_MODE = "vr_display_mode";
/**
@@ -8743,6 +9224,7 @@
*
* @hide
*/
+ @Readable
public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";
/**
@@ -8750,6 +9232,7 @@
*
* @hide
*/
+ @Readable
public static final String MANAGED_PROFILE_CONTACT_REMOTE_SEARCH =
"managed_profile_contact_remote_search";
@@ -8758,6 +9241,7 @@
*
* @hide
*/
+ @Readable
public static final String CROSS_PROFILE_CALENDAR_ENABLED =
"cross_profile_calendar_enabled";
@@ -8766,6 +9250,7 @@
*
* @hide
*/
+ @Readable
public static final String AUTOMATIC_STORAGE_MANAGER_ENABLED =
"automatic_storage_manager_enabled";
@@ -8774,6 +9259,7 @@
*
* @hide
*/
+ @Readable
public static final String AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN =
"automatic_storage_manager_days_to_retain";
@@ -8789,6 +9275,7 @@
*
* @hide
*/
+ @Readable
public static final String AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED =
"automatic_storage_manager_bytes_cleared";
@@ -8797,6 +9284,7 @@
*
* @hide
*/
+ @Readable
public static final String AUTOMATIC_STORAGE_MANAGER_LAST_RUN =
"automatic_storage_manager_last_run";
/**
@@ -8806,6 +9294,7 @@
*
* @hide
*/
+ @Readable
public static final String AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY =
"automatic_storage_manager_turned_off_by_policy";
@@ -8813,6 +9302,7 @@
* Whether SystemUI navigation keys is enabled.
* @hide
*/
+ @Readable
public static final String SYSTEM_NAVIGATION_KEYS_ENABLED =
"system_navigation_keys_enabled";
@@ -8821,6 +9311,7 @@
*
* @hide
*/
+ @Readable
public static final String QS_TILES = "sysui_qs_tiles";
/**
@@ -8831,6 +9322,7 @@
*
* @hide
*/
+ @Readable
public static final String CONTROLS_ENABLED = "controls_enabled";
/**
@@ -8840,6 +9332,7 @@
* @hide
*/
@TestApi
+ @Readable
public static final String POWER_MENU_LOCKED_SHOW_CONTENT =
"power_menu_locked_show_content";
@@ -8849,18 +9342,21 @@
* @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 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
+ @Readable
public static final String NOTIFICATION_BADGING = "notification_badging";
/**
@@ -8879,6 +9376,7 @@
* The value 1 - enable, 0 - disable
* @hide
*/
+ @Readable
public static final String NOTIFICATION_HISTORY_ENABLED = "notification_history_enabled";
/**
@@ -8887,6 +9385,7 @@
* The value 1 - enable, 0 - disable
* @hide
*/
+ @Readable
public static final String BUBBLE_IMPORTANT_CONVERSATIONS
= "bubble_important_conversations";
@@ -8896,18 +9395,21 @@
*
* @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 @@
* Type: string
* @hide
*/
+ @Readable
public static final String BACKUP_MANAGER_CONSTANTS = "backup_manager_constants";
@@ -8952,6 +9455,7 @@
* Type: string
* @hide
*/
+ @Readable
public static final String BACKUP_LOCAL_TRANSPORT_PARAMETERS =
"backup_local_transport_parameters";
@@ -8960,6 +9464,7 @@
* the user is driving.
* @hide
*/
+ @Readable
public static final String BLUETOOTH_ON_WHILE_DRIVING = "bluetooth_on_while_driving";
/**
@@ -8969,6 +9474,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String VOLUME_HUSH_GESTURE = "volume_hush_gesture";
/** @hide */
@@ -8985,6 +9491,7 @@
* 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 @@
*
* @hide
*/
+ @Readable
public static final String LOW_POWER_WARNING_ACKNOWLEDGED =
"low_power_warning_acknowledged";
@@ -9002,6 +9510,7 @@
* suppressed.
* @hide
*/
+ @Readable
public static final String SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION =
"suppress_auto_battery_saver_suggestion";
@@ -9010,6 +9519,7 @@
* 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 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS =
"location_access_check_interval_millis";
@@ -9026,6 +9537,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS =
"location_access_check_delay_millis";
@@ -9035,6 +9547,7 @@
*/
@SystemApi
@Deprecated
+ @Readable
public static final String LOCATION_PERMISSIONS_UPGRADE_TO_Q_MODE =
"location_permissions_upgrade_to_q_mode";
@@ -9043,6 +9556,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String AUTO_REVOKE_DISABLED = "auto_revoke_disabled";
/**
@@ -9053,6 +9567,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String THEME_CUSTOMIZATION_OVERLAY_PACKAGES =
"theme_customization_overlay_packages";
@@ -9063,6 +9578,7 @@
* 2 = fully gestural
* @hide
*/
+ @Readable
public static final String NAVIGATION_MODE =
"navigation_mode";
@@ -9070,6 +9586,7 @@
* 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 @@
* 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 @@
* 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 @@
* @see Settings.Global#SHOW_MEDIA_ON_QUICK_SETTINGS
* @hide
*/
+ @Readable
public static final String MEDIA_CONTROLS_RESUME = "qs_media_resumption";
/**
@@ -9127,6 +9651,7 @@
* @see Settings.Secure#MEDIA_CONTROLS_RESUME
* @hide
*/
+ @Readable
public static final String MEDIA_CONTROLS_RESUME_BLOCKED = "qs_media_resumption_blocked";
/**
@@ -9138,6 +9663,7 @@
* @hide
*/
@TestApi
+ @Readable
public static final String ACCESSIBILITY_MAGNIFICATION_MODE =
"accessibility_magnification_mode";
@@ -9173,6 +9699,7 @@
* @hide
*/
@TestApi
+ @Readable
public static final String ACCESSIBILITY_MAGNIFICATION_CAPABILITY =
"accessibility_magnification_capability";
@@ -9182,6 +9709,7 @@
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT =
"accessibility_show_window_magnification_prompt";
@@ -9198,6 +9726,7 @@
* @see #ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_BUTTON_MODE =
"accessibility_button_mode";
@@ -9226,6 +9755,7 @@
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_FLOATING_MENU_SIZE =
"accessibility_floating_menu_size";
@@ -9238,6 +9768,7 @@
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_FLOATING_MENU_ICON_TYPE =
"accessibility_floating_menu_icon_type";
@@ -9246,6 +9777,7 @@
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED =
"accessibility_floating_menu_fade_enabled";
@@ -9255,6 +9787,7 @@
*
* @hide
*/
+ @Readable
public static final String ACCESSIBILITY_FLOATING_MENU_OPACITY =
"accessibility_floating_menu_opacity";
@@ -9263,6 +9796,7 @@
*
* @hide
*/
+ @Readable
public static final String ADAPTIVE_CONNECTIVITY_ENABLED = "adaptive_connectivity_enabled";
/**
@@ -9274,6 +9808,7 @@
*
* @hide
*/
+ @Readable
public static final String[] LEGACY_RESTORE_SETTINGS = {
ENABLED_NOTIFICATION_LISTENERS,
ENABLED_NOTIFICATION_ASSISTANT,
@@ -9285,6 +9820,7 @@
*
* @hide
*/
+ @Readable
public static final String ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS =
"reminder_exp_learning_time_elapsed";
@@ -9293,6 +9829,7 @@
*
* @hide
*/
+ @Readable
public static final String ASSIST_HANDLES_LEARNING_EVENT_COUNT =
"reminder_exp_learning_event_count";
@@ -9406,6 +9943,7 @@
* @hide
*/
@TestApi
+ @Readable
public static final String NOTIFICATION_BUBBLES = "notification_bubbles";
/**
@@ -9414,6 +9952,7 @@
* Type: int
* @hide
*/
+ @Readable
public static final String ADD_USERS_WHEN_LOCKED = "add_users_when_locked";
/**
@@ -9421,6 +9960,7 @@
* <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 @@
* 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 @@
* {@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 @@
* 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 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String AIRPLANE_MODE_TOGGLEABLE_RADIOS = "airplane_mode_toggleable_radios";
/**
@@ -9494,6 +10044,7 @@
*
* @hide
*/
+ @Readable
public static final String BLUETOOTH_CLASS_OF_DEVICE = "bluetooth_class_of_device";
/**
@@ -9501,6 +10052,7 @@
* See {@link android.bluetooth.BluetoothProfile}.
* {@hide}
*/
+ @Readable
public static final String BLUETOOTH_DISABLED_PROFILES = "bluetooth_disabled_profiles";
/**
@@ -9513,6 +10065,7 @@
* "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 @@
* @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 @@
* 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 @@
* 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 @@
* URI for "wired charging started" sound.
* @hide
*/
+ @Readable
public static final String CHARGING_STARTED_SOUND = "charging_started_sound";
/**
@@ -9671,6 +10240,7 @@
* </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 @@
* in the power menu.
* @hide
*/
+ @Readable
public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu";
/**
@@ -9686,6 +10257,7 @@
*
* @hide
*/
+ @Readable
public static final String CUSTOM_BUGREPORT_HANDLER_APP = "custom_bugreport_handler_app";
/**
@@ -9694,29 +10266,34 @@
*
* @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 @@
* 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 @@
* 1 = CDMA Cell Broadcast SMS enabled
* @hide
*/
+ @Readable
public static final String CDMA_CELL_BROADCAST_SMS =
"cdma_cell_broadcast_sms";
@@ -9747,6 +10327,7 @@
* 2 = Roaming on any networks
* @hide
*/
+ @Readable
public static final String CDMA_ROAMING_MODE = "roaming_settings";
/**
@@ -9754,6 +10335,7 @@
* 1 = NV
* @hide
*/
+ @Readable
public static final String CDMA_SUBSCRIPTION_MODE = "subscription_mode";
/**
@@ -9763,44 +10345,49 @@
*
* @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 @@
* 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 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
/**
@@ -9833,6 +10422,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String EUICC_PROVISIONED = "euicc_provisioned";
/**
@@ -9848,6 +10438,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String EUICC_SUPPORTED_COUNTRIES = "euicc_supported_countries";
/**
@@ -9863,6 +10454,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String EUICC_UNSUPPORTED_COUNTRIES = "euicc_unsupported_countries";
/**
@@ -9871,6 +10463,7 @@
* (0 = false, 1 = true)
* @hide
*/
+ @Readable
public static final String DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES
= "force_resizable_activities";
@@ -9878,6 +10471,7 @@
* 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 @@
* 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 @@
* @hide
*/
@Deprecated
+ @Readable
public static final String DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM =
"enable_sizecompat_freeform";
@@ -9906,6 +10502,7 @@
* @hide
*/
@TestApi
+ @Readable
@SuppressLint("NoSettingsProvider")
public static final String DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW =
"enable_non_resizable_multi_window";
@@ -9917,6 +10514,7 @@
* (0 = false, 1 = true)
* @hide
*/
+ @Readable
public static final String DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR =
"render_shadows_in_compositor";
@@ -9925,6 +10523,7 @@
* (0 = false, 1 = true)
* @hide
*/
+ @Readable
public static final String DEVELOPMENT_USE_BLAST_ADAPTER_VR =
"use_blast_adapter_vr";
@@ -9933,6 +10532,7 @@
* (0 = false, 1 = true)
* @hide
*/
+ @Readable
public static final String DEVELOPMENT_USE_BLAST_ADAPTER_SV =
"use_blast_adapter_sv";
@@ -9942,21 +10542,24 @@
*
* @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 @@
* @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 @@
* @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 @@
* 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 @@
* 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 @@
*
* @hide
*/
+ @Readable
public static final String HDMI_CONTROL_AUTO_WAKEUP_ENABLED =
"hdmi_control_auto_wakeup_enabled";
@@ -10094,6 +10708,7 @@
*
* @hide
*/
+ @Readable
public static final String HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED =
"hdmi_control_auto_device_off_enabled";
@@ -10115,6 +10730,7 @@
*
* @hide
*/
+ @Readable
public static final String HDMI_CONTROL_SEND_STANDBY_ON_SLEEP =
"hdmi_control_send_standby_on_sleep";
@@ -10122,6 +10738,7 @@
* 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 @@
*
* @hide
*/
+ @Readable
public static final String LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS =
"location_background_throttle_interval_ms";
@@ -10139,6 +10757,7 @@
* 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 @@
* 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 @@
* @hide
*/
@TestApi
+ @Readable
public static final String LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST =
"location_ignore_settings_package_whitelist";
@@ -10163,23 +10784,26 @@
* (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 @@
* (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 @@
* (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} */
+ @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} */
- 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} */
+ @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} */
- 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} */
+ @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} */
- 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} */
+ @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} */
- 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} */
+ @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} */
- public static final String NETPOLICY_OVERRIDE_ENABLED = "netpolicy_override_enabled";
+ /** {@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 @@
* 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 @@
* 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 @@
* build config value.
* @hide
*/
+ @Readable
public static final String TCP_DEFAULT_INIT_RWND = "tcp_default_init_rwnd";
/**
@@ -10530,6 +11225,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String TETHER_SUPPORTED = "tether_supported";
/**
@@ -10537,6 +11233,7 @@
* which defaults to false.
* @hide
*/
+ @Readable
public static final String TETHER_DUN_REQUIRED = "tether_dun_required";
/**
@@ -10548,6 +11245,7 @@
* 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 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled";
/**
@@ -10567,6 +11266,7 @@
* is interpreted as |false|.
* @hide
*/
+ @Readable
public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER =
"tether_enable_legacy_dhcp_server";
@@ -10581,6 +11281,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
/**
@@ -10591,62 +11292,71 @@
* @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 @@
*
* @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 @@
* 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 @@
* 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 @@
*
* @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 @@
* 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 @@
* @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 @@
* @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 @@
* 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 @@
*/
@Deprecated
@SystemApi
+ @Readable
public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled";
/**
@@ -10842,6 +11575,7 @@
* Type: int (0 for false, 1 for true)
* @hide
*/
+ @Readable
public static final String WIFI_MIGRATION_COMPLETED = "wifi_migration_completed";
/**
@@ -10850,6 +11584,7 @@
* 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 @@
* Type: long
* @hide
*/
+ @Readable
public static final String SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS =
"speed_label_cache_eviction_age_millis";
@@ -10877,6 +11613,8 @@
* @hide
* @deprecated To be removed.
*/
+ @Deprecated
+ @Readable
public static final String NETWORK_RECOMMENDATIONS_ENABLED =
"network_recommendations_enabled";
@@ -10890,6 +11628,7 @@
* Type: string - package name
* @hide
*/
+ @Readable
public static final String NETWORK_RECOMMENDATIONS_PACKAGE =
"network_recommendations_package";
@@ -10901,6 +11640,7 @@
* @hide
*/
@TestApi
+ @Readable
public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
/**
@@ -10910,6 +11650,7 @@
* Type: long
* @hide
*/
+ @Readable
public static final String RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS =
"recommended_network_evaluator_cache_expiry_ms";
@@ -10921,6 +11662,8 @@
* @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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* @hide
*/
@Deprecated
+ @Readable
public static final String WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED =
"wifi_connected_mac_randomization_enabled";
@@ -11051,24 +11811,28 @@
* @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 @@
* 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 @@
*
* @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 @@
* 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 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
+ @Readable
public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices";
/**
@@ -11202,10 +11978,12 @@
*
* @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 @@
*
* @hide
*/
+ @Readable
public static final String SEND_ACTION_APP_ERROR = "send_action_app_error";
/**
@@ -11224,6 +12003,7 @@
*
* @hide
*/
+ @Readable
public static final String DROPBOX_AGE_SECONDS = "dropbox_age_seconds";
/**
@@ -11232,6 +12012,7 @@
*
* @hide
*/
+ @Readable
public static final String DROPBOX_MAX_FILES = "dropbox_max_files";
/**
@@ -11240,6 +12021,7 @@
*
* @hide
*/
+ @Readable
public static final String DROPBOX_QUOTA_KB = "dropbox_quota_kb";
/**
@@ -11248,6 +12030,7 @@
*
* @hide
*/
+ @Readable
public static final String DROPBOX_QUOTA_PERCENT = "dropbox_quota_percent";
/**
@@ -11256,6 +12039,7 @@
*
* @hide
*/
+ @Readable
public static final String DROPBOX_RESERVE_PERCENT = "dropbox_reserve_percent";
/**
@@ -11263,6 +12047,7 @@
*
* @hide
*/
+ @Readable
public static final String DROPBOX_TAG_PREFIX = "dropbox:";
/**
@@ -11273,6 +12058,7 @@
*
* @hide
*/
+ @Readable
public static final String ERROR_LOGCAT_PREFIX = "logcat_for_";
/**
@@ -11286,6 +12072,7 @@
*
* @hide
*/
+ @Readable
public static final String MAX_ERROR_BYTES_PREFIX = "max_error_bytes_for_";
/**
@@ -11294,6 +12081,7 @@
*
* @hide
*/
+ @Readable
public static final String SYS_FREE_STORAGE_LOG_INTERVAL = "sys_free_storage_log_interval";
/**
@@ -11303,6 +12091,7 @@
*
* @hide
*/
+ @Readable
public static final String
DISK_FREE_CHANGE_REPORTING_THRESHOLD = "disk_free_change_reporting_threshold";
@@ -11315,6 +12104,7 @@
*
* @hide
*/
+ @Readable
public static final String
SYS_STORAGE_THRESHOLD_PERCENTAGE = "sys_storage_threshold_percentage";
@@ -11326,6 +12116,7 @@
*
* @hide
*/
+ @Readable
public static final String
SYS_STORAGE_THRESHOLD_MAX_BYTES = "sys_storage_threshold_max_bytes";
@@ -11336,6 +12127,7 @@
*
* @hide
*/
+ @Readable
public static final String
SYS_STORAGE_FULL_THRESHOLD_BYTES = "sys_storage_full_threshold_bytes";
@@ -11345,6 +12137,7 @@
*
* @hide
*/
+ @Readable
public static final String
SYS_STORAGE_CACHE_PERCENTAGE = "sys_storage_cache_percentage";
@@ -11354,6 +12147,7 @@
*
* @hide
*/
+ @Readable
public static final String
SYS_STORAGE_CACHE_MAX_BYTES = "sys_storage_cache_max_bytes";
@@ -11363,6 +12157,7 @@
*
* @hide
*/
+ @Readable
public static final String
SYNC_MAX_RETRY_DELAY_IN_SECONDS = "sync_max_retry_delay_in_seconds";
@@ -11372,6 +12167,7 @@
*
* @hide
*/
+ @Readable
public static final String CONNECTIVITY_CHANGE_DELAY = "connectivity_change_delay";
@@ -11381,7 +12177,7 @@
*
* @hide
*/
-
+ @Readable
public static final String CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS =
"connectivity_sampling_interval_in_seconds";
@@ -11391,6 +12187,7 @@
*
* @hide
*/
+ @Readable
public static final String PAC_CHANGE_DELAY = "pac_change_delay";
/**
@@ -11423,6 +12220,7 @@
* 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 @@
* @hide
*/
@Deprecated
+ @Readable
public static final String
CAPTIVE_PORTAL_DETECTION_ENABLED = "captive_portal_detection_enabled";
@@ -11443,6 +12242,7 @@
*
* @hide
*/
+ @Readable
public static final String CAPTIVE_PORTAL_SERVER = "captive_portal_server";
/**
@@ -11451,6 +12251,7 @@
*
* @hide
*/
+ @Readable
public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url";
/**
@@ -11459,6 +12260,7 @@
*
* @hide
*/
+ @Readable
public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url";
/**
@@ -11467,6 +12269,7 @@
*
* @hide
*/
+ @Readable
public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url";
/**
@@ -11475,6 +12278,7 @@
*
* @hide
*/
+ @Readable
public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS =
"captive_portal_other_fallback_urls";
@@ -11484,6 +12288,7 @@
* by "@@,@@".
* @hide
*/
+ @Readable
public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS =
"captive_portal_fallback_probe_specs";
@@ -11494,6 +12299,7 @@
*
* @hide
*/
+ @Readable
public static final String CAPTIVE_PORTAL_USE_HTTPS = "captive_portal_use_https";
/**
@@ -11502,6 +12308,7 @@
*
* @hide
*/
+ @Readable
public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent";
/**
@@ -11509,6 +12316,7 @@
*
* @hide
*/
+ @Readable
public static final String DATA_STALL_RECOVERY_ON_BAD_NETWORK =
"data_stall_recovery_on_bad_network";
@@ -11517,6 +12325,7 @@
*
* @hide
*/
+ @Readable
public static final String MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS =
"min_duration_between_recovery_steps";
/**
@@ -11524,6 +12333,7 @@
*
* @hide
*/
+ @Readable
public static final String NSD_ON = "nsd_on";
/**
@@ -11531,6 +12341,7 @@
*
* @hide
*/
+ @Readable
public static final String SET_INSTALL_LOCATION = "set_install_location";
/**
@@ -11540,6 +12351,7 @@
* 2 = sdcard
* @hide
*/
+ @Readable
public static final String DEFAULT_INSTALL_LOCATION = "default_install_location";
/**
@@ -11548,6 +12360,7 @@
*
* @hide
*/
+ @Readable
public static final String
INET_CONDITION_DEBOUNCE_UP_DELAY = "inet_condition_debounce_up_delay";
@@ -11557,10 +12370,12 @@
*
* @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 @@
* 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 @@
*
* @hide
*/
+ @Readable
public static final String GLOBAL_HTTP_PROXY_HOST = "global_http_proxy_host";
/**
@@ -11582,6 +12399,7 @@
*
* @hide
*/
+ @Readable
public static final String GLOBAL_HTTP_PROXY_PORT = "global_http_proxy_port";
/**
@@ -11593,6 +12411,7 @@
*
* @hide
*/
+ @Readable
public static final String
GLOBAL_HTTP_PROXY_EXCLUSION_LIST = "global_http_proxy_exclusion_list";
@@ -11600,6 +12419,7 @@
* 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 @@
*
* @hide
*/
+ @Readable
public static final String SET_GLOBAL_HTTP_PROXY = "set_global_http_proxy";
/**
@@ -11616,6 +12437,7 @@
*
* @hide
*/
+ @Readable
public static final String DEFAULT_DNS_SERVER = "default_dns_server";
/**
@@ -11628,11 +12450,13 @@
*
* @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 @@
*
* {@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 @@
*
* {@hide}
*/
+ @Readable
public static final String
ENABLE_RADIO_BUG_DETECTION = "enable_radio_bug_detection";
@@ -11700,6 +12539,7 @@
*
* {@hide}
*/
+ @Readable
public static final String
RADIO_BUG_WAKELOCK_TIMEOUT_COUNT_THRESHOLD =
"radio_bug_wakelock_timeout_count_threshold";
@@ -11709,6 +12549,7 @@
*
* {@hide}
*/
+ @Readable
public static final String
RADIO_BUG_SYSTEM_ERROR_COUNT_THRESHOLD =
"radio_bug_system_error_count_threshold";
@@ -11755,6 +12596,7 @@
* @hide
* @see com.android.server.am.ActivityManagerConstants
*/
+ @Readable
public static final String ACTIVITY_MANAGER_CONSTANTS = "activity_manager_constants";
/**
@@ -11763,6 +12605,7 @@
* Default: 1
* @hide
*/
+ @Readable
public static final String ACTIVITY_STARTS_LOGGING_ENABLED
= "activity_starts_logging_enabled";
@@ -11772,6 +12615,7 @@
* Default: 1
* @hide
*/
+ @Readable
public static final String FOREGROUND_SERVICE_STARTS_LOGGING_ENABLED =
"foreground_service_starts_logging_enabled";
@@ -11779,6 +12623,7 @@
* @hide
* @see com.android.server.appbinding.AppBindingConstants
*/
+ @Readable
public static final String APP_BINDING_CONSTANTS = "app_binding_constants";
/**
@@ -11801,6 +12646,7 @@
* @see com.android.server.AppOpsService.Constants
*/
@TestApi
+ @Readable
public static final String APP_OPS_CONSTANTS = "app_ops_constants";
/**
@@ -11836,6 +12682,7 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@TestApi
+ @Readable
public static final String BATTERY_SAVER_CONSTANTS = "battery_saver_constants";
/**
@@ -11853,6 +12700,7 @@
*
* @hide
*/
+ @Readable
public static final String BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS =
"battery_saver_device_specific_constants";
@@ -11882,6 +12730,7 @@
* </pre>
* @hide
*/
+ @Readable
public static final String BATTERY_TIP_CONSTANTS = "battery_tip_constants";
/**
@@ -11907,6 +12756,7 @@
* </pre>
* @hide
*/
+ @Readable
public static final String ANOMALY_DETECTION_CONSTANTS = "anomaly_detection_constants";
/**
@@ -11914,6 +12764,7 @@
* current version is 1.
* @hide
*/
+ @Readable
public static final String ANOMALY_CONFIG_VERSION = "anomaly_config_version";
/**
@@ -11921,6 +12772,7 @@
* {@link android.app.StatsManager}.
* @hide
*/
+ @Readable
public static final String ANOMALY_CONFIG = "anomaly_config";
/**
@@ -11940,6 +12792,7 @@
* </pre>
* @hide
*/
+ @Readable
public static final String ALWAYS_ON_DISPLAY_CONSTANTS = "always_on_display_constants";
/**
@@ -11950,6 +12803,7 @@
* Any other value defaults to enabled.
* @hide
*/
+ @Readable
public static final String SYS_UIDCPUPOWER = "sys_uidcpupower";
/**
@@ -11961,6 +12815,7 @@
* Any other value defaults to disabled.
* @hide
*/
+ @Readable
public static final String SYS_TRACED = "sys_traced";
/**
@@ -11969,6 +12824,7 @@
*
* @hide
*/
+ @Readable
public static final String FPS_DEVISOR = "fps_divisor";
/**
@@ -11978,6 +12834,7 @@
*
* @hide
*/
+ @Readable
public static final String DISPLAY_PANEL_LPM = "display_panel_lpm";
/**
@@ -11992,6 +12849,7 @@
* 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 @@
* 0 = disable, 1 = enable.
* @hide
*/
+ @Readable
public static final String ART_VERIFIER_VERIFY_DEBUGGABLE =
"art_verifier_verify_debuggable";
@@ -12019,6 +12878,7 @@
* @hide
* @see com.android.server.power.PowerManagerConstants
*/
+ @Readable
public static final String POWER_MANAGER_CONSTANTS = "power_manager_constants";
/**
@@ -12044,6 +12904,7 @@
* @hide
* @see com.android.server.pm.ShortcutService.ConfigConstants
*/
+ @Readable
public static final String SHORTCUT_MANAGER_CONSTANTS = "shortcut_manager_constants";
/**
@@ -12061,6 +12922,7 @@
* @hide
* see also com.android.server.devicepolicy.DevicePolicyConstants
*/
+ @Readable
public static final String DEVICE_POLICY_CONSTANTS = "device_policy_constants";
/**
@@ -12097,6 +12959,7 @@
* @hide
* see also android.view.textclassifier.TextClassificationConstants
*/
+ @Readable
public static final String TEXT_CLASSIFIER_CONSTANTS = "text_classifier_constants";
/**
@@ -12121,6 +12984,7 @@
* @hide
* see also com.android.internal.os.BatteryStatsImpl.Constants
*/
+ @Readable
public static final String BATTERY_STATS_CONSTANTS = "battery_stats_constants";
/**
@@ -12131,6 +12995,7 @@
* @hide
* @see com.android.server.content.SyncManagerConstants
*/
+ @Readable
public static final String SYNC_MANAGER_CONSTANTS = "sync_manager_constants";
/**
@@ -12150,6 +13015,7 @@
*
* @hide
*/
+ @Readable
public static final String BROADCAST_FG_CONSTANTS = "bcast_fg_constants";
/**
@@ -12160,6 +13026,7 @@
*
* @hide
*/
+ @Readable
public static final String BROADCAST_BG_CONSTANTS = "bcast_bg_constants";
/**
@@ -12170,6 +13037,7 @@
*
* @hide
*/
+ @Readable
public static final String BROADCAST_OFFLOAD_CONSTANTS = "bcast_offload_constants";
/**
@@ -12182,6 +13050,7 @@
* @see #ADAPTIVE_BATTERY_MANAGEMENT_ENABLED
*/
@SystemApi
+ @Readable
public static final String APP_STANDBY_ENABLED = "app_standby_enabled";
/**
@@ -12192,6 +13061,7 @@
* @hide
* @see #APP_STANDBY_ENABLED
*/
+ @Readable
public static final String ADAPTIVE_BATTERY_MANAGEMENT_ENABLED =
"adaptive_battery_management_enabled";
@@ -12203,6 +13073,7 @@
*
* @hide
*/
+ @Readable
public static final String ENABLE_RESTRICTED_BUCKET = "enable_restricted_bucket";
/**
@@ -12220,6 +13091,7 @@
*
* @hide
*/
+ @Readable
public static final String APP_AUTO_RESTRICTION_ENABLED =
"app_auto_restriction_enabled";
@@ -12229,6 +13101,7 @@
* Default: 1
* @hide
*/
+ @Readable
public static final String FORCED_APP_STANDBY_ENABLED = "forced_app_standby_enabled";
/**
@@ -12237,6 +13110,7 @@
* 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 @@
* 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 @@
* 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 @@
* Default: 1
* @hide
*/
+ @Readable
public static final String WIFI_ON_WHEN_PROXY_DISCONNECTED
= "wifi_on_when_proxy_disconnected";
@@ -12282,6 +13159,7 @@
* Type: string
* @hide
*/
+ @Readable
public static final String TIME_ONLY_MODE_CONSTANTS
= "time_only_mode_constants";
@@ -12292,6 +13170,7 @@
* Default: 0
* @hide
*/
+ @Readable
public static final String UNGAZE_SLEEP_ENABLED = "ungaze_sleep_enabled";
/**
@@ -12300,6 +13179,7 @@
* Default: 0
* @hide
*/
+ @Readable
public static final String NETWORK_WATCHLIST_ENABLED = "network_watchlist_enabled";
/**
@@ -12308,6 +13188,7 @@
* Default: 1
* @hide
*/
+ @Readable
public static final String SHOW_HIDDEN_LAUNCHER_ICON_APPS_ENABLED =
"show_hidden_icon_apps_enabled";
@@ -12317,6 +13198,7 @@
* Default: 0
* @hide
*/
+ @Readable
public static final String SHOW_NEW_APP_INSTALLED_NOTIFICATION_ENABLED =
"show_new_app_installed_notification_enabled";
@@ -12330,6 +13212,7 @@
*
* @hide
*/
+ @Readable
public static final String KEEP_PROFILE_IN_BACKGROUND = "keep_profile_in_background";
/**
@@ -12351,6 +13234,7 @@
*
* @hide
*/
+ @Readable
public static final String ADB_ALLOWED_CONNECTION_TIME =
"adb_allowed_connection_time";
@@ -12358,12 +13242,14 @@
* 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 @@
* 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 @@
*
* @hide
*/
+ @Readable
public static final String FANCY_IME_ANIMATIONS = "fancy_ime_animations";
/**
@@ -12387,6 +13275,7 @@
* TODO: remove this settings before code freeze (bug/1907571)
* @hide
*/
+ @Readable
public static final String COMPATIBILITY_MODE = "compatibility_mode";
/**
@@ -12396,6 +13285,7 @@
* 2 = Vibrate
* @hide
*/
+ @Readable
public static final String EMERGENCY_TONE = "emergency_tone";
/**
@@ -12404,6 +13294,7 @@
* boolean (1 or 0).
* @hide
*/
+ @Readable
public static final String CALL_AUTO_RETRY = "call_auto_retry";
/**
@@ -12411,6 +13302,7 @@
* The value is a boolean (1 or 0).
* @hide
*/
+ @Readable
public static final String EMERGENCY_AFFORDANCE_NEEDED = "emergency_affordance_needed";
/**
@@ -12420,6 +13312,7 @@
*
* @hide
*/
+ @Readable
public static final String ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS =
"enable_automatic_system_server_heap_dumps";
@@ -12428,18 +13321,21 @@
* @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 @@
* 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 @@
* to dumpable apps that opt-in.
* @hide
*/
+ @Readable
public static final String ANGLE_DEBUG_PACKAGE = "angle_debug_package";
/**
@@ -12468,12 +13367,14 @@
* 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 @@
* 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 @@
* List of package names that should check ANGLE rules
* @hide
*/
+ @Readable
public static final String ANGLE_ALLOWLIST = "angle_allowlist";
/**
@@ -12497,6 +13400,7 @@
* e.g. feature1:feature2:feature3,feature1:feature3:feature5
* @hide
*/
+ @Readable
public static final String ANGLE_EGL_FEATURES = "angle_egl_features";
/**
@@ -12504,6 +13408,7 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* updatable production driver.
* @hide
*/
+ @Readable
public static final String UPDATABLE_DRIVER_PRODUCTION_DENYLISTS =
"updatable_driver_production_denylists";
@@ -12561,6 +13472,7 @@
* i.e. <apk1>,<apk2>,...,<apkN>
* @hide
*/
+ @Readable
public static final String UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST =
"updatable_driver_production_allowlist";
@@ -12570,6 +13482,7 @@
* i.e. <lib1>:<lib2>:...:<libN>
* @hide
*/
+ @Readable
public static final String UPDATABLE_DRIVER_SPHAL_LIBRARIES =
"updatable_driver_sphal_libraries";
@@ -12578,6 +13491,7 @@
* i.e. <layer1>:<layer2>:...:<layerN>
* @hide
*/
+ @Readable
public static final String GPU_DEBUG_LAYERS = "gpu_debug_layers";
/**
@@ -12585,12 +13499,14 @@
* 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 @@
* {@link android.os.Build.VERSION_CODES#N_MR1}.
*/
@Deprecated
+ @Readable
public static final String SHOW_PROCESSES = "show_processes";
/**
@@ -12607,6 +13524,7 @@
* @hide
*/
@TestApi
+ @Readable
public static final String LOW_POWER_MODE = "low_power";
/**
@@ -12615,6 +13533,7 @@
* @hide
*/
@TestApi
+ @Readable
public static final String LOW_POWER_MODE_STICKY = "low_power_sticky";
/**
@@ -12624,6 +13543,7 @@
*
* @hide
*/
+ @Readable
public static final String LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL =
"low_power_sticky_auto_disable_level";
@@ -12633,6 +13553,7 @@
*
* @hide
*/
+ @Readable
public static final String LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED =
"low_power_sticky_auto_disable_enabled";
@@ -12646,6 +13567,7 @@
* @see android.os.PowerManager#getPowerSaveModeTrigger()
* @hide
*/
+ @Readable
public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level";
/**
@@ -12656,6 +13578,7 @@
* @hide
*/
@TestApi
+ @Readable
public static final String AUTOMATIC_POWER_SAVE_MODE = "automatic_power_save_mode";
/**
@@ -12666,6 +13589,7 @@
* @hide
*/
@TestApi
+ @Readable
public static final String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD =
"dynamic_power_savings_disable_threshold";
@@ -12676,6 +13600,7 @@
* @hide
*/
@TestApi
+ @Readable
public static final String DYNAMIC_POWER_SAVINGS_ENABLED = "dynamic_power_savings_enabled";
/**
@@ -12687,6 +13612,7 @@
* @hide
*/
@Deprecated
+ @Readable
public static final String TIME_REMAINING_ESTIMATE_MILLIS =
"time_remaining_estimate_millis";
@@ -12700,6 +13626,7 @@
* @hide
*/
@Deprecated
+ @Readable
public static final String TIME_REMAINING_ESTIMATE_BASED_ON_USAGE =
"time_remaining_estimate_based_on_usage";
@@ -12712,6 +13639,7 @@
* @hide
*/
@Deprecated
+ @Readable
public static final String AVERAGE_TIME_TO_DISCHARGE = "average_time_to_discharge";
/**
@@ -12723,6 +13651,7 @@
* @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 @@
*
* @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 @@
* 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 @@
* @hide
*/
@TestApi
+ @Readable
public static final String HIDE_ERROR_DIALOGS = "hide_error_dialogs";
/**
@@ -12763,6 +13696,7 @@
* 1 = enabled
* @hide
*/
+ @Readable
public static final String DOCK_AUDIO_MEDIA_ENABLED = "dock_audio_media_enabled";
/**
@@ -12822,6 +13756,7 @@
* ENCODED_SURROUND_OUTPUT_MANUAL
* @hide
*/
+ @Readable
public static final String ENCODED_SURROUND_OUTPUT = "encoded_surround_output";
/**
@@ -12833,6 +13768,7 @@
*
* @hide
*/
+ @Readable
public static final String ENCODED_SURROUND_OUTPUT_ENABLED_FORMATS =
"encoded_surround_output_enabled_formats";
@@ -12840,36 +13776,42 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 @@
*
* @hide
*/
+ @Readable
public static final String LOW_BATTERY_SOUND_TIMEOUT = "low_battery_sound_timeout";
/**
@@ -12987,6 +13945,7 @@
*
* @hide
*/
+ @Readable
public static final String WIFI_BOUNCE_DELAY_OVERRIDE_MS = "wifi_bounce_delay_override_ms";
/**
@@ -12996,6 +13955,7 @@
*
* @hide
*/
+ @Readable
public static final String POLICY_CONTROL = "policy_control";
/**
@@ -13003,6 +13963,7 @@
*
* @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 @@
*
* @hide
*/
+ @Readable
public static final String BLOCKED_SLICES = "blocked_slices";
/**
@@ -13022,6 +13984,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String ZEN_MODE = "zen_mode";
/** @hide */
@@ -13061,6 +14024,7 @@
*
* @hide
*/
+ @Readable
public static final String ZEN_MODE_RINGER_LEVEL = "zen_mode_ringer_level";
/**
@@ -13069,6 +14033,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String ZEN_MODE_CONFIG_ETAG = "zen_mode_config_etag";
/**
@@ -13098,6 +14063,7 @@
* @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 @@
/**
* The name of the device
*/
+ @Readable
public static final String DEVICE_NAME = "device_name";
/**
@@ -13119,6 +14086,7 @@
* Type: int (0 for false, 1 for true)
* @hide
*/
+ @Readable
public static final String NETWORK_SCORING_PROVISIONED = "network_scoring_provisioned";
/**
@@ -13130,6 +14098,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String REQUIRE_PASSWORD_TO_DECRYPT = "require_password_to_decrypt";
/**
@@ -13143,6 +14112,7 @@
* {@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 @@
* @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 @@
* {@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 @@
* @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 @@
* instead.
*/
@Deprecated
+ @Readable
public static final String WFC_IMS_ROAMING_MODE =
Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_MODE;
@@ -13203,6 +14177,7 @@
* instead
*/
@Deprecated
+ @Readable
public static final String WFC_IMS_ROAMING_ENABLED =
Telephony.SimInfo.COLUMN_WFC_IMS_ROAMING_ENABLED;
@@ -13213,6 +14188,7 @@
* Type: int (0 for false, 1 for true)
* @hide
*/
+ @Readable
public static final String LTE_SERVICE_FORCED = "lte_service_forced";
@@ -13222,6 +14198,7 @@
* See WindowManagerPolicy.WindowManagerFuncs
* @hide
*/
+ @Readable
public static final String LID_BEHAVIOR = "lid_behavior";
/**
@@ -13230,6 +14207,7 @@
* Type: int
* @hide
*/
+ @Readable
public static final String EPHEMERAL_COOKIE_MAX_SIZE_BYTES =
"ephemeral_cookie_max_size_bytes";
@@ -13241,6 +14219,7 @@
*
* @hide
*/
+ @Readable
public static final String ENABLE_EPHEMERAL_FEATURE = "enable_ephemeral_feature";
/**
@@ -13251,6 +14230,7 @@
*
* @hide
*/
+ @Readable
public static final String INSTANT_APP_DEXOPT_ENABLED = "instant_app_dexopt_enabled";
/**
@@ -13259,6 +14239,7 @@
* Type: long
* @hide
*/
+ @Readable
public static final String INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD =
"installed_instant_app_min_cache_period";
@@ -13268,6 +14249,7 @@
* Type: long
* @hide
*/
+ @Readable
public static final String INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD =
"installed_instant_app_max_cache_period";
@@ -13277,6 +14259,7 @@
* Type: long
* @hide
*/
+ @Readable
public static final String UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD =
"uninstalled_instant_app_min_cache_period";
@@ -13286,6 +14269,7 @@
* Type: long
* @hide
*/
+ @Readable
public static final String UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD =
"uninstalled_instant_app_max_cache_period";
@@ -13295,6 +14279,7 @@
* 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 @@
* 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 @@
* <p>
* Type: int
*/
+ @Readable
public static final String BOOT_COUNT = "boot_count";
/**
@@ -13322,6 +14309,7 @@
* before the user restrictions are loaded.
* @hide
*/
+ @Readable
public static final String SAFE_BOOT_DISALLOWED = "safe_boot_disallowed";
/**
@@ -13333,6 +14321,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String DEVICE_DEMO_MODE = "device_demo_mode";
/**
@@ -13342,6 +14331,7 @@
*
* @hide
*/
+ @Readable
public static final String NETWORK_ACCESS_TIMEOUT_MS = "network_access_timeout_ms";
/**
@@ -13352,6 +14342,7 @@
*
* @hide
*/
+ @Readable
public static final String DATABASE_DOWNGRADE_REASON = "database_downgrade_reason";
/**
@@ -13362,6 +14353,7 @@
*
* @hide
*/
+ @Readable
public static final String DATABASE_CREATION_BUILDID = "database_creation_buildid";
/**
@@ -13370,6 +14362,7 @@
*
* @hide
*/
+ @Readable
public static final String CONTACTS_DATABASE_WAL_ENABLED = "contacts_database_wal_enabled";
/**
@@ -13377,6 +14370,7 @@
*
* @hide
*/
+ @Readable
public static final String LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED =
"location_settings_link_to_permissions_enabled";
@@ -13387,6 +14381,7 @@
*
* @hide
*/
+ @Readable
public static final String EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS =
"euicc_removing_invisible_profiles_timeout_millis";
@@ -13396,6 +14391,7 @@
*
* @hide
*/
+ @Readable
public static final String EUICC_FACTORY_RESET_TIMEOUT_MILLIS =
"euicc_factory_reset_timeout_millis";
@@ -13405,6 +14401,7 @@
*
* @hide
*/
+ @Readable
public static final String EUICC_SWITCH_SLOT_TIMEOUT_MILLIS =
"euicc_switch_slot_timeout_millis";
@@ -13414,6 +14411,7 @@
*
* @hide
*/
+ @Readable
public static final String ENABLE_MULTI_SLOT_TIMEOUT_MILLIS =
"enable_multi_slot_timeout_millis";
@@ -13423,6 +14421,7 @@
*
* @hide
*/
+ @Readable
public static final String STORAGE_SETTINGS_CLOBBER_THRESHOLD =
"storage_settings_clobber_threshold";
@@ -13432,6 +14431,7 @@
*
* @hide
*/
+ @Readable
public static final String OVERRIDE_SETTINGS_PROVIDER_RESTORE_ANY_VERSION =
"override_settings_provider_restore_any_version";
/**
@@ -13442,6 +14442,7 @@
*
* @hide
*/
+ @Readable
public static final String CHAINED_BATTERY_ATTRIBUTION_ENABLED =
"chained_battery_attribution_enabled";
@@ -13454,6 +14455,7 @@
*
* @hide
*/
+ @Readable
public static final String ENABLE_ADB_INCREMENTAL_INSTALL_DEFAULT =
"enable_adb_incremental_install_default";
@@ -13471,6 +14473,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES =
"autofill_compat_mode_allowed_packages";
@@ -13484,6 +14487,7 @@
*
* @hide
*/
+ @Readable
public static final String AUTOFILL_LOGGING_LEVEL = "autofill_logging_level";
/**
@@ -13491,6 +14495,7 @@
*
* @hide
*/
+ @Readable
public static final String AUTOFILL_MAX_PARTITIONS_SIZE = "autofill_max_partitions_size";
/**
@@ -13499,6 +14504,7 @@
*
* @hide
*/
+ @Readable
public static final String AUTOFILL_MAX_VISIBLE_DATASETS = "autofill_max_visible_datasets";
/**
@@ -13507,6 +14513,7 @@
* @hide
*/
@TestApi
+ @Readable
public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS =
"hidden_api_blacklist_exemptions";
@@ -13519,14 +14526,16 @@
* @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 @@
*
* @hide
*/
+ @Readable
public static final String SIGNED_CONFIG_VERSION = "signed_config_version";
/**
@@ -13544,6 +14554,7 @@
*
* @hide
*/
+ @Readable
public static final String SOUND_TRIGGER_DETECTION_SERVICE_OP_TIMEOUT =
"sound_trigger_detection_service_op_timeout";
@@ -13553,6 +14564,7 @@
*
* @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 @@
* Indicates whether aware is available in the current location.
* @hide
*/
+ @Readable
public static final String AWARE_ALLOWED = "aware_allowed";
/**
@@ -13568,6 +14581,7 @@
* Used by PhoneWindowManager.
* @hide
*/
+ @Readable
public static final String POWER_BUTTON_LONG_PRESS =
"power_button_long_press";
@@ -13577,31 +14591,10 @@
* Used by PhoneWindowManager.
* @hide
*/
+ @Readable
public static final String POWER_BUTTON_VERY_LONG_PRESS =
"power_button_very_long_press";
-
- /**
- * Keyguard should be on the left hand side of the screen, for wide screen layouts.
- *
- * @hide
- */
- public static final int ONE_HANDED_KEYGUARD_SIDE_LEFT = 0;
-
- /**
- * Keyguard should be on the right hand side of the screen, for wide screen layouts.
- *
- * @hide
- */
- public static final int ONE_HANDED_KEYGUARD_SIDE_RIGHT = 1;
- /**
- * In one handed mode, which side the keyguard should be on. Allowable values are one of
- * the ONE_HANDED_KEYGUARD_SIDE_* constants.
- *
- * @hide
- */
- public static final String ONE_HANDED_KEYGUARD_SIDE = "one_handed_keyguard_side";
-
/**
* Keys we no longer back up under the current schema, but want to continue to
* process when restoring historical backup datasets.
@@ -13624,7 +14617,8 @@
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
@@ -13653,6 +14647,11 @@
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
@@ -14062,6 +15061,7 @@
* 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";
/**
@@ -14070,18 +15070,21 @@
* @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";
/**
@@ -14089,6 +15092,7 @@
* The value 1 - enable, 0 - disable
* @hide
*/
+ @Readable
public static final String MULTI_SIM_SMS_PROMPT = "multi_sim_sms_prompt";
/** User preferred subscriptions setting.
@@ -14098,6 +15102,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @Readable
public static final String[] MULTI_SIM_USER_PREFERRED_SUBS = {"user_preferred_sub1",
"user_preferred_sub2","user_preferred_sub3"};
@@ -14105,6 +15110,7 @@
* Which subscription is enabled for a physical slot.
* @hide
*/
+ @Readable
public static final String ENABLED_SUBSCRIPTION_FOR_SLOT = "enabled_subscription_for_slot";
/**
@@ -14112,6 +15118,7 @@
* The value 1 - enable, 0 - disable
* @hide
*/
+ @Readable
public static final String MODEM_STACK_ENABLED_FOR_SLOT = "modem_stack_enabled_for_slot";
/**
@@ -14119,6 +15126,7 @@
* The value 1 - enable, 0 - disable
* @hide
*/
+ @Readable
public static final String NEW_CONTACT_AGGREGATOR = "new_contact_aggregator";
/**
@@ -14128,12 +15136,14 @@
* @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";
/**
@@ -14141,6 +15151,7 @@
* The value 1 - enable, 0 - disable
* @hide
*/
+ @Readable
public static final String ENABLE_CELLULAR_ON_BOOT = "enable_cellular_on_boot";
/**
@@ -14149,6 +15160,7 @@
* Should be a float, and includes updates only.
* @hide
*/
+ @Readable
public static final String MAX_NOTIFICATION_ENQUEUE_RATE = "max_notification_enqueue_rate";
/**
@@ -14157,6 +15169,7 @@
* The value 1 - enable, 0 - disable
* @hide
*/
+ @Readable
public static final String SHOW_NOTIFICATION_CHANNEL_WARNINGS =
"show_notification_channel_warnings";
@@ -14164,6 +15177,7 @@
* Whether cell is enabled/disabled
* @hide
*/
+ @Readable
public static final String CELL_ON = "cell_on";
/**
@@ -14192,30 +15206,35 @@
* 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";
@@ -14223,6 +15242,7 @@
* 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";
@@ -14243,6 +15263,7 @@
* 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";
@@ -14254,6 +15275,7 @@
* The value 1 - enable, 0 - disable
* @hide
*/
+ @Readable
public static final String NOTIFICATION_FEEDBACK_ENABLED = "notification_feedback_enabled";
/**
@@ -14265,6 +15287,7 @@
*
* @hide
*/
+ @Readable
public static final String BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT =
"blocking_helper_dismiss_to_view_ratio";
@@ -14276,6 +15299,7 @@
*
* @hide
*/
+ @Readable
public static final String BLOCKING_HELPER_STREAK_LIMIT = "blocking_helper_streak_limit";
/**
@@ -14303,6 +15327,7 @@
*
* @hide
*/
+ @Readable
public static final String SQLITE_COMPATIBILITY_WAL_FLAGS =
"sqlite_compatibility_wal_flags";
@@ -14312,6 +15337,7 @@
* 1 = yes
* @hide
*/
+ @Readable
public static final String ENABLE_GNSS_RAW_MEAS_FULL_TRACKING =
"enable_gnss_raw_meas_full_tracking";
@@ -14323,6 +15349,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT =
"install_carrier_app_notification_persistent";
@@ -14334,6 +15361,7 @@
* @hide
*/
@SystemApi
+ @Readable
public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS =
"install_carrier_app_notification_sleep_millis";
@@ -14343,6 +15371,7 @@
* everything else is unspecified.
* @hide
*/
+ @Readable
public static final String ZRAM_ENABLED =
"zram_enabled";
@@ -14352,6 +15381,7 @@
* "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";
/**
@@ -14374,6 +15404,7 @@
* @see com.android.systemui.statusbar.policy.SmartReplyConstants
* @hide
*/
+ @Readable
public static final String SMART_REPLIES_IN_NOTIFICATIONS_FLAGS =
"smart_replies_in_notifications_flags";
@@ -14390,6 +15421,7 @@
* </pre>
* @hide
*/
+ @Readable
public static final String SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS =
"smart_suggestions_in_notifications_flags";
@@ -14399,6 +15431,7 @@
* @hide
*/
@TestApi
+ @Readable
@SuppressLint("NoSettingsProvider")
public static final String SHOW_FIRST_CRASH_DIALOG = "show_first_crash_dialog";
@@ -14406,6 +15439,7 @@
* 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";
/**
@@ -14413,6 +15447,7 @@
* this app.
* @hide
*/
+ @Readable
public static final String SHOW_MUTE_IN_CRASH_DIALOG = "show_mute_in_crash_dialog";
@@ -14468,6 +15503,7 @@
*
* @hide
*/
+ @Readable
public static final String BACKUP_AGENT_TIMEOUT_PARAMETERS =
"backup_agent_timeout_parameters";
@@ -14482,6 +15518,7 @@
*
* @hide
*/
+ @Readable
public static final String GNSS_SATELLITE_BLOCKLIST = "gnss_satellite_blocklist";
/**
@@ -14493,6 +15530,7 @@
*
* @hide
*/
+ @Readable
public static final String GNSS_HAL_LOCATION_REQUEST_DURATION_MILLIS =
"gnss_hal_location_request_duration_millis";
@@ -14509,6 +15547,7 @@
*
* @hide
*/
+ @Readable
public static final String BINDER_CALLS_STATS = "binder_calls_stats";
/**
@@ -14522,6 +15561,7 @@
*
* @hide
*/
+ @Readable
public static final String LOOPER_STATS = "looper_stats";
/**
@@ -14536,6 +15576,7 @@
*
* @hide
*/
+ @Readable
public static final String KERNEL_CPU_THREAD_READER = "kernel_cpu_thread_reader";
/**
@@ -14543,6 +15584,7 @@
* 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";
@@ -14552,6 +15594,7 @@
*
* @hide
*/
+ @Readable
public static final String APPOP_HISTORY_MODE = "mode";
/**
@@ -14561,6 +15604,7 @@
*
* @hide
*/
+ @Readable
public static final String APPOP_HISTORY_BASE_INTERVAL_MILLIS = "baseIntervalMillis";
/**
@@ -14569,6 +15613,7 @@
*
* @hide
*/
+ @Readable
public static final String APPOP_HISTORY_INTERVAL_MULTIPLIER = "intervalMultiplier";
/**
@@ -14590,6 +15635,7 @@
*
* @hide
*/
+ @Readable
public static final String APPOP_HISTORY_PARAMETERS =
"appop_history_parameters";
@@ -14607,6 +15653,7 @@
*
* @hide
*/
+ @Readable
public static final String AUTO_REVOKE_PARAMETERS =
"auto_revoke_parameters";
@@ -14618,6 +15665,7 @@
* @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";
@@ -14626,6 +15674,7 @@
*
* @hide
*/
+ @Readable
public static final String TEXT_CLASSIFIER_ACTION_MODEL_PARAMS =
"text_classifier_action_model_params";
@@ -14639,6 +15688,7 @@
*
* @hide
*/
+ @Readable
public static final String POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE =
"power_button_suppression_delay_after_gesture_wake";
@@ -14647,6 +15697,7 @@
*
* @hide
*/
+ @Readable
public static final String ADVANCED_BATTERY_USAGE_AMOUNT = "advanced_battery_usage_amount";
/**
@@ -14661,6 +15712,7 @@
* 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";
@@ -14671,6 +15723,7 @@
* 1: Enabled
* @hide
*/
+ @Readable
public static final String SHOW_PEOPLE_SPACE = "show_people_space";
/**
@@ -14681,6 +15734,7 @@
* 2: All conversations
* @hide
*/
+ @Readable
public static final String PEOPLE_SPACE_CONVERSATION_TYPE =
"people_space_conversation_type";
@@ -14691,6 +15745,7 @@
* 1: Enabled
* @hide
*/
+ @Readable
public static final String SHOW_NEW_NOTIF_DISMISS = "show_new_notif_dismiss";
/**
@@ -14705,6 +15760,7 @@
* 1: Enabled (All apps will receive the new rules)
* @hide
*/
+ @Readable
public static final String BACKPORT_S_NOTIF_RULES = "backport_s_notif_rules";
/**
@@ -14719,6 +15775,7 @@
* <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";
@@ -14732,6 +15789,7 @@
* <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";
@@ -14748,6 +15806,7 @@
*
* @hide
*/
+ @Readable
public static final String BLOCK_UNTRUSTED_TOUCHES_MODE = "block_untrusted_touches";
/**
@@ -14773,6 +15832,7 @@
*
* @hide
*/
+ @Readable
public static final String MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH =
"maximum_obscuring_opacity_for_touch";
@@ -14785,6 +15845,7 @@
* 1: enabled
* @hide
*/
+ @Readable
public static final String RESTRICTED_NETWORKING_MODE = "restricted_networking_mode";
}
@@ -14806,7 +15867,8 @@
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/rotationresolver/RotationResolverInternal.java b/core/java/android/rotationresolver/RotationResolverInternal.java
index db879a7..1f1a66c 100644
--- a/core/java/android/rotationresolver/RotationResolverInternal.java
+++ b/core/java/android/rotationresolver/RotationResolverInternal.java
@@ -46,7 +46,6 @@
* error is captured. {@link RotationResolverCallbackInternal}
* @param proposedRotation the screen rotation that is proposed by the system.
* @param currentRotation the current screen rotation.
- * @param packageName the package name of the current activity that is running in foreground.
* @param timeoutMillis the timeout in millisecond for the query. If the query doesn't get
* fulfilled within this amount of time. It will be discarded and the
* callback will receive a failure result code {@link
@@ -55,8 +54,7 @@
*/
public abstract void resolveRotation(@NonNull RotationResolverCallbackInternal callback,
@Surface.Rotation int proposedRotation, @Surface.Rotation int currentRotation,
- String packageName, @DurationMillisLong long timeoutMillis,
- @NonNull CancellationSignal cancellationSignal);
+ @DurationMillisLong long timeoutMillis, @NonNull CancellationSignal cancellationSignal);
/**
* Internal interfaces for the rotation resolver callback.
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index c39b8c5..a79b197 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -130,6 +130,15 @@
public static final int KM_TAG_ASSOCIATED_DATA = Tag.ASSOCIATED_DATA; // KM_BYTES | 1000;
public static final int KM_TAG_NONCE = Tag.NONCE; // KM_BYTES | 1001;
public static final int KM_TAG_MAC_LENGTH = Tag.MAC_LENGTH; // KM_UINT | 1003;
+ public static final int KM_TAG_RESET_SINCE_ID_ROTATION =
+ Tag.RESET_SINCE_ID_ROTATION; // KM_BOOL | 1004
+ public static final int KM_TAG_CONFIRMATION_TOKEN = Tag.CONFIRMATION_TOKEN; // KM_BYTES | 1005;
+ public static final int KM_TAG_CERTIFICATE_SERIAL = Tag.CERTIFICATE_SERIAL; // KM_UINT | 1006;
+ public static final int KM_TAG_CERTIFICATE_SUBJECT = Tag.CERTIFICATE_SUBJECT; // KM_UINT | 1007;
+ public static final int KM_TAG_CERTIFICATE_NOT_BEFORE =
+ Tag.CERTIFICATE_NOT_BEFORE; // KM_DATE | 1008;
+ public static final int KM_TAG_CERTIFICATE_NOT_AFTER =
+ Tag.CERTIFICATE_NOT_AFTER; // KM_DATE | 1009;
// Algorithm values.
public static final int KM_ALGORITHM_RSA = Algorithm.RSA;
@@ -317,6 +326,10 @@
ErrorCode.HARDWARE_TYPE_UNAVAILABLE; // -68;
public static final int KM_ERROR_DEVICE_LOCKED =
ErrorCode.DEVICE_LOCKED; // -72;
+ public static final int KM_ERROR_MISSING_NOT_BEFORE =
+ ErrorCode.MISSING_NOT_BEFORE; // -80;
+ public static final int KM_ERROR_MISSING_NOT_AFTER =
+ ErrorCode.MISSING_NOT_AFTER; // -80;
public static final int KM_ERROR_UNIMPLEMENTED =
ErrorCode.UNIMPLEMENTED; // -100;
public static final int KM_ERROR_VERSION_MISMATCH =
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index 44daeff..2b7cb1d 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -41,7 +41,7 @@
void onListenerHintsChanged(int hints);
void onInterruptionFilterChanged(int interruptionFilter);
- // companion device managers only
+ // companion device managers and assistants only
void onNotificationChannelModification(String pkgName, in UserHandle user, in NotificationChannel channel, int modificationType);
void onNotificationChannelGroupModification(String pkgName, in UserHandle user, in NotificationChannelGroup group, int modificationType);
diff --git a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
index 4ebaa96..ad49ffd 100644
--- a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
+++ b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
@@ -87,7 +87,9 @@
* Implementation for wrapping the opaque blob used for resume-on-reboot prior to
* reboot. The service should not assume any structure of the blob to be wrapped. The
* implementation should wrap the opaque blob in a reasonable time or throw {@link IOException}
- * if it's unable to complete the action.
+ * if it's unable to complete the action due to retry-able errors (e.g network errors)
+ * and {@link IllegalArgumentException} if {@code wrapBlob} fails due to fatal errors
+ * (e.g corrupted blob).
*
* @param blob The opaque blob with size on the order of 100 bytes.
* @param lifeTimeInMillis The life time of the blob. This must be strictly enforced by the
@@ -95,7 +97,8 @@
* this function after expiration should
* fail.
* @return Wrapped blob to be persisted across reboot with size on the order of 100 bytes.
- * @throws IOException if the implementation is unable to wrap the blob successfully.
+ * @throws IOException if the implementation is unable to wrap the blob successfully due to
+ * retry-able errors.
*/
@NonNull
public abstract byte[] onWrap(@NonNull byte[] blob, @DurationMillisLong long lifeTimeInMillis)
@@ -106,12 +109,13 @@
* operation would happen after reboot during direct boot mode (i.e before device is unlocked
* for the first time). The implementation should unwrap the wrapped blob in a reasonable time
* and returns the result or throw {@link IOException} if it's unable to complete the action
- * and {@link IllegalArgumentException} if {@code unwrapBlob} fails because the wrappedBlob is
- * stale.
+ * due to retry-able errors (e.g network error) and {@link IllegalArgumentException}
+ * if {@code unwrapBlob} fails due to fatal errors (e.g stale or corrupted blob).
*
* @param wrappedBlob The wrapped blob with size on the order of 100 bytes.
* @return Unwrapped blob used for resume-on-reboot with the size on the order of 100 bytes.
- * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully.
+ * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully
+ * due to retry-able errors.
*/
@NonNull
public abstract byte[] onUnwrap(@NonNull byte[] wrappedBlob) throws IOException;
diff --git a/core/java/android/service/rotationresolver/RotationResolutionRequest.java b/core/java/android/service/rotationresolver/RotationResolutionRequest.java
index 94a6052..8e76e2f 100644
--- a/core/java/android/service/rotationresolver/RotationResolutionRequest.java
+++ b/core/java/android/service/rotationresolver/RotationResolutionRequest.java
@@ -20,6 +20,7 @@
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.Surface;
/**
* This class represents a request to an {@link RotationResolverService}. The request contains
@@ -54,7 +55,7 @@
mTimeoutMillis = timeoutMillis;
}
- public int getProposedRotation() {
+ @Surface.Rotation public int getProposedRotation() {
return mProposedRotation;
}
diff --git a/core/java/android/service/rotationresolver/RotationResolverService.java b/core/java/android/service/rotationresolver/RotationResolverService.java
index 593a642..604dd0a 100644
--- a/core/java/android/service/rotationresolver/RotationResolverService.java
+++ b/core/java/android/service/rotationresolver/RotationResolverService.java
@@ -146,11 +146,8 @@
}
mPendingCallback = new RotationResolverCallbackWrapper(callback, this);
mCancellationSignal = CancellationSignal.fromTransport(transport);
- try {
- onResolveRotation(request, mCancellationSignal, mPendingCallback);
- } catch (UnsupportedOperationException e) {
- reportFailures(callback, ROTATION_RESULT_FAILURE_CANCELLED);
- }
+
+ onResolveRotation(request, mCancellationSignal, mPendingCallback);
}
@MainThread
diff --git a/core/java/android/service/storage/ExternalStorageService.java b/core/java/android/service/storage/ExternalStorageService.java
index 0123c36..a750b68 100644
--- a/core/java/android/service/storage/ExternalStorageService.java
+++ b/core/java/android/service/storage/ExternalStorageService.java
@@ -158,7 +158,7 @@
* @param volumeUuid uuid of the {@link StorageVolume} from which cache needs to be freed
* @param bytes number of bytes which need to be freed
*/
- public void onFreeCacheRequested(@NonNull UUID volumeUuid, @BytesLong long bytes) {
+ public void onFreeCache(@NonNull UUID volumeUuid, @BytesLong long bytes) throws IOException {
throw new UnsupportedOperationException("onFreeCacheRequested not implemented");
}
@@ -202,7 +202,7 @@
RemoteCallback callback) {
mHandler.post(() -> {
try {
- onFreeCacheRequested(StorageManager.convert(volumeUuid), bytes);
+ onFreeCache(StorageManager.convert(volumeUuid), bytes);
sendResult(sessionId, null /* throwable */, callback);
} catch (Throwable t) {
sendResult(sessionId, t, callback);
diff --git a/core/java/android/service/voice/VoiceInteractionServiceInfo.java b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
index f7710e6..ff03cc1 100644
--- a/core/java/android/service/voice/VoiceInteractionServiceInfo.java
+++ b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.pm.PackageManager;
@@ -44,6 +45,7 @@
private ServiceInfo mServiceInfo;
private String mSessionService;
private String mRecognitionService;
+ private String mHotwordDetectionService;
private String mSettingsActivity;
private boolean mSupportsAssist;
private boolean mSupportsLaunchFromKeyguard;
@@ -133,6 +135,8 @@
false);
mSupportsLocalInteraction = array.getBoolean(com.android.internal.
R.styleable.VoiceInteractionService_supportsLocalInteraction, false);
+ mHotwordDetectionService = array.getString(com.android.internal.R.styleable
+ .VoiceInteractionService_hotwordDetectionService);
array.recycle();
if (mSessionService == null) {
mParseError = "No sessionService specified";
@@ -181,4 +185,9 @@
public boolean getSupportsLocalInteraction() {
return mSupportsLocalInteraction;
}
+
+ @Nullable
+ public String getHotwordDetectionService() {
+ return mHotwordDetectionService;
+ }
}
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 16b45c3..7f45b38 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -475,9 +475,8 @@
mObjects.insertAt(0, dirs);
- final int baseLength = mBase.length();
- // Update from 0 characters to whatever the real text is
- reflow(mBase, 0, 0, baseLength);
+ // Update from 0 characters to whatever the displayed text is
+ reflow(mBase, 0, 0, mDisplay.length());
if (mBase instanceof Spannable) {
if (mWatcher == null)
@@ -485,6 +484,7 @@
// Strip out any watchers for other DynamicLayouts.
final Spannable sp = (Spannable) mBase;
+ final int baseLength = mBase.length();
final ChangeWatcher[] spans = sp.getSpans(0, baseLength, ChangeWatcher.class);
for (int i = 0; i < spans.length; i++) {
sp.removeSpan(spans[i]);
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 2de7558..aef185c 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -36,6 +36,7 @@
import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
@@ -155,6 +156,32 @@
}
};
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ FontConfig that = (FontConfig) o;
+ return mLastModifiedTimeMillis == that.mLastModifiedTimeMillis
+ && mConfigVersion == that.mConfigVersion
+ && Objects.equals(mFamilies, that.mFamilies)
+ && Objects.equals(mAliases, that.mAliases);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFamilies, mAliases, mLastModifiedTimeMillis, mConfigVersion);
+ }
+
+ @Override
+ public String toString() {
+ return "FontConfig{"
+ + "mFamilies=" + mFamilies
+ + ", mAliases=" + mAliases
+ + ", mLastModifiedTimeMillis=" + mLastModifiedTimeMillis
+ + ", mConfigVersion=" + mConfigVersion
+ + '}';
+ }
+
/**
* Represents single font entry in system font configuration.
*
@@ -317,6 +344,37 @@
public boolean isItalic() {
return getStyle().getSlant() == FontStyle.FONT_SLANT_ITALIC;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Font font = (Font) o;
+ return mIndex == font.mIndex
+ && Objects.equals(mFile, font.mFile)
+ && Objects.equals(mOriginalFile, font.mOriginalFile)
+ && Objects.equals(mStyle, font.mStyle)
+ && Objects.equals(mFontVariationSettings, font.mFontVariationSettings)
+ && Objects.equals(mFontFamilyName, font.mFontFamilyName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFile, mOriginalFile, mStyle, mIndex, mFontVariationSettings,
+ mFontFamilyName);
+ }
+
+ @Override
+ public String toString() {
+ return "Font{"
+ + "mFile=" + mFile
+ + ", mOriginalFile=" + mOriginalFile
+ + ", mStyle=" + mStyle
+ + ", mIndex=" + mIndex
+ + ", mFontVariationSettings='" + mFontVariationSettings + '\''
+ + ", mFontFamilyName='" + mFontFamilyName + '\''
+ + '}';
+ }
}
/**
@@ -398,6 +456,30 @@
return new Alias[size];
}
};
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Alias alias = (Alias) o;
+ return mWeight == alias.mWeight
+ && Objects.equals(mName, alias.mName)
+ && Objects.equals(mOriginal, alias.mOriginal);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mName, mOriginal, mWeight);
+ }
+
+ @Override
+ public String toString() {
+ return "Alias{"
+ + "mName='" + mName + '\''
+ + ", mOriginal='" + mOriginal + '\''
+ + ", mWeight=" + mWeight
+ + '}';
+ }
}
/**
@@ -413,7 +495,7 @@
public static final class FontFamily implements Parcelable {
private final @NonNull List<Font> mFonts;
private final @Nullable String mName;
- private final @Nullable LocaleList mLocaleList;
+ private final @NonNull LocaleList mLocaleList;
private final @Variant int mVariant;
/** @hide */
@@ -454,7 +536,7 @@
* @hide Only system server can create this instance and passed via IPC.
*/
public FontFamily(@NonNull List<Font> fonts, @Nullable String name,
- @Nullable LocaleList localeList, @Variant int variant) {
+ @NonNull LocaleList localeList, @Variant int variant) {
mFonts = fonts;
mName = name;
mLocaleList = localeList;
@@ -493,8 +575,6 @@
*
* The locale list will be used for deciding which font family should be used in fallback
* list.
- *
- * @return non-null if a locale list is available. Otherwise null.
*/
public @NonNull LocaleList getLocaleList() {
return mLocaleList;
@@ -559,5 +639,31 @@
public @NonNull String getLanguages() {
return mLocaleList.toLanguageTags();
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ FontFamily that = (FontFamily) o;
+ return mVariant == that.mVariant
+ && Objects.equals(mFonts, that.mFonts)
+ && Objects.equals(mName, that.mName)
+ && Objects.equals(mLocaleList, that.mLocaleList);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mFonts, mName, mLocaleList, mVariant);
+ }
+
+ @Override
+ public String toString() {
+ return "FontFamily{"
+ + "mFonts=" + mFonts
+ + ", mName='" + mName + '\''
+ + ", mLocaleList=" + mLocaleList
+ + ", mVariant=" + mVariant
+ + '}';
+ }
}
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 85911ff..f99d430 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -977,6 +977,9 @@
calculateEllipsis(start, end, measured, widthStart,
ellipsisWidth, ellipsize, j,
textWidth, paint, forceEllipsis);
+ } else {
+ mLines[mColumns * j + ELLIPSIS_START] = 0;
+ mLines[mColumns * j + ELLIPSIS_COUNT] = 0;
}
}
diff --git a/core/java/android/tracing/ITracingServiceProxy.aidl b/core/java/android/tracing/ITracingServiceProxy.aidl
new file mode 100644
index 0000000..4520db3
--- /dev/null
+++ b/core/java/android/tracing/ITracingServiceProxy.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.tracing;
+
+/**
+ * Binder interface for the TracingServiceProxy running in system_server.
+ *
+ * {@hide}
+ */
+interface ITracingServiceProxy
+{
+ /**
+ * Notifies system tracing app that a tracing session has ended. If a session is repurposed
+ * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
+ * there is no buffer available to dump.
+ */
+ oneway void notifyTraceSessionEnded(boolean sessionStolen);
+}
diff --git a/core/java/android/tracing/OWNERS b/core/java/android/tracing/OWNERS
new file mode 100644
index 0000000..f5de4eb
--- /dev/null
+++ b/core/java/android/tracing/OWNERS
@@ -0,0 +1,2 @@
+cfijalkovich@google.com
+carmenjackson@google.com
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 790773f..b229212 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -65,7 +65,7 @@
DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "true");
DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
- DEFAULT_FLAGS.put("settings_silky_home", "true");
+ DEFAULT_FLAGS.put("settings_silky_home", "false");
DEFAULT_FLAGS.put("settings_contextual_home", "false");
DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false");
}
diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java
index 5ac95d4..c0d8187 100644
--- a/core/java/android/uwb/RangingManager.java
+++ b/core/java/android/uwb/RangingManager.java
@@ -94,7 +94,8 @@
synchronized (this) {
if (!hasSession(sessionHandle)) {
Log.w(TAG,
- "onRangingOpened - received unexpected SessionHandle: " + sessionHandle);
+ "onRangingOpenedFailed - received unexpected SessionHandle: "
+ + sessionHandle);
return;
}
@@ -124,7 +125,7 @@
@RangingChangeReason int reason, PersistableBundle params) {
synchronized (this) {
if (!hasSession(sessionHandle)) {
- Log.w(TAG, "onRangingStartFailed - received unexpected SessionHandle: "
+ Log.w(TAG, "onRangingReconfigureFailed - received unexpected SessionHandle: "
+ sessionHandle);
return;
}
diff --git a/core/java/android/uwb/SessionHandle.java b/core/java/android/uwb/SessionHandle.java
index 928fcbdc..b23f5ad 100644
--- a/core/java/android/uwb/SessionHandle.java
+++ b/core/java/android/uwb/SessionHandle.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* @hide
*/
@@ -73,6 +75,11 @@
}
@Override
+ public int hashCode() {
+ return Objects.hashCode(mId);
+ }
+
+ @Override
public String toString() {
return "SessionHandle [id=" + mId + "]";
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 4168064..0ba1dfe 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -25,8 +25,8 @@
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.KeyguardManager;
-import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -59,8 +59,12 @@
* an application window, excluding the system decorations. The application display area may
* be smaller than the real display area because the system subtracts the space needed
* for decor elements such as the status bar. Use {@link WindowMetrics#getBounds()} to query the
- * application window bounds. Generally, use {@link WindowManager#getCurrentWindowMetrics()} to
- * query the metrics and perform UI-related actions.</li>
+ * application window bounds.</li>
+ * <li>The real display area specifies the part of the display that contains content
+ * including the system decorations. Even so, the real display area may be smaller than the
+ * physical size of the display if the window manager is emulating a smaller display
+ * using (adb shell wm size). Use the following methods to query the
+ * real display area: {@link #getRealSize}, {@link #getRealMetrics}.</li>
* </ul>
* </p><p>
* A logical display does not necessarily represent a particular physical display device
@@ -673,9 +677,9 @@
@UnsupportedAppUsage
public DisplayAdjustments getDisplayAdjustments() {
if (mResources != null) {
- final DisplayAdjustments currentAdjustments = mResources.getDisplayAdjustments();
- if (!mDisplayAdjustments.equals(currentAdjustments)) {
- mDisplayAdjustments = new DisplayAdjustments(currentAdjustments);
+ final DisplayAdjustments currentAdjustements = mResources.getDisplayAdjustments();
+ if (!mDisplayAdjustments.equals(currentAdjustements)) {
+ mDisplayAdjustments = new DisplayAdjustments(currentAdjustements);
}
}
@@ -1213,34 +1217,30 @@
}
/**
- * Provides the largest {@link Point outSize} an app may expect in the current system state,
- * without subtracting any window decor.
+ * Gets the real size of the display without subtracting any window decor or
+ * applying any compatibility scale factors.
* <p>
- * The size describes the largest potential area the window might occupy. The size is adjusted
- * based on the current rotation of the display.
+ * The size is adjusted based on the current rotation of the display.
* </p><p>
* The real size may be smaller than the physical size of the screen when the
* window manager is emulating a smaller display (using adb shell wm size).
- * </p>
+ * </p><p>
+ * In general, {@link #getRealSize(Point)} and {@link WindowManager#getMaximumWindowMetrics()}
+ * report the same bounds except that certain areas of the display may not be available to
+ * windows created in the {@link WindowManager}'s {@link Context}.
+ *
+ * For example, imagine a device which has a multi-task mode that limits windows to half of the
+ * screen. In this case, {@link WindowManager#getMaximumWindowMetrics()} reports the
+ * bounds of the screen half where the window is located, while {@link #getRealSize(Point)}
+ * still reports the bounds of the whole display.
*
* @param outSize Set to the real size of the display.
+ *
+ * @see WindowManager#getMaximumWindowMetrics()
*/
public void getRealSize(Point outSize) {
synchronized (this) {
updateDisplayInfoLocked();
- if (shouldReportMaxBounds()) {
- final Rect bounds = mResources.getConfiguration()
- .windowConfiguration.getMaxBounds();
- outSize.x = bounds.width();
- outSize.y = bounds.height();
- if (DEBUG) {
- Log.d(TAG, "getRealSize determined from max bounds: " + outSize
- + " for uid " + Process.myUid());
- }
- // Skip adjusting by fixed rotation, since if it is necessary, the configuration
- // should already reflect the expected rotation.
- return;
- }
outSize.x = mDisplayInfo.logicalWidth;
outSize.y = mDisplayInfo.logicalHeight;
if (mMayAdjustByFixedRotation) {
@@ -1250,11 +1250,9 @@
}
/**
- * Provides the largest {@link DisplayMetrics outMetrics} an app may expect in the current
- * system state, without subtracting any window decor.
+ * Gets display metrics based on the real size of this display.
* <p>
- * The size describes the largest potential area the window might occupy. The size is adjusted
- * based on the current rotation of the display.
+ * The size is adjusted based on the current rotation of the display.
* </p><p>
* The real size may be smaller than the physical size of the screen when the
* window manager is emulating a smaller display (using adb shell wm size).
@@ -1265,18 +1263,6 @@
public void getRealMetrics(DisplayMetrics outMetrics) {
synchronized (this) {
updateDisplayInfoLocked();
- if (shouldReportMaxBounds()) {
- mDisplayInfo.getMaxBoundsMetrics(outMetrics,
- CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO,
- mResources.getConfiguration());
- if (DEBUG) {
- Log.d(TAG, "getRealMetrics determined from max bounds: " + outMetrics
- + " for uid " + Process.myUid());
- }
- // Skip adjusting by fixed rotation, since if it is necessary, the configuration
- // should already reflect the expected rotation.
- return;
- }
mDisplayInfo.getLogicalMetrics(outMetrics,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
if (mMayAdjustByFixedRotation) {
@@ -1286,20 +1272,6 @@
}
/**
- * Determines if {@link WindowConfiguration#getMaxBounds()} should be reported as the
- * display dimensions. The max bounds field may be smaller than the logical dimensions
- * when apps need to be sandboxed.
- * @return {@code true} when max bounds should be applied.
- */
- private boolean shouldReportMaxBounds() {
- if (mResources == null) {
- return false;
- }
- final Configuration config = mResources.getConfiguration();
- return config != null && !config.windowConfiguration.getMaxBounds().isEmpty();
- }
-
- /**
* Gets the state of the display, such as whether it is on or off.
*
* @return The state of the display: one of {@link #STATE_OFF}, {@link #STATE_ON},
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 5d4a4e5..e6cd252 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -58,11 +58,11 @@
public static final int VSYNC_SOURCE_SURFACE_FLINGER = 1;
/**
- * Specifies to generate config changed events from Surface Flinger.
+ * Specifies to generate mode changed events from Surface Flinger.
* <p>
* Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h
*/
- public static final int EVENT_REGISTRATION_CONFIG_CHANGED_FLAG = 0x1;
+ public static final int EVENT_REGISTRATION_MODE_CHANGED_FLAG = 0x1;
/**
* Specifies to generate frame rate override events from Surface Flinger.
@@ -197,14 +197,14 @@
}
/**
- * Called when a display config changed event is received.
+ * Called when a display mode changed event is received.
*
* @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
* timebase.
* @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
- * @param configId The new config Id
+ * @param modeId The new mode Id
*/
- public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
+ public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId) {
}
/**
@@ -273,8 +273,8 @@
// Called from native code.
@SuppressWarnings("unused")
- private void dispatchConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
- onConfigChanged(timestampNanos, physicalDisplayId, configId);
+ private void dispatchModeChanged(long timestampNanos, long physicalDisplayId, int modeId) {
+ onModeChanged(timestampNanos, physicalDisplayId, modeId);
}
// Called from native code.
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 8a44504..2a00b5a 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -24,7 +24,6 @@
import static android.view.DisplayInfoProto.NAME;
import android.annotation.Nullable;
-import android.app.WindowConfiguration;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -616,29 +615,11 @@
getMetricsWithSize(outMetrics, ci, configuration, appWidth, appHeight);
}
- /**
- * Populates {@code outMetrics} with details of the logical display. Bounds are limited
- * by the logical size of the display.
- *
- * @param outMetrics the {@link DisplayMetrics} to be populated
- * @param compatInfo the {@link CompatibilityInfo} to be applied
- * @param configuration the {@link Configuration}
- */
public void getLogicalMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
Configuration configuration) {
getMetricsWithSize(outMetrics, compatInfo, configuration, logicalWidth, logicalHeight);
}
- /**
- * Similar to {@link #getLogicalMetrics}, but the limiting bounds are determined from
- * {@link WindowConfiguration#getMaxBounds()}
- */
- public void getMaxBoundsMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
- Configuration configuration) {
- Rect bounds = configuration.windowConfiguration.getMaxBounds();
- getMetricsWithSize(outMetrics, compatInfo, configuration, bounds.width(), bounds.height());
- }
-
public int getNaturalWidth() {
return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ?
logicalWidth : logicalHeight;
diff --git a/core/java/android/view/IRemoteAnimationRunner.aidl b/core/java/android/view/IRemoteAnimationRunner.aidl
index 423e23d..1f64fb8 100644
--- a/core/java/android/view/IRemoteAnimationRunner.aidl
+++ b/core/java/android/view/IRemoteAnimationRunner.aidl
@@ -30,11 +30,15 @@
/**
* Called when the process needs to start the remote animation.
*
+ * @param transition The old transition type. Must be one of WindowManager.TRANSIT_OLD_* values.
* @param apps The list of apps to animate.
+ * @param wallpapers The list of wallpapers to animate.
+ * @param nonApps The list of non-app windows such as Bubbles to animate.
* @param finishedCallback The callback to invoke when the animation is finished.
*/
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
- void onAnimationStart(in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers,
+ void onAnimationStart(int transit, in RemoteAnimationTarget[] apps,
+ in RemoteAnimationTarget[] wallpapers, in RemoteAnimationTarget[] nonApps,
in IRemoteAnimationFinishedCallback finishedCallback);
/**
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index ae8afca..62f4b86 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -124,16 +124,10 @@
*
* @param token Token to be registered.
* @param type Window type to be used with this token.
- * @param options A bundle used to pass window-related options.
* @param displayId The ID of the display where this token should be added.
- * @param packageName The name of package to request to add window token. Could be {@code null}
- * if callers holds the MANAGE_APP_TOKENS permission.
- * @return {@link WindowManagerGlobal#ADD_OKAY} if the addition was successful, an error code
- * otherwise.
+ * @param options A bundle used to pass window-related options.
*/
- int addWindowTokenWithOptions(IBinder token, int type, int displayId, in Bundle options,
- String packageName);
- void addWindowToken(IBinder token, int type, int displayId);
+ void addWindowToken(IBinder token, int type, int displayId, in Bundle options);
/**
* Remove window token on a specific display.
*
@@ -784,6 +778,10 @@
/**
* Registers a listener for a {@link android.app.WindowContext} to handle configuration changes
* from the server side.
+ * <p>
+ * Note that this API should be invoked after calling
+ * {@link android.app.WindowTokenClient#attachContext(WindowContext)}
+ * </p>
*
* @param clientToken the window context's token
* @param type Window type of the window context
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 990b7bd..dad932c 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -123,14 +123,6 @@
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/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 0939336..6a34a15 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -111,6 +111,9 @@
mControl = new InsetsAnimationControlImpl(controls, frame, state, listener,
types, mCallbacks, durationMs, interpolator, animationType, translator);
InsetsAnimationThread.getHandler().post(() -> {
+ if (mControl.isCancelled()) {
+ return;
+ }
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW,
"InsetsAsyncAnimation: " + WindowInsets.Type.toString(types), types);
listener.onReady(mControl, types);
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 24bc308..f8c4d15 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -928,7 +928,9 @@
* seamless transition is one that doesn't have any visual interruptions, such as a black
* screen for a second or two. True indicates that any frame rate changes caused by this
* request should be seamless. False indicates that non-seamless refresh rates are also
- * acceptable.
+ * acceptable. Non-seamless switches might be used when the benefit of matching the content's
+ * frame rate outweighs the cost of the transition, for example when displaying
+ * long-running video content.
*
* @throws IllegalArgumentException If frameRate or compatibility are invalid.
*/
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index acd2507..6a629ca 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -161,8 +161,8 @@
int L, int T, int R, int B);
private static native void nativeSetDisplaySize(long transactionObj, IBinder displayToken,
int width, int height);
- private static native SurfaceControl.DisplayInfo nativeGetDisplayInfo(IBinder displayToken);
- private static native SurfaceControl.DisplayConfig[] nativeGetDisplayConfigs(
+ private static native DisplayInfo nativeGetDisplayInfo(IBinder displayToken);
+ private static native DisplayMode[] nativeGetDisplayModes(
IBinder displayToken);
private static native DisplayedContentSamplingAttributes
nativeGetDisplayedContentSamplingAttributes(IBinder displayToken);
@@ -170,13 +170,13 @@
boolean enable, int componentMask, int maxFrames);
private static native DisplayedContentSample nativeGetDisplayedContentSample(
IBinder displayToken, long numFrames, long timestamp);
- private static native int nativeGetActiveConfig(IBinder displayToken);
- private static native boolean nativeSetDesiredDisplayConfigSpecs(IBinder displayToken,
- SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs);
- private static native SurfaceControl.DesiredDisplayConfigSpecs
- nativeGetDesiredDisplayConfigSpecs(IBinder displayToken);
+ private static native int nativeGetActiveDisplayMode(IBinder displayToken);
+ private static native boolean nativeSetDesiredDisplayModeSpecs(IBinder displayToken,
+ DesiredDisplayModeSpecs desiredDisplayModeSpecs);
+ private static native DesiredDisplayModeSpecs
+ nativeGetDesiredDisplayModeSpecs(IBinder displayToken);
private static native int[] nativeGetDisplayColorModes(IBinder displayToken);
- private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries(
+ private static native DisplayPrimaries nativeGetDisplayNativePrimaries(
IBinder displayToken);
private static native int[] nativeGetCompositionDataspaces();
private static native int nativeGetActiveColorMode(IBinder displayToken);
@@ -188,8 +188,6 @@
IBinder displayToken, int mode);
private static native void nativeDeferTransactionUntil(long transactionObj, long nativeObject,
long barrierObject, long frame);
- private static native void nativeReparentChildren(long transactionObj, long nativeObject,
- long newParentObject);
private static native void nativeReparent(long transactionObj, long nativeObject,
long newParentNativeObject);
@@ -1745,11 +1743,11 @@
*
* @hide
*/
- public static final class DisplayConfig {
+ public static final class DisplayMode {
/**
* Invalid display config id.
*/
- public static final int INVALID_DISPLAY_CONFIG_ID = -1;
+ public static final int INVALID_DISPLAY_MODE_ID = -1;
public int width;
public int height;
@@ -1766,7 +1764,7 @@
* configs within the same group can be done seamlessly in most cases.
* @see: android.hardware.graphics.composer@2.4::IComposerClient::Attribute::CONFIG_GROUP
*/
- public int configGroup;
+ public int group;
@Override
public String toString() {
@@ -1777,7 +1775,7 @@
+ ", refreshRate=" + refreshRate
+ ", appVsyncOffsetNanos=" + appVsyncOffsetNanos
+ ", presentationDeadlineNanos=" + presentationDeadlineNanos
- + ", configGroup=" + configGroup + "}";
+ + ", group=" + group + "}";
}
}
@@ -1804,21 +1802,21 @@
/**
* @hide
*/
- public static SurfaceControl.DisplayConfig[] getDisplayConfigs(IBinder displayToken) {
+ public static DisplayMode[] getDisplayModes(IBinder displayToken) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
- return nativeGetDisplayConfigs(displayToken);
+ return nativeGetDisplayModes(displayToken);
}
/**
* @hide
*/
- public static int getActiveConfig(IBinder displayToken) {
+ public static int getActiveDisplayMode(IBinder displayToken) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
- return nativeGetActiveConfig(displayToken);
+ return nativeGetActiveDisplayMode(displayToken);
}
/**
@@ -1865,8 +1863,8 @@
*
* @hide
*/
- public static final class DesiredDisplayConfigSpecs {
- public int defaultConfig;
+ public static final class DesiredDisplayModeSpecs {
+ public int defaultMode;
/**
* The primary refresh rate range represents display manager's general guidance on the
* display configs surface flinger will consider when switching refresh rates. Unless
@@ -1891,16 +1889,16 @@
*/
public boolean allowGroupSwitching;
- public DesiredDisplayConfigSpecs() {}
+ public DesiredDisplayModeSpecs() {}
- public DesiredDisplayConfigSpecs(DesiredDisplayConfigSpecs other) {
+ public DesiredDisplayModeSpecs(DesiredDisplayModeSpecs other) {
copyFrom(other);
}
- public DesiredDisplayConfigSpecs(int defaultConfig, boolean allowGroupSwitching,
+ public DesiredDisplayModeSpecs(int defaultMode, boolean allowGroupSwitching,
float primaryRefreshRateMin, float primaryRefreshRateMax,
float appRequestRefreshRateMin, float appRequestRefreshRateMax) {
- this.defaultConfig = defaultConfig;
+ this.defaultMode = defaultMode;
this.allowGroupSwitching = allowGroupSwitching;
this.primaryRefreshRateMin = primaryRefreshRateMin;
this.primaryRefreshRateMax = primaryRefreshRateMax;
@@ -1910,14 +1908,14 @@
@Override
public boolean equals(@Nullable Object o) {
- return o instanceof DesiredDisplayConfigSpecs && equals((DesiredDisplayConfigSpecs) o);
+ return o instanceof DesiredDisplayModeSpecs && equals((DesiredDisplayModeSpecs) o);
}
/**
* Tests for equality.
*/
- public boolean equals(DesiredDisplayConfigSpecs other) {
- return other != null && defaultConfig == other.defaultConfig
+ public boolean equals(DesiredDisplayModeSpecs other) {
+ return other != null && defaultMode == other.defaultMode
&& primaryRefreshRateMin == other.primaryRefreshRateMin
&& primaryRefreshRateMax == other.primaryRefreshRateMax
&& appRequestRefreshRateMin == other.appRequestRefreshRateMin
@@ -1932,8 +1930,8 @@
/**
* Copies the supplied object's values to this object.
*/
- public void copyFrom(DesiredDisplayConfigSpecs other) {
- defaultConfig = other.defaultConfig;
+ public void copyFrom(DesiredDisplayModeSpecs other) {
+ defaultMode = other.defaultMode;
primaryRefreshRateMin = other.primaryRefreshRateMin;
primaryRefreshRateMax = other.primaryRefreshRateMax;
appRequestRefreshRateMin = other.appRequestRefreshRateMin;
@@ -1944,7 +1942,7 @@
public String toString() {
return String.format("defaultConfig=%d primaryRefreshRateRange=[%.0f %.0f]"
+ " appRequestRefreshRateRange=[%.0f %.0f]",
- defaultConfig, primaryRefreshRateMin, primaryRefreshRateMax,
+ defaultMode, primaryRefreshRateMin, primaryRefreshRateMax,
appRequestRefreshRateMin, appRequestRefreshRateMax);
}
}
@@ -1952,25 +1950,31 @@
/**
* @hide
*/
- public static boolean setDesiredDisplayConfigSpecs(IBinder displayToken,
- SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs) {
+ public static boolean setDesiredDisplayModeSpecs(IBinder displayToken,
+ DesiredDisplayModeSpecs desiredDisplayModeSpecs) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
+ if (desiredDisplayModeSpecs == null) {
+ throw new IllegalArgumentException("desiredDisplayModeSpecs must not be null");
+ }
+ if (desiredDisplayModeSpecs.defaultMode < 0) {
+ throw new IllegalArgumentException("defaultMode must be non-negative");
+ }
- return nativeSetDesiredDisplayConfigSpecs(displayToken, desiredDisplayConfigSpecs);
+ return nativeSetDesiredDisplayModeSpecs(displayToken, desiredDisplayModeSpecs);
}
/**
* @hide
*/
- public static SurfaceControl.DesiredDisplayConfigSpecs getDesiredDisplayConfigSpecs(
+ public static DesiredDisplayModeSpecs getDesiredDisplayModeSpecs(
IBinder displayToken) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
- return nativeGetDesiredDisplayConfigSpecs(displayToken);
+ return nativeGetDesiredDisplayModeSpecs(displayToken);
}
/**
@@ -2041,7 +2045,7 @@
/**
* @hide
*/
- public static SurfaceControl.DisplayPrimaries getDisplayNativePrimaries(
+ public static DisplayPrimaries getDisplayNativePrimaries(
IBinder displayToken) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
@@ -2970,15 +2974,6 @@
}
/**
- * @hide
- */
- public Transaction reparentChildren(SurfaceControl sc, SurfaceControl newParent) {
- checkPreconditions(sc);
- nativeReparentChildren(mNativeObject, sc.mNativeObject, newParent.mNativeObject);
- return this;
- }
-
- /**
* Re-parents a given layer to a new parent. Children inherit transform (position, scaling)
* crop, visibility, and Z-ordering from their parents, as if the children were pixels within the
* parent Surface.
@@ -3252,7 +3247,10 @@
* interruptions, such as a black screen for a second or two. True
* indicates that any frame rate changes caused by this request
* should be seamless. False indicates that non-seamless refresh
- * rates are also acceptable.
+ * rates are also acceptable. Non-seamless switches might be
+ * used when the benefit of matching the content's frame rate
+ * outweighs the cost of the transition, for example when
+ * displaying long-running video content.
* @return This transaction object.
*/
@NonNull
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index f019648..18029af 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -162,7 +162,6 @@
@NonNull WindowlessWindowManager wwm, boolean useSfChoreographer) {
mWm = wwm;
mViewRoot = new ViewRootImpl(c, d, mWm, useSfChoreographer);
- mViewRoot.forceDisableBLAST();
mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
}
@@ -188,7 +187,6 @@
mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
mSurfaceControl, hostToken);
mViewRoot = new ViewRootImpl(context, display, mWm);
- mViewRoot.forceDisableBLAST();
mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
}
diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
index b10370a..acbcbfa 100644
--- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java
+++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
@@ -85,12 +85,13 @@
for (int i = params.length - 1; i >= 0; i--) {
SurfaceParams surfaceParams = params[i];
SurfaceControl surface = surfaceParams.surface;
- if (frame > 0) {
- t.deferTransactionUntil(surface, mTargetSc, frame);
- }
applyParams(t, surfaceParams, mTmpFloat9);
}
- t.apply();
+ if (mTargetViewRootImpl != null) {
+ mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
+ } else {
+ t.apply();
+ }
}
public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 749c0df..1273b49 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -740,6 +740,7 @@
* @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 @@
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;
}
}
@@ -8740,14 +8744,17 @@
/**
* Populates a {@link ViewStructure} for content capture.
*
- * <p>This method is called after a view is that is eligible for content capture
- * (for example, if it {@link #isImportantForAutofill()}, an intelligence service is enabled for
- * the user, and the activity rendering the view is enabled for content capture) is laid out and
- * is visible.
- *
- * <p>The populated structure is then passed to the service through
+ * <p>This method is called after a view that is eligible for content capture
+ * (for example, if it {@link #isImportantForContentCapture()}, an intelligence service is
+ * enabled for the user, and the activity rendering the view is enabled for content capture)
+ * is laid out and is visible. The populated structure is then passed to the service through
* {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)}.
*
+ * <p>The default implementation of this method sets the most relevant properties based on
+ * related {@link View} methods, and views in the standard Android widgets library also
+ * override it to set their relevant properties. Therefore, if overriding this method, it
+ * is recommended to call {@code super.onProvideContentCaptureStructure()}.
+ *
* <p><b>Note: </b>views that manage a virtual structure under this view must populate just
* the node representing this view and return right away, then asynchronously report (not
* necessarily in the UI thread) when the children nodes appear, disappear or have their text
@@ -8755,7 +8762,7 @@
* {@link ContentCaptureSession#notifyViewAppeared(ViewStructure)},
* {@link ContentCaptureSession#notifyViewDisappeared(AutofillId)}, and
* {@link ContentCaptureSession#notifyViewTextChanged(AutofillId, CharSequence)}
- * respectively. The structure for the a child must be created using
+ * respectively. The structure for a child must be created using
* {@link ContentCaptureSession#newVirtualViewStructure(AutofillId, long)}, and the
* {@code autofillId} for a child can be obtained either through
* {@code childStructure.getAutofillId()} or
@@ -8900,7 +8907,7 @@
/**
* Called when assist structure is being retrieved from a view as part of
* {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData} to
- * generate additional virtual structure under this view. The defaullt implementation
+ * generate additional virtual structure under this view. The default implementation
* uses {@link #getAccessibilityNodeProvider()} to try to generate this from the
* view's virtual accessibility nodes, if any. You can override this for a more
* optimal implementation providing this data.
@@ -17918,6 +17925,8 @@
*
* @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 18ef80c..4716141 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -315,7 +315,7 @@
* In that case we receive a call back from {@link ActivityThread} and this flag is used to
* preserve the initial value.
*
- * @see #performConfigurationChange(Configuration, Configuration, boolean, int)
+ * @see #performConfigurationChange(MergedConfiguration, boolean, int)
*/
private boolean mForceNextConfigUpdate;
@@ -1675,7 +1675,8 @@
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 @@
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();
}
}
@@ -10137,9 +10141,11 @@
* Merges the transaction passed in with the next transaction in BLASTBufferQueue. This ensures
* you can add transactions to the upcoming frame.
*/
- void mergeWithNextTransaction(Transaction t, long frameNumber) {
+ public void mergeWithNextTransaction(Transaction t, long frameNumber) {
if (mBlastBufferQueue != null) {
mBlastBufferQueue.mergeWithNextTransaction(t, frameNumber);
+ } else {
+ t.apply();
}
}
}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index dd0ab65..47ac1ee 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -116,14 +116,6 @@
*/
public static final int RELAYOUT_INSETS_PENDING = 0x1;
- /**
- * Flag for relayout: the client may be currently using the current surface,
- * so if it is to be destroyed as a part of the relayout the destroy must
- * be deferred until later. The client will call performDeferredDestroy()
- * when it is okay.
- */
- public static final int RELAYOUT_DEFER_SURFACE_DESTROY = 0x2;
-
public static final int ADD_FLAG_IN_TOUCH_MODE = 0x1;
public static final int ADD_FLAG_APP_VISIBLE = 0x2;
public static final int ADD_FLAG_USE_TRIPLE_BUFFERING = 0x4;
@@ -147,7 +139,6 @@
public static final int ADD_INVALID_DISPLAY = -9;
public static final int ADD_INVALID_TYPE = -10;
public static final int ADD_INVALID_USER = -11;
- public static final int ADD_TOO_MANY_TOKENS = -12;
@UnsupportedAppUsage
private static WindowManagerGlobal sDefaultWindowManager;
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index b85f1079..39d3c01 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -71,7 +71,7 @@
new HashMap<IBinder, ResizeCompleteCallback>();
private final SurfaceSession mSurfaceSession = new SurfaceSession();
- private final SurfaceControl mRootSurface;
+ protected final SurfaceControl mRootSurface;
private final Configuration mConfiguration;
private final IWindowSession mRealWm;
private final IBinder mHostInputToken;
@@ -126,7 +126,7 @@
}
}
- protected void attachToParentSurface(SurfaceControl.Builder b) {
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
b.setParent(mRootSurface);
}
@@ -140,10 +140,10 @@
InsetsSourceControl[] outActiveControls) {
final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession)
.setFormat(attrs.format)
- .setBufferSize(getSurfaceWidth(attrs), getSurfaceHeight(attrs))
+ .setBLASTLayer()
.setName(attrs.getTitle().toString())
.setCallsite("WindowlessWindowManager.addToDisplay");
- attachToParentSurface(b);
+ attachToParentSurface(window, b);
final SurfaceControl sc = b.build();
if (((attrs.inputFeatures &
@@ -162,7 +162,11 @@
mStateForWindow.put(window.asBinder(), state);
}
- return WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
+ final int res = WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE |
+ WindowManagerGlobal.ADD_FLAG_USE_BLAST;
+
+ // Include whether the window is in touch mode.
+ return isInTouchMode() ? res | WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE : res;
}
/**
@@ -210,6 +214,26 @@
return !PixelFormat.formatHasAlpha(attrs.format);
}
+ private boolean isInTouchMode() {
+ try {
+ return WindowManagerGlobal.getWindowSession().getInTouchMode();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to check if the window is in touch mode", e);
+ }
+ return false;
+ }
+
+ /** Access to package members for SystemWindow leashing
+ * @hide
+ */
+ protected IBinder getWindowBinder(View rootView) {
+ final ViewRootImpl root = rootView.getViewRootImpl();
+ if (root == null) {
+ return null;
+ }
+ return root.mWindow.asBinder();
+ }
+
/** @hide */
@Nullable
protected SurfaceControl getSurfaceControl(View rootView) {
@@ -254,8 +278,8 @@
WindowManager.LayoutParams attrs = state.mParams;
if (viewFlags == View.VISIBLE) {
- t.setBufferSize(sc, getSurfaceWidth(attrs), getSurfaceHeight(attrs))
- .setOpaque(sc, isOpaque(attrs)).show(sc).apply();
+ outSurfaceSize.set(getSurfaceWidth(attrs), getSurfaceHeight(attrs));
+ t.setOpaque(sc, isOpaque(attrs)).show(sc).apply();
outSurfaceControl.copyFrom(sc, "WindowlessWindowManager.relayout");
} else {
t.hide(sc).apply();
@@ -276,7 +300,8 @@
}
}
- return 0;
+ // Include whether the window is in touch mode.
+ return isInTouchMode() ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
}
@Override
@@ -289,10 +314,6 @@
}
@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 794181e..decbf8c 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -200,6 +200,34 @@
"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 @@
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/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 415b3a7..37220fe 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -164,12 +164,12 @@
/**
* Default implementation calls {@link #finishComposingText()} and
- * {@code setImeTemporarilyConsumesInput(false)}.
+ * {@code setImeConsumesInput(false)}.
*/
@CallSuper
public void closeConnection() {
finishComposingText();
- setImeTemporarilyConsumesInput(false);
+ setImeConsumesInput(false);
}
/**
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 2df75f6..bde4cb7 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -299,7 +299,7 @@
* {@link EditorInfo} is using {@link Configuration#ORIENTATION_PORTRAIT} mode.
* @hide
*/
- public static final int IME_FLAG_APP_WINDOW_PORTRAIT = 0x80000000;
+ public static final int IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT = 0x00000001;
/**
* Generic unspecified type for {@link #imeOptions}.
@@ -321,7 +321,6 @@
* 1 1 IME_ACTION_NEXT
* 11 IME_ACTION_DONE
* 111 IME_ACTION_PREVIOUS
- * 1 IME_FLAG_APP_WINDOW_PORTRAIT
* 1 IME_FLAG_NO_PERSONALIZED_LEARNING
* 1 IME_FLAG_NO_FULLSCREEN
* 1 IME_FLAG_NAVIGATE_PREVIOUS
@@ -356,7 +355,7 @@
* Masks for {@link internalImeOptions}
*
* <pre>
- * 1 IME_FLAG_APP_WINDOW_PORTRAIT
+ * 1 IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT
* |-------|-------|-------|-------|</pre>
*/
@@ -984,6 +983,7 @@
dest.writeInt(inputType);
dest.writeInt(imeOptions);
dest.writeString(privateImeOptions);
+ dest.writeInt(internalImeOptions);
TextUtils.writeToParcel(actionLabel, dest, flags);
dest.writeInt(actionId);
dest.writeInt(initialSelStart);
@@ -1019,6 +1019,7 @@
res.inputType = source.readInt();
res.imeOptions = source.readInt();
res.privateImeOptions = source.readString();
+ res.internalImeOptions = source.readInt();
res.actionLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
res.actionId = source.readInt();
res.initialSelStart = source.readInt();
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 6300320..0ab4e05 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -19,6 +19,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.LocaleList;
@@ -69,7 +73,8 @@
/**
* The IME provided locales for the request. If non-empty, the inline suggestions should
- * return languages from the supported locales. If not provided, it'll default to system locale.
+ * return languages from the supported locales. If not provided, it'll default to be empty if
+ * target SDK is S or above, and default to system locale otherwise.
*
* <p>Note for Autofill Providers: It is <b>recommended</b> for the returned inline suggestions
* to have one locale to guarantee consistent UI rendering.</p>
@@ -156,7 +161,18 @@
return ActivityThread.currentPackageName();
}
+ /**
+ * The {@link InlineSuggestionsRequest#getSupportedLocales()} now returns empty locale list when
+ * it's not set, instead of the default system locale.
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+ private static final long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY = 169273070L;
+
private static LocaleList defaultSupportedLocales() {
+ if (CompatChanges.isChangeEnabled(IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY)) {
+ return LocaleList.getEmptyLocaleList();
+ }
return LocaleList.getDefault();
}
@@ -189,7 +205,7 @@
- // Code below generated by codegen v1.0.15.
+ // Code below generated by codegen v1.0.22.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -264,7 +280,8 @@
/**
* The IME provided locales for the request. If non-empty, the inline suggestions should
- * return languages from the supported locales. If not provided, it'll default to system locale.
+ * return languages from the supported locales. If not provided, it'll default to be empty if
+ * target SDK is S or above, and default to system locale otherwise.
*
* <p>Note for Autofill Providers: It is <b>recommended</b> for the returned inline suggestions
* to have one locale to guarantee consistent UI rendering.</p>
@@ -522,7 +539,8 @@
/**
* The IME provided locales for the request. If non-empty, the inline suggestions should
- * return languages from the supported locales. If not provided, it'll default to system locale.
+ * return languages from the supported locales. If not provided, it'll default to be empty if
+ * target SDK is S or above, and default to system locale otherwise.
*
* <p>Note for Autofill Providers: It is <b>recommended</b> for the returned inline suggestions
* to have one locale to guarantee consistent UI rendering.</p>
@@ -622,10 +640,10 @@
}
@DataClass.Generated(
- time = 1595457701315L,
- codegenVersion = "1.0.15",
+ time = 1612206506050L,
+ codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
- inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\nprivate static final @android.compat.annotation.ChangeId @android.compat.annotation.EnabledSince long IME_AUTOFILL_DEFAULT_SUPPORTED_LOCALES_IS_EMPTY\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index f3111bd..34a60bb 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -1004,20 +1004,19 @@
@Nullable Bundle opts);
/**
- * Called by the input method to indicate that it temporarily consumes all input for itself,
- * or no longer does so.
+ * Called by the input method to indicate that it consumes all input for itself, or no longer
+ * does so.
*
- * <p>Editors should reflect that they are temporarily not receiving input by hiding the
- * cursor if {@code imeTemporarilyConsumesInput} is {@code true}, and resume showing the
- * cursor if it is {@code false}.
+ * <p>Editors should reflect that they are not receiving input by hiding the cursor if
+ * {@code imeConsumesInput} is {@code true}, and resume showing the cursor if it is
+ * {@code false}.
*
- * @param imeTemporarilyConsumesInput {@code true} when the IME is temporarily consuming input
- * and the cursor should be hidden, {@code false} when input to the editor resumes and the
- * cursor should be shown again.
+ * @param imeConsumesInput {@code true} when the IME is consuming input and the cursor should be
+ * hidden, {@code false} when input to the editor resumes and the cursor should be shown again.
* @return {@code true} on success, {@code false} if the input connection is no longer valid, or
* the protocol is not supported.
*/
- default boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
+ default boolean setImeConsumesInput(boolean imeConsumesInput) {
return false;
}
}
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index b29149f..b1501a4 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -341,7 +341,7 @@
* @throws NullPointerException if the target is {@code null}.
*/
@Override
- public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
- return mTarget.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput);
+ public boolean setImeConsumesInput(boolean imeConsumesInput) {
+ return mTarget.setImeConsumesInput(imeConsumesInput);
}
}
diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java
index 046f75f..2452e4c 100644
--- a/core/java/android/widget/CheckBox.java
+++ b/core/java/android/widget/CheckBox.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.util.AttributeSet;
+import android.widget.RemoteViews.RemoteView;
/**
* <p>
@@ -52,6 +53,7 @@
* {@link android.R.styleable#View View Attributes}
* </p>
*/
+@RemoteView
public class CheckBox extends CompoundButton {
public CheckBox(Context context) {
this(context, null);
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 135ff9f..63f8ee7 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -27,11 +27,13 @@
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
+import android.view.RemotableViewMethod;
import android.view.SoundEffectConstants;
import android.view.ViewDebug;
import android.view.ViewHierarchyEncoder;
@@ -275,6 +277,7 @@
* @param resId the resource identifier of the drawable
* @attr ref android.R.styleable#CompoundButton_button
*/
+ @RemotableViewMethod(asyncImpl = "setButtonDrawableAsync")
public void setButtonDrawable(@DrawableRes int resId) {
final Drawable d;
if (resId != 0) {
@@ -285,6 +288,12 @@
setButtonDrawable(d);
}
+ /** @hide **/
+ public Runnable setButtonDrawableAsync(@DrawableRes int resId) {
+ Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId);
+ return () -> setButtonDrawable(drawable);
+ }
+
/**
* Sets a drawable as the compound button image.
*
@@ -336,6 +345,23 @@
}
/**
+ * Sets the button of this CompoundButton to the specified Icon.
+ *
+ * @param icon an Icon holding the desired button, or {@code null} to clear
+ * the button
+ */
+ @RemotableViewMethod(asyncImpl = "setButtonIconAsync")
+ public void setButtonIcon(@Nullable Icon icon) {
+ setButtonDrawable(icon == null ? null : icon.loadDrawable(getContext()));
+ }
+
+ /** @hide **/
+ public Runnable setButtonIconAsync(@Nullable Icon icon) {
+ Drawable button = icon == null ? null : icon.loadDrawable(getContext());
+ return () -> setButtonDrawable(button);
+ }
+
+ /**
* Applies a tint to the button drawable. Does not modify the current tint
* mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
* <p>
@@ -350,6 +376,7 @@
* @see #setButtonTintList(ColorStateList)
* @see Drawable#setTintList(ColorStateList)
*/
+ @RemotableViewMethod
public void setButtonTintList(@Nullable ColorStateList tint) {
mButtonTintList = tint;
mHasButtonTint = true;
@@ -394,6 +421,7 @@
* @see #getButtonTintMode()
* @see Drawable#setTintBlendMode(BlendMode)
*/
+ @RemotableViewMethod
public void setButtonTintBlendMode(@Nullable BlendMode tintMode) {
mButtonBlendMode = tintMode;
mHasButtonBlendMode = true;
diff --git a/core/java/android/widget/RadioButton.java b/core/java/android/widget/RadioButton.java
index a04d7c3..9b35034 100644
--- a/core/java/android/widget/RadioButton.java
+++ b/core/java/android/widget/RadioButton.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.util.AttributeSet;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -49,6 +50,7 @@
* {@link android.R.styleable#View View Attributes}
* </p>
*/
+@RemoteView
public class RadioButton extends CompoundButton {
public RadioButton(Context context) {
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 4722fdc..d445fdc 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -31,6 +31,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
+import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -59,6 +60,7 @@
* @see RadioButton
*
*/
+@RemoteView
public class RadioGroup extends LinearLayout {
private static final String LOG_TAG = RadioGroup.class.getSimpleName();
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index b47a0ac..6cb4b81 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -47,6 +47,7 @@
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.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;
@@ -132,6 +134,13 @@
* <li>{@link android.widget.TextClock}</li>
* <li>{@link android.widget.TextView}</li>
* </ul>
+ * <p>As of API 31, the following widgets and layouts may also be used:</p>
+ * <ul>
+ * <li>{@link android.widget.CheckBox}</li>
+ * <li>{@link android.widget.RadioButton}</li>
+ * <li>{@link android.widget.RadioGroup}</li>
+ * <li>{@link android.widget.Switch}</li>
+ * </ul>
* <p>Descendants of these classes are not supported.</p>
*/
public class RemoteViews implements Parcelable, Filter {
@@ -185,6 +194,9 @@
private static final int REMOVE_FROM_PARENT_ACTION_TAG = 23;
private static final int RESOURCE_REFLECTION_ACTION_TAG = 24;
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 = {
@@ -2552,6 +2564,169 @@
}
}
+ private static class SetCompoundButtonCheckedAction extends Action {
+
+ private final boolean mChecked;
+
+ SetCompoundButtonCheckedAction(@IdRes int viewId, boolean checked) {
+ this.viewId = viewId;
+ mChecked = checked;
+ }
+
+ SetCompoundButtonCheckedAction(Parcel in) {
+ viewId = in.readInt();
+ mChecked = in.readBoolean();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(viewId);
+ dest.writeBoolean(mChecked);
+ }
+
+ @Override
+ public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+ throws ActionException {
+ final View target = root.findViewById(viewId);
+ if (target == null) return;
+
+ if (!(target instanceof CompoundButton)) {
+ Log.w(LOG_TAG, "Cannot set checked to view "
+ + viewId + " because it is not a CompoundButton");
+ return;
+ }
+
+ ((CompoundButton) target).setChecked(mChecked);
+ }
+
+ @Override
+ public int getActionTag() {
+ return SET_COMPOUND_BUTTON_CHECKED_TAG;
+ }
+ }
+
+ private static class SetRadioGroupCheckedAction extends Action {
+
+ @IdRes private final int mCheckedId;
+
+ SetRadioGroupCheckedAction(@IdRes int viewId, @IdRes int checkedId) {
+ this.viewId = viewId;
+ mCheckedId = checkedId;
+ }
+
+ SetRadioGroupCheckedAction(Parcel in) {
+ viewId = in.readInt();
+ mCheckedId = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(viewId);
+ dest.writeInt(mCheckedId);
+ }
+
+ @Override
+ public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+ throws ActionException {
+ final View target = root.findViewById(viewId);
+ if (target == null) return;
+
+ if (!(target instanceof RadioGroup)) {
+ Log.w(LOG_TAG, "Cannot check " + viewId + " because it's not a RadioGroup");
+ return;
+ }
+
+ ((RadioGroup) target).check(mCheckedId);
+ }
+
+ @Override
+ public int getActionTag() {
+ return SET_RADIO_GROUP_CHECKED;
+ }
+ }
+
+ 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.
@@ -2766,6 +2941,12 @@
return new ResourceReflectionAction(parcel);
case COMPLEX_UNIT_DIMENSION_REFLECTION_ACTION_TAG:
return new ComplexUnitDimensionReflectionAction(parcel);
+ case SET_COMPOUND_BUTTON_CHECKED_TAG:
+ 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");
}
@@ -3501,6 +3682,28 @@
}
/**
+ * 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.
@@ -3846,6 +4049,26 @@
}
/**
+ * Equivalent to calling {@link android.widget.CompoundButton#setChecked(boolean)}.
+ *
+ * @param viewId The id of the view whose property to set.
+ * @param checked true to check the button, false to uncheck it.
+ */
+ public void setCompoundButtonChecked(@IdRes int viewId, boolean checked) {
+ addAction(new SetCompoundButtonCheckedAction(viewId, checked));
+ }
+
+ /**
+ * Equivalent to calling {@link android.widget.RadioGroup#check(int)}.
+ *
+ * @param viewId The id of the view whose property to set.
+ * @param checkedId The unique id of the radio button to select in the group.
+ */
+ public void setRadioGroupChecked(@IdRes int viewId, @IdRes int checkedId) {
+ addAction(new SetRadioGroupCheckedAction(viewId, checkedId));
+ }
+
+ /**
* Provides an alternate layout ID, which can be used to inflate this view. This layout will be
* used by the host when the widgets displayed on a light-background where foreground elements
* and text can safely draw using a dark color without any additional background protection.
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 3295fd2..d3600ef 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -35,6 +35,7 @@
import android.graphics.Region.Op;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.text.Layout;
@@ -48,12 +49,14 @@
import android.util.MathUtils;
import android.view.Gravity;
import android.view.MotionEvent;
+import android.view.RemotableViewMethod;
import android.view.SoundEffectConstants;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.view.ViewStructure;
import android.view.accessibility.AccessibilityEvent;
import android.view.inspector.InspectableProperty;
+import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -84,6 +87,7 @@
* @attr ref android.R.styleable#Switch_thumbTextPadding
* @attr ref android.R.styleable#Switch_track
*/
+@RemoteView
public class Switch extends CompoundButton {
private static final int THUMB_ANIMATION_DURATION = 250;
@@ -441,6 +445,7 @@
*
* @attr ref android.R.styleable#Switch_switchPadding
*/
+ @RemotableViewMethod
public void setSwitchPadding(int pixels) {
mSwitchPadding = pixels;
requestLayout();
@@ -466,6 +471,7 @@
*
* @attr ref android.R.styleable#Switch_switchMinWidth
*/
+ @RemotableViewMethod
public void setSwitchMinWidth(int pixels) {
mSwitchMinWidth = pixels;
requestLayout();
@@ -491,6 +497,7 @@
*
* @attr ref android.R.styleable#Switch_thumbTextPadding
*/
+ @RemotableViewMethod
public void setThumbTextPadding(int pixels) {
mThumbTextPadding = pixels;
requestLayout();
@@ -533,10 +540,17 @@
*
* @attr ref android.R.styleable#Switch_track
*/
+ @RemotableViewMethod(asyncImpl = "setTrackResourceAsync")
public void setTrackResource(@DrawableRes int resId) {
setTrackDrawable(getContext().getDrawable(resId));
}
+ /** @hide **/
+ public Runnable setTrackResourceAsync(@DrawableRes int resId) {
+ Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId);
+ return () -> setTrackDrawable(drawable);
+ }
+
/**
* Get the drawable used for the track that the switch slides within.
*
@@ -550,6 +564,23 @@
}
/**
+ * Set the drawable used for the track that the switch slides within to the specified Icon.
+ *
+ * @param icon an Icon holding the desired track, or {@code null} to clear
+ * the track
+ */
+ @RemotableViewMethod(asyncImpl = "setTrackIconAsync")
+ public void setTrackIcon(@Nullable Icon icon) {
+ setTrackDrawable(icon == null ? null : icon.loadDrawable(getContext()));
+ }
+
+ /** @hide **/
+ public Runnable setTrackIconAsync(@Nullable Icon icon) {
+ Drawable track = icon == null ? null : icon.loadDrawable(getContext());
+ return () -> setTrackDrawable(track);
+ }
+
+ /**
* Applies a tint to the track drawable. Does not modify the current
* tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
* <p>
@@ -563,6 +594,7 @@
* @see #getTrackTintList()
* @see Drawable#setTintList(ColorStateList)
*/
+ @RemotableViewMethod
public void setTrackTintList(@Nullable ColorStateList tint) {
mTrackTintList = tint;
mHasTrackTint = true;
@@ -607,6 +639,7 @@
* @see #getTrackTintMode()
* @see Drawable#setTintBlendMode(BlendMode)
*/
+ @RemotableViewMethod
public void setTrackTintBlendMode(@Nullable BlendMode blendMode) {
mTrackBlendMode = blendMode;
mHasTrackTintMode = true;
@@ -686,10 +719,17 @@
*
* @attr ref android.R.styleable#Switch_thumb
*/
+ @RemotableViewMethod(asyncImpl = "setThumbResourceAsync")
public void setThumbResource(@DrawableRes int resId) {
setThumbDrawable(getContext().getDrawable(resId));
}
+ /** @hide **/
+ public Runnable setThumbResourceAsync(@DrawableRes int resId) {
+ Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId);
+ return () -> setThumbDrawable(drawable);
+ }
+
/**
* Get the drawable used for the switch "thumb" - the piece that the user
* can physically touch and drag along the track.
@@ -704,6 +744,24 @@
}
/**
+ * Set the drawable used for the switch "thumb" - the piece that the user
+ * can physically touch and drag along the track - to the specified Icon.
+ *
+ * @param icon an Icon holding the desired thumb, or {@code null} to clear
+ * the thumb
+ */
+ @RemotableViewMethod(asyncImpl = "setThumbIconAsync")
+ public void setThumbIcon(@Nullable Icon icon) {
+ setThumbDrawable(icon == null ? null : icon.loadDrawable(getContext()));
+ }
+
+ /** @hide **/
+ public Runnable setThumbIconAsync(@Nullable Icon icon) {
+ Drawable track = icon == null ? null : icon.loadDrawable(getContext());
+ return () -> setThumbDrawable(track);
+ }
+
+ /**
* Applies a tint to the thumb drawable. Does not modify the current
* tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
* <p>
@@ -717,6 +775,7 @@
* @see #getThumbTintList()
* @see Drawable#setTintList(ColorStateList)
*/
+ @RemotableViewMethod
public void setThumbTintList(@Nullable ColorStateList tint) {
mThumbTintList = tint;
mHasThumbTint = true;
@@ -761,6 +820,7 @@
* @see #getThumbTintMode()
* @see Drawable#setTintBlendMode(BlendMode)
*/
+ @RemotableViewMethod
public void setThumbTintBlendMode(@Nullable BlendMode blendMode) {
mThumbBlendMode = blendMode;
mHasThumbTintMode = true;
@@ -822,6 +882,7 @@
*
* @attr ref android.R.styleable#Switch_splitTrack
*/
+ @RemotableViewMethod
public void setSplitTrack(boolean splitTrack) {
mSplitTrack = splitTrack;
invalidate();
@@ -852,6 +913,7 @@
*
* @attr ref android.R.styleable#Switch_textOn
*/
+ @RemotableViewMethod
public void setTextOn(CharSequence textOn) {
mTextOn = textOn;
requestLayout();
@@ -875,6 +937,7 @@
*
* @attr ref android.R.styleable#Switch_textOff
*/
+ @RemotableViewMethod
public void setTextOff(CharSequence textOff) {
mTextOff = textOff;
requestLayout();
@@ -889,6 +952,7 @@
* @param showText {@code true} to display on/off text
* @attr ref android.R.styleable#Switch_showText
*/
+ @RemotableViewMethod
public void setShowText(boolean showText) {
if (mShowText != showText) {
mShowText = showText;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 8cfbca8..fe37c53 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -497,9 +497,9 @@
private TextUtils.TruncateAt mEllipsize;
// A flag to indicate the cursor was hidden by IME.
- private boolean mImeTemporarilyConsumesInput;
+ private boolean mImeIsConsumingInput;
- // Whether cursor is visible without regard to {@link mImeTemporarilyConsumesInput}.
+ // Whether cursor is visible without regard to {@link mImeConsumesInput}.
// {code true} is the default value.
private boolean mCursorVisibleFromAttr = true;
@@ -8750,7 +8750,7 @@
}
}
if (getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT) {
- outAttrs.internalImeOptions |= EditorInfo.IME_FLAG_APP_WINDOW_PORTRAIT;
+ outAttrs.internalImeOptions |= EditorInfo.IME_INTERNAL_FLAG_APP_WINDOW_PORTRAIT;
}
if (isMultilineInputType(outAttrs.inputType)) {
// Multi-line text editors should always show an enter key.
@@ -10506,8 +10506,8 @@
/**
* Set whether the cursor is visible. The default is true. Note that this property only
- * makes sense for editable TextView. If IME is temporarily consuming the input, the cursor will
- * be always invisible, visibility will be updated as the last state when IME does not consume
+ * makes sense for editable TextView. If IME is consuming the input, the cursor will always be
+ * invisible, visibility will be updated as the last state when IME does not consume
* the input anymore.
*
* @see #isCursorVisible()
@@ -10521,20 +10521,20 @@
}
/**
- * Sets the IME is temporarily consuming the input and make the cursor invisible if
- * {@code imeTemporarilyConsumesInput} is {@code true}. Otherwise, make the cursor visible.
+ * Sets the IME is consuming the input and make the cursor invisible if {@code imeConsumesInput}
+ * is {@code true}. Otherwise, make the cursor visible.
*
- * @param imeTemporarilyConsumesInput {@code true} if IME is temporarily consuming the input
+ * @param imeConsumesInput {@code true} if IME is consuming the input
*
* @hide
*/
- public void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
- mImeTemporarilyConsumesInput = imeTemporarilyConsumesInput;
+ public void setImeConsumesInput(boolean imeConsumesInput) {
+ mImeIsConsumingInput = imeConsumesInput;
updateCursorVisibleInternal();
}
private void updateCursorVisibleInternal() {
- boolean visible = mCursorVisibleFromAttr && !mImeTemporarilyConsumesInput;
+ boolean visible = mCursorVisibleFromAttr && !mImeIsConsumingInput;
if (visible && mEditor == null) return; // visible is the default value with no edit data
createEditorIfNeeded();
if (mEditor.mCursorVisible != visible) {
@@ -10550,7 +10550,7 @@
/**
* @return whether or not the cursor is visible (assuming this TextView is editable). This
- * method may return {@code false} when the IME is temporarily consuming the input even if the
+ * method may return {@code false} when the IME is consuming the input even if the
* {@code mEditor.mCursorVisible} attribute is {@code true} or {@code #setCursorVisible(true)}
* is called.
*
diff --git a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
index 0152387..c2ee646 100644
--- a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
+++ b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
@@ -53,8 +53,10 @@
public static final String KEY_CUR_TASK = "cur_task";
/** Package of newly requested heavy-weight app. */
public static final String KEY_NEW_APP = "new_app";
+ public static final String KEY_ACTIVITY_OPTIONS = "activity_options";
IntentSender mStartIntent;
+ Bundle mActivityOptions;
boolean mHasResult;
String mCurApp;
int mCurTask;
@@ -65,8 +67,9 @@
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
-
+
mStartIntent = (IntentSender)getIntent().getParcelableExtra(KEY_INTENT);
+ mActivityOptions = getIntent().getBundleExtra(KEY_ACTIVITY_OPTIONS);
mHasResult = getIntent().getBooleanExtra(KEY_HAS_RESULT, false);
mCurApp = getIntent().getStringExtra(KEY_CUR_APP);
mCurTask = getIntent().getIntExtra(KEY_CUR_TASK, 0);
@@ -148,9 +151,9 @@
if (mHasResult) {
startIntentSenderForResult(mStartIntent, -1, null,
Intent.FLAG_ACTIVITY_FORWARD_RESULT,
- Intent.FLAG_ACTIVITY_FORWARD_RESULT, 0);
+ Intent.FLAG_ACTIVITY_FORWARD_RESULT, 0, mActivityOptions);
} else {
- startIntentSenderForResult(mStartIntent, -1, null, 0, 0, 0);
+ startIntentSenderForResult(mStartIntent, -1, null, 0, 0, 0, mActivityOptions);
}
} catch (IntentSender.SendIntentException ex) {
Log.w("HeavyWeightSwitcherActivity", "Failure starting", ex);
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index a5eb5f6..7aca36a 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -179,9 +179,10 @@
*
* @param changeId the ID of the change that was overridden
* @param packageName the app package name that was overridden
+ * @return {@code true} if an override existed
* @throws SecurityException if overriding changes is not permitted
*/
- void clearOverrideForTest(long changeId, String packageName);
+ boolean clearOverrideForTest(long changeId, String packageName);
/**
* Enables all compatibility changes that have enabledSinceTargetSdk ==
diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java
index fc95275..af21f81 100644
--- a/core/java/com/android/internal/infra/ServiceConnector.java
+++ b/core/java/com/android/internal/infra/ServiceConnector.java
@@ -726,4 +726,43 @@
}
}
}
+
+ /**
+ * A {@link ServiceConnector} that doesn't connect to anything.
+ *
+ * @param <T> the type of the {@link IInterface ipc interface} for the remote service
+ */
+ class NoOp<T extends IInterface> extends AndroidFuture<Object> implements ServiceConnector<T> {
+ {
+ completeExceptionally(new IllegalStateException("ServiceConnector is a no-op"));
+ }
+
+ @Override
+ public boolean run(@NonNull VoidJob<T> job) {
+ return false;
+ }
+
+ @Override
+ public AndroidFuture<Void> post(@NonNull VoidJob<T> job) {
+ return (AndroidFuture) this;
+ }
+
+ @Override
+ public <R> AndroidFuture<R> postForResult(@NonNull Job<T, R> job) {
+ return (AndroidFuture) this;
+ }
+
+ @Override
+ public <R> AndroidFuture<R> postAsync(@NonNull Job<T, CompletableFuture<R>> job) {
+ return (AndroidFuture) this;
+ }
+
+ @Override
+ public AndroidFuture<T> connect() {
+ return (AndroidFuture) this;
+ }
+
+ @Override
+ public void unbind() {}
+ }
}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 3d896c8..6e9bc84 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -18,10 +18,12 @@
import static com.android.internal.jank.FrameTracker.ChoreographerWrapper;
import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR;
@@ -114,6 +116,8 @@
public static final int CUJ_LOCKSCREEN_PIN_DISAPPEAR = 22;
public static final int CUJ_LOCKSCREEN_TRANSITION_FROM_AOD = 23;
public static final int CUJ_LOCKSCREEN_TRANSITION_TO_AOD = 24;
+ public static final int CUJ_LAUNCHER_OPEN_ALL_APPS = 25;
+ public static final int CUJ_LAUNCHER_ALL_APPS_SCROLL = 26;
private static final int NO_STATSD_LOGGING = -1;
@@ -147,6 +151,8 @@
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PIN_DISAPPEAR,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_FROM_AOD,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_TRANSITION_TO_AOD,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL,
};
private static volatile InteractionJankMonitor sInstance;
@@ -191,6 +197,8 @@
CUJ_LOCKSCREEN_PIN_DISAPPEAR,
CUJ_LOCKSCREEN_TRANSITION_FROM_AOD,
CUJ_LOCKSCREEN_TRANSITION_TO_AOD,
+ CUJ_LAUNCHER_OPEN_ALL_APPS,
+ CUJ_LAUNCHER_ALL_APPS_SCROLL,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -457,6 +465,10 @@
return "CUJ_LOCKSCREEN_TRANSITION_FROM_AOD";
case CUJ_LOCKSCREEN_TRANSITION_TO_AOD:
return "CUJ_LOCKSCREEN_TRANSITION_TO_AOD";
+ case CUJ_LAUNCHER_OPEN_ALL_APPS :
+ return "CUJ_LAUNCHER_OPEN_ALL_APPS";
+ case CUJ_LAUNCHER_ALL_APPS_SCROLL:
+ return "CUJ_LAUNCHER_ALL_APPS_SCROLL";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
index 8fe17fb..16b6f3a 100644
--- a/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
+++ b/core/java/com/android/internal/os/AmbientDisplayPowerCalculator.java
@@ -43,8 +43,7 @@
*/
@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/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index e76e34f..094724c 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.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 @@
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 @@
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 4c3b950..7d42de4 100644
--- a/core/java/com/android/internal/os/BluetoothPowerCalculator.java
+++ b/core/java/com/android/internal/os/BluetoothPowerCalculator.java
@@ -50,8 +50,7 @@
@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 @@
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/CPU_OWNERS b/core/java/com/android/internal/os/CPU_OWNERS
new file mode 100644
index 0000000..5d3473c
--- /dev/null
+++ b/core/java/com/android/internal/os/CPU_OWNERS
@@ -0,0 +1,3 @@
+connoro@google.com
+dplotnikov@google.com
+rslawik@google.com # Android Telemetry
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index 11c8761..45d8128 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -17,81 +17,166 @@
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 @@
}
}
-
// 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 9ea934a..df25cda 100644
--- a/core/java/com/android/internal/os/GnssPowerCalculator.java
+++ b/core/java/com/android/internal/os/GnssPowerCalculator.java
@@ -45,8 +45,7 @@
@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 dcc8a15..4a4991b 100644
--- a/core/java/com/android/internal/os/IdlePowerCalculator.java
+++ b/core/java/com/android/internal/os/IdlePowerCalculator.java
@@ -49,8 +49,7 @@
@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/KernelCpuTotalBpfMapReader.java b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java
index fa552e3..50331e3 100644
--- a/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java
+++ b/core/java/com/android/internal/os/KernelCpuTotalBpfMapReader.java
@@ -24,10 +24,7 @@
}
/** Returns whether total CPU time is measured. */
- public static boolean isSupported() {
- // TODO(b/174245730): Implement this check.
- return true;
- }
+ public static native boolean isSupported();
/** Reads total CPU time from bpf map. */
public static native boolean read(Callback callback);
diff --git a/core/java/com/android/internal/os/MemoryPowerCalculator.java b/core/java/com/android/internal/os/MemoryPowerCalculator.java
index df46058..21dcce9 100644
--- a/core/java/com/android/internal/os/MemoryPowerCalculator.java
+++ b/core/java/com/android/internal/os/MemoryPowerCalculator.java
@@ -26,8 +26,7 @@
@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 e3bd64d..22001d4 100644
--- a/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
+++ b/core/java/com/android/internal/os/MobileRadioPowerCalculator.java
@@ -85,8 +85,7 @@
@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/OWNERS b/core/java/com/android/internal/os/OWNERS
index 1b07aa0..0aa54f5 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -1,5 +1,6 @@
per-file *Power* = file:/services/core/java/com/android/server/power/OWNERS
per-file *Zygote* = file:/ZYGOTE_OWNERS
+per-file *Cpu* = file:CPU_OWNERS
# BatteryStats
per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS
diff --git a/core/java/com/android/internal/os/PhonePowerCalculator.java b/core/java/com/android/internal/os/PhonePowerCalculator.java
index 6ab8c90..362ca07 100644
--- a/core/java/com/android/internal/os/PhonePowerCalculator.java
+++ b/core/java/com/android/internal/os/PhonePowerCalculator.java
@@ -39,8 +39,7 @@
@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 05fcc70..141363f 100644
--- a/core/java/com/android/internal/os/PowerCalculator.java
+++ b/core/java/com/android/internal/os/PowerCalculator.java
@@ -67,17 +67,10 @@
* @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 @@
*/
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 c86c795..bb1222e 100644
--- a/core/java/com/android/internal/os/ScreenPowerCalculator.java
+++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java
@@ -51,8 +51,7 @@
@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/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
index 55fc1bb..955f6af 100644
--- a/core/java/com/android/internal/os/SystemServicePowerCalculator.java
+++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
@@ -51,10 +51,9 @@
@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 53f8515..8e80286 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 @@
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/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index d196d4a..c8afea9 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -1091,4 +1091,11 @@
* fully-feature Memory Tagging, rather than the static Tagged Pointers.
*/
public static native boolean nativeSupportsTaggedPointers();
+
+ /**
+ * Returns the current native tagging level, as one of the
+ * MEMORY_TAG_LEVEL_* constants. Returns zero if no tagging is present, or
+ * we failed to determine the level.
+ */
+ public static native int nativeCurrentTaggingLevel();
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 94e21e5..f2ed50e 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -787,9 +787,19 @@
Zygote.applyInvokeWithSystemProperty(parsedArgs);
if (Zygote.nativeSupportsMemoryTagging()) {
- /* The system server is more privileged than regular app processes, so it has async
- * tag checks enabled on hardware that supports memory tagging. */
- parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_ASYNC;
+ /* The system server has ASYNC MTE by default, in order to allow
+ * system services to specify their own MTE level later, as you
+ * can't re-enable MTE once it's disabled. */
+ String mode = SystemProperties.get("arm64.memtag.process.system_server", "async");
+ if (mode.equals("async")) {
+ parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_ASYNC;
+ } else if (mode.equals("sync")) {
+ parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_SYNC;
+ } else if (!mode.equals("off")) {
+ /* When we have an invalid memory tag level, keep the current level. */
+ parsedArgs.mRuntimeFlags |= Zygote.nativeCurrentTaggingLevel();
+ Slog.e(TAG, "Unknown memory tag level for the system server: \"" + mode + "\"");
+ }
} else if (Zygote.nativeSupportsTaggedPointers()) {
/* Enable pointer tagging in the system server. Hardware support for this is present
* in all ARMv8 CPUs. */
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index a4ce027..585ddf6 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -492,10 +492,12 @@
long elapsedTimeMs = System.currentTimeMillis() - mUsapPoolRefillTriggerTimestamp;
if (elapsedTimeMs >= mUsapPoolRefillDelayMs) {
- // Normalize the poll timeout value when the time between one poll event and the
- // next pushes us over the delay value. This prevents poll receiving a 0
- // timeout value, which would result in it returning immediately.
- pollTimeoutMs = -1;
+ // The refill delay has elapsed during the period between poll invocations.
+ // We will now check for any currently ready file descriptors before refilling
+ // the USAP pool.
+ pollTimeoutMs = 0;
+ mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
+ mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
} else if (elapsedTimeMs <= 0) {
// This can occur if the clock used by currentTimeMillis is reset, which is
@@ -517,9 +519,11 @@
}
if (pollReturnValue == 0) {
- // The poll timeout has been exceeded. This only occurs when we have finished the
- // USAP pool refill delay period.
-
+ // The poll returned zero results either when the timeout value has been exceeded
+ // or when a non-blocking poll is issued and no FDs are ready. In either case it
+ // is time to refill the pool. This will result in a duplicate assignment when
+ // the non-blocking poll returns zero results, but it avoids an additional
+ // conditional in the else branch.
mUsapPoolRefillTriggerTimestamp = INVALID_TIMESTAMP;
mUsapPoolRefillAction = UsapPoolRefillAction.DELAYED;
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index e7b7bf4..19506a3 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -79,7 +79,7 @@
private static final int DO_CLOSE_CONNECTION = 150;
private static final int DO_COMMIT_CONTENT = 160;
private static final int DO_GET_SURROUNDING_TEXT = 41;
- private static final int DO_SET_IME_TEMPORARILY_CONSUMES_INPUT = 170;
+ private static final int DO_SET_IME_CONSUMES_INPUT = 170;
@GuardedBy("mLock")
@@ -268,13 +268,12 @@
}
/**
- * Dispatches the request for setting ime temporarily consumes input.
+ * Dispatches the request for setting ime consumes input.
*
- * <p>See {@link InputConnection#setImeTemporarilyConsumesInput(boolean)}.
+ * <p>See {@link InputConnection#setImeConsumesInput(boolean)}.
*/
- public void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
- dispatchMessage(obtainMessageB(DO_SET_IME_TEMPORARILY_CONSUMES_INPUT,
- imeTemporarilyConsumesInput));
+ public void setImeConsumesInput(boolean imeConsumesInput) {
+ dispatchMessage(obtainMessageB(DO_SET_IME_CONSUMES_INPUT, imeConsumesInput));
}
void dispatchMessage(Message msg) {
@@ -822,17 +821,17 @@
}
return;
}
- case DO_SET_IME_TEMPORARILY_CONSUMES_INPUT: {
+ case DO_SET_IME_CONSUMES_INPUT: {
Trace.traceBegin(Trace.TRACE_TAG_INPUT,
- "InputConnection#setImeTemporarilyConsumesInput");
+ "InputConnection#setImeConsumesInput");
try {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG,
- "setImeTemporarilyConsumesInput on inactive InputConnection");
+ "setImeConsumesInput on inactive InputConnection");
return;
}
- ic.setImeTemporarilyConsumesInput(msg.arg1 == 1);
+ ic.setImeConsumesInput(msg.arg1 == 1);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
}
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index 586404c..b06b4e5 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -86,5 +86,5 @@
void getSurroundingText(int beforeLength, int afterLength, int flags,
ISurroundingTextResultCallback callback);
- void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput);
+ void setImeConsumesInput(boolean imeConsumesInput);
}
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index 84c92ca..0e9d135 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -525,12 +525,12 @@
}
/**
- * See {@link InputConnection#setImeTemporarilyConsumesInput(boolean)}.
+ * See {@link InputConnection#setImeConsumesInput(boolean)}.
*/
@AnyThread
- public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
+ public boolean setImeConsumesInput(boolean imeConsumesInput) {
try {
- mIInputContext.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput);
+ mIInputContext.setImeConsumesInput(imeConsumesInput);
return true;
} catch (RemoteException e) {
return false;
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 0000000..6cc5a4a
--- /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 40e671f..1b1e0bf 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -18,8 +18,6 @@
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.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 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 @@
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 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 @@
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 @@
@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 @@
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 @@
});
// 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 @@
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 @@
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 @@
}
}
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 @@
}
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 @@
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 @@
} 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 @@
}
}
- 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/DisableImageView.java b/core/java/com/android/internal/widget/DisableImageView.java
new file mode 100644
index 0000000..0d9bf71
--- /dev/null
+++ b/core/java/com/android/internal/widget/DisableImageView.java
@@ -0,0 +1,68 @@
+/*
+ * 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.Nullable;
+import android.content.Context;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+
+/**
+ * ImageView which applies a saturation image filter, used by AppWidgets to represent disabled icon
+ */
+@RemoteViews.RemoteView
+public class DisableImageView extends ImageView {
+
+ public DisableImageView(Context context) {
+ this(context, null, 0, 0);
+ }
+
+ public DisableImageView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0, 0);
+ }
+
+ public DisableImageView(Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public DisableImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ // Apply the disabled filter
+ ColorMatrix brightnessMatrix = new ColorMatrix();
+ float brightnessF = 0.5f;
+ int brightnessI = (int) (255 * brightnessF);
+ // Brightness: C-new = C-old*(1-amount) + amount
+ float scale = 1f - brightnessF;
+ float[] mat = brightnessMatrix.getArray();
+ mat[0] = scale;
+ mat[6] = scale;
+ mat[12] = scale;
+ mat[4] = brightnessI;
+ mat[9] = brightnessI;
+ mat[14] = brightnessI;
+
+ ColorMatrix filterMatrix = new ColorMatrix();
+ filterMatrix.setSaturation(0);
+ filterMatrix.preConcat(brightnessMatrix);
+ setColorFilter(new ColorMatrixColorFilter(filterMatrix));
+ }
+}
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index 4ccf9ce..3d054a5 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -245,11 +245,11 @@
}
@Override
- public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
+ public boolean setImeConsumesInput(boolean imeConsumesInput) {
if (mTextView == null) {
- return super.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput);
+ return super.setImeConsumesInput(imeConsumesInput);
}
- mTextView.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput);
+ mTextView.setImeConsumesInput(imeConsumesInput);
return true;
}
diff --git a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
index 5213746..058a921 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 @@
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 @@
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 c7ea781..8e6497b 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 @@
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 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (mEmphasizedMode) {
+ if (mEmphasizedMode && !mPrioritizedWrapMode) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
@@ -151,7 +155,15 @@
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 @@
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 @@
@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 @@
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 @@
}
/**
+ * 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/OWNERS b/core/java/com/android/internal/widget/OWNERS
index 3fc3933..d284d51 100644
--- a/core/java/com/android/internal/widget/OWNERS
+++ b/core/java/com/android/internal/widget/OWNERS
@@ -11,6 +11,7 @@
per-file *Messaging* = file:/services/core/java/com/android/server/notification/OWNERS
per-file *Message* = file:/services/core/java/com/android/server/notification/OWNERS
per-file *Conversation* = file:/services/core/java/com/android/server/notification/OWNERS
+per-file *People* = file:/services/core/java/com/android/server/notification/OWNERS
per-file *ImageResolver* = file:/services/core/java/com/android/server/notification/OWNERS
per-file CallLayout.java = file:/services/core/java/com/android/server/notification/OWNERS
per-file CachingIconView.java = file:/services/core/java/com/android/server/notification/OWNERS
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 0000000..77f4c8f
--- /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/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index b4d8e50..95999a7 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -23,7 +23,6 @@
import android.os.Build;
import android.os.DropBoxManager;
import android.os.Environment;
-import android.os.FileObserver;
import android.os.FileUtils;
import android.os.RecoverySystem;
import android.os.RemoteException;
@@ -74,7 +73,6 @@
SystemProperties.getInt("ro.debuggable", 0) == 1 ? 196608 : 65536;
private static final int GMSCORE_LASTK_LOG_SIZE = 196608;
- private static final File TOMBSTONE_DIR = new File("/data/tombstones");
private static final String TAG_TOMBSTONE = "SYSTEM_TOMBSTONE";
// The pre-froyo package and class of the system updater, which
@@ -85,9 +83,6 @@
private static final String OLD_UPDATER_CLASS =
"com.google.android.systemupdater.SystemUpdateReceiver";
- // Keep a reference to the observer so the finalizer doesn't disable it.
- private static FileObserver sTombstoneObserver = null;
-
private static final String LOG_FILES_FILE = "log-files.xml";
private static final AtomicFile sFile = new AtomicFile(new File(
Environment.getDataSystemDirectory(), LOG_FILES_FILE), "log-files");
@@ -153,7 +148,7 @@
Downloads.removeAllDownloadsByPackage(context, OLD_UPDATER_PACKAGE, OLD_UPDATER_CLASS);
}
- private String getPreviousBootHeaders() {
+ private static String getPreviousBootHeaders() {
try {
return FileUtils.readTextFile(lastHeaderFile, 0, null);
} catch (IOException e) {
@@ -161,7 +156,7 @@
}
}
- private String getCurrentBootHeaders() throws IOException {
+ private static String getCurrentBootHeaders() throws IOException {
return new StringBuilder(512)
.append("Build: ").append(Build.FINGERPRINT).append("\n")
.append("Hardware: ").append(Build.BOARD).append("\n")
@@ -175,7 +170,7 @@
}
- private String getBootHeadersToLogAndUpdate() throws IOException {
+ private static String getBootHeadersToLogAndUpdate() throws IOException {
final String oldHeaders = getPreviousBootHeaders();
final String newHeaders = getCurrentBootHeaders();
@@ -247,38 +242,27 @@
logFsMountTime();
addFsckErrorsToDropBoxAndLogFsStat(db, timestamps, headers, -LOG_SIZE, "SYSTEM_FSCK");
logSystemServerShutdownTimeMetrics();
-
- // Scan existing tombstones (in case any new ones appeared)
- File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
- for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
- if (tombstoneFiles[i].isFile()) {
- addFileToDropBox(db, timestamps, headers, tombstoneFiles[i].getPath(),
- LOG_SIZE, "SYSTEM_TOMBSTONE");
- }
- }
-
writeTimestamps(timestamps);
+ }
- // Start watching for new tombstone files; will record them as they occur.
- // This gets registered with the singleton file observer thread.
- sTombstoneObserver = new FileObserver(TOMBSTONE_DIR.getPath(), FileObserver.CREATE) {
- @Override
- public void onEvent(int event, String path) {
- HashMap<String, Long> timestamps = readTimestamps();
- try {
- File file = new File(TOMBSTONE_DIR, path);
- if (file.isFile() && file.getName().startsWith("tombstone_")) {
- addFileToDropBox(db, timestamps, headers, file.getPath(), LOG_SIZE,
- TAG_TOMBSTONE);
- }
- } catch (IOException e) {
- Slog.e(TAG, "Can't log tombstone", e);
- }
- writeTimestamps(timestamps);
- }
- };
-
- sTombstoneObserver.startWatching();
+ /**
+ * Add a tombstone to the DropBox.
+ *
+ * @param ctx Context
+ * @param tombstone path to the tombstone
+ */
+ public static void addTombstoneToDropBox(Context ctx, File tombstone) {
+ final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
+ final String bootReason = SystemProperties.get("ro.boot.bootreason", null);
+ HashMap<String, Long> timestamps = readTimestamps();
+ try {
+ final String headers = getBootHeadersToLogAndUpdate();
+ addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
+ TAG_TOMBSTONE);
+ } catch (IOException e) {
+ Slog.e(TAG, "Can't log tombstone", e);
+ }
+ writeTimestamps(timestamps);
}
private static void addLastkToDropBox(
@@ -761,7 +745,7 @@
}
}
- private void writeTimestamps(HashMap<String, Long> timestamps) {
+ private static void writeTimestamps(HashMap<String, Long> timestamps) {
synchronized (sFile) {
final FileOutputStream stream;
try {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 0b48e72..8edc8a1 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -207,9 +207,9 @@
],
shared_libs: [
- "audioclient-types-aidl-unstable-cpp",
- "audioflinger-aidl-unstable-cpp",
- "av-types-aidl-unstable-cpp",
+ "audioclient-types-aidl-cpp",
+ "audioflinger-aidl-cpp",
+ "av-types-aidl-cpp",
"libandroidicu",
"libbpf_android",
"libnetdbpf",
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 2279d57..35d1d7b 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -5,6 +5,9 @@
# Connectivity
per-file android_net_* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
+# CPU
+per-file *Cpu* = file:/core/java/com/android/internal/os/CPU_OWNERS
+
# Display
per-file android_hardware_display_* = file:/services/core/java/com/android/server/display/OWNERS
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index bb51c57..c64174b 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 @@
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 @@
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 @@
}
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 @@
(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/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 6337680..8977b97 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -40,7 +40,7 @@
jmethodID dispatchVsync;
jmethodID dispatchHotplug;
- jmethodID dispatchConfigChanged;
+ jmethodID dispatchModeChanged;
jmethodID dispatchFrameRateOverrides;
struct {
@@ -69,8 +69,8 @@
void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
VsyncEventData vsyncEventData) override;
void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
- void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
- int32_t configId, nsecs_t vsyncPeriod) override;
+ void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
+ nsecs_t vsyncPeriod) override;
void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
std::vector<FrameRateOverride> overrides) override;
void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) override {}
@@ -129,20 +129,19 @@
mMessageQueue->raiseAndClearException(env, "dispatchHotplug");
}
-void NativeDisplayEventReceiver::dispatchConfigChanged(
- nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId, nsecs_t) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+void NativeDisplayEventReceiver::dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
+ int32_t modeId, nsecs_t) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
- ScopedLocalRef<jobject> receiverObj(env,
- jniGetReferent(env, mReceiverWeakGlobal));
- if (receiverObj.get()) {
- ALOGV("receiver %p ~ Invoking config changed handler.", this);
- env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchConfigChanged,
- timestamp, displayId.value, configId);
- ALOGV("receiver %p ~ Returned from config changed handler.", this);
- }
+ ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
+ if (receiverObj.get()) {
+ ALOGV("receiver %p ~ Invoking mode changed handler.", this);
+ env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchModeChanged,
+ timestamp, displayId.value, modeId);
+ ALOGV("receiver %p ~ Returned from mode changed handler.", this);
+ }
- mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged");
+ mMessageQueue->raiseAndClearException(env, "dispatchModeChanged");
}
void NativeDisplayEventReceiver::dispatchFrameRateOverrides(
@@ -173,7 +172,7 @@
ALOGV("receiver %p ~ Returned from FrameRateOverride handler.", this);
}
- mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged");
+ mMessageQueue->raiseAndClearException(env, "dispatchModeChanged");
}
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj,
@@ -243,8 +242,9 @@
"(JJIJJ)V");
gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
- gDisplayEventReceiverClassInfo.dispatchConfigChanged = GetMethodIDOrDie(env,
- gDisplayEventReceiverClassInfo.clazz, "dispatchConfigChanged", "(JJI)V");
+ gDisplayEventReceiverClassInfo.dispatchModeChanged =
+ GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchModeChanged",
+ "(JJI)V");
gDisplayEventReceiverClassInfo.dispatchFrameRateOverrides =
GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz,
"dispatchFrameRateOverrides",
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 05fcaec..7a3366a 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -44,8 +44,8 @@
#include <ui/BlurRegion.h>
#include <ui/ConfigStoreTypes.h>
#include <ui/DeviceProductInfo.h>
-#include <ui/DisplayConfig.h>
#include <ui/DisplayInfo.h>
+#include <ui/DisplayMode.h>
#include <ui/DisplayedFrameStats.h>
#include <ui/FrameStats.h>
#include <ui/GraphicTypes.h>
@@ -98,8 +98,8 @@
jfieldID refreshRate;
jfieldID appVsyncOffsetNanos;
jfieldID presentationDeadlineNanos;
- jfieldID configGroup;
-} gDisplayConfigClassInfo;
+ jfieldID group;
+} gDisplayModeClassInfo;
static struct {
jfieldID bottom;
@@ -202,13 +202,13 @@
static struct {
jclass clazz;
jmethodID ctor;
- jfieldID defaultConfig;
+ jfieldID defaultMode;
jfieldID allowGroupSwitching;
jfieldID primaryRefreshRateMin;
jfieldID primaryRefreshRateMax;
jfieldID appRequestRefreshRateMin;
jfieldID appRequestRefreshRateMax;
-} gDesiredDisplayConfigSpecsClassInfo;
+} gDesiredDisplayModeSpecsClassInfo;
static struct {
jclass clazz;
@@ -1028,101 +1028,97 @@
return object;
}
-static jobjectArray nativeGetDisplayConfigs(JNIEnv* env, jclass clazz, jobject tokenObj) {
- Vector<DisplayConfig> configs;
+static jobjectArray nativeGetDisplayModes(JNIEnv* env, jclass clazz, jobject tokenObj) {
+ Vector<ui::DisplayMode> modes;
if (const auto token = ibinderForJavaObject(env, tokenObj); !token ||
- SurfaceComposerClient::getDisplayConfigs(token, &configs) != NO_ERROR ||
- configs.isEmpty()) {
+ SurfaceComposerClient::getDisplayModes(token, &modes) != NO_ERROR || modes.isEmpty()) {
return nullptr;
}
- jobjectArray configArray =
- env->NewObjectArray(configs.size(), gDisplayConfigClassInfo.clazz, nullptr);
+ jobjectArray modesArray =
+ env->NewObjectArray(modes.size(), gDisplayModeClassInfo.clazz, nullptr);
- for (size_t c = 0; c < configs.size(); ++c) {
- const DisplayConfig& config = configs[c];
- jobject object =
- env->NewObject(gDisplayConfigClassInfo.clazz, gDisplayConfigClassInfo.ctor);
- env->SetIntField(object, gDisplayConfigClassInfo.width, config.resolution.getWidth());
- env->SetIntField(object, gDisplayConfigClassInfo.height, config.resolution.getHeight());
- env->SetFloatField(object, gDisplayConfigClassInfo.xDpi, config.xDpi);
- env->SetFloatField(object, gDisplayConfigClassInfo.yDpi, config.yDpi);
+ for (size_t c = 0; c < modes.size(); ++c) {
+ const ui::DisplayMode& mode = modes[c];
+ jobject object = env->NewObject(gDisplayModeClassInfo.clazz, gDisplayModeClassInfo.ctor);
+ env->SetIntField(object, gDisplayModeClassInfo.width, mode.resolution.getWidth());
+ env->SetIntField(object, gDisplayModeClassInfo.height, mode.resolution.getHeight());
+ env->SetFloatField(object, gDisplayModeClassInfo.xDpi, mode.xDpi);
+ env->SetFloatField(object, gDisplayModeClassInfo.yDpi, mode.yDpi);
- env->SetFloatField(object, gDisplayConfigClassInfo.refreshRate, config.refreshRate);
- env->SetLongField(object, gDisplayConfigClassInfo.appVsyncOffsetNanos,
- config.appVsyncOffset);
- env->SetLongField(object, gDisplayConfigClassInfo.presentationDeadlineNanos,
- config.presentationDeadline);
- env->SetIntField(object, gDisplayConfigClassInfo.configGroup, config.configGroup);
- env->SetObjectArrayElement(configArray, static_cast<jsize>(c), object);
+ env->SetFloatField(object, gDisplayModeClassInfo.refreshRate, mode.refreshRate);
+ env->SetLongField(object, gDisplayModeClassInfo.appVsyncOffsetNanos, mode.appVsyncOffset);
+ env->SetLongField(object, gDisplayModeClassInfo.presentationDeadlineNanos,
+ mode.presentationDeadline);
+ env->SetIntField(object, gDisplayModeClassInfo.group, mode.group);
+ env->SetObjectArrayElement(modesArray, static_cast<jsize>(c), object);
env->DeleteLocalRef(object);
}
- return configArray;
+ return modesArray;
}
-static jboolean nativeSetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, jobject tokenObj,
- jobject desiredDisplayConfigSpecs) {
+static jboolean nativeSetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobject tokenObj,
+ jobject DesiredDisplayModeSpecs) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == nullptr) return JNI_FALSE;
- jint defaultConfig = env->GetIntField(desiredDisplayConfigSpecs,
- gDesiredDisplayConfigSpecsClassInfo.defaultConfig);
+ size_t defaultMode =
+ static_cast<size_t>(env->GetIntField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.defaultMode));
jboolean allowGroupSwitching =
- env->GetBooleanField(desiredDisplayConfigSpecs,
- gDesiredDisplayConfigSpecsClassInfo.allowGroupSwitching);
+ env->GetBooleanField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching);
jfloat primaryRefreshRateMin =
- env->GetFloatField(desiredDisplayConfigSpecs,
- gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMin);
+ env->GetFloatField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMin);
jfloat primaryRefreshRateMax =
- env->GetFloatField(desiredDisplayConfigSpecs,
- gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMax);
+ env->GetFloatField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMax);
jfloat appRequestRefreshRateMin =
- env->GetFloatField(desiredDisplayConfigSpecs,
- gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMin);
+ env->GetFloatField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMin);
jfloat appRequestRefreshRateMax =
- env->GetFloatField(desiredDisplayConfigSpecs,
- gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMax);
+ env->GetFloatField(DesiredDisplayModeSpecs,
+ gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMax);
- size_t result = SurfaceComposerClient::setDesiredDisplayConfigSpecs(token, defaultConfig,
- allowGroupSwitching,
- primaryRefreshRateMin,
- primaryRefreshRateMax,
- appRequestRefreshRateMin,
- appRequestRefreshRateMax);
+ size_t result = SurfaceComposerClient::setDesiredDisplayModeSpecs(token, defaultMode,
+ allowGroupSwitching,
+ primaryRefreshRateMin,
+ primaryRefreshRateMax,
+ appRequestRefreshRateMin,
+ appRequestRefreshRateMax);
return result == NO_ERROR ? JNI_TRUE : JNI_FALSE;
}
-static jobject nativeGetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, jobject tokenObj) {
+static jobject nativeGetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobject tokenObj) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == nullptr) return nullptr;
- int32_t defaultConfig;
+ size_t defaultMode;
bool allowGroupSwitching;
float primaryRefreshRateMin;
float primaryRefreshRateMax;
float appRequestRefreshRateMin;
float appRequestRefreshRateMax;
- if (SurfaceComposerClient::getDesiredDisplayConfigSpecs(token, &defaultConfig,
- &allowGroupSwitching,
- &primaryRefreshRateMin,
- &primaryRefreshRateMax,
- &appRequestRefreshRateMin,
- &appRequestRefreshRateMax) !=
- NO_ERROR) {
+ if (SurfaceComposerClient::getDesiredDisplayModeSpecs(token, &defaultMode, &allowGroupSwitching,
+ &primaryRefreshRateMin,
+ &primaryRefreshRateMax,
+ &appRequestRefreshRateMin,
+ &appRequestRefreshRateMax) != NO_ERROR) {
return nullptr;
}
- return env->NewObject(gDesiredDisplayConfigSpecsClassInfo.clazz,
- gDesiredDisplayConfigSpecsClassInfo.ctor, defaultConfig,
- allowGroupSwitching, primaryRefreshRateMin, primaryRefreshRateMax,
- appRequestRefreshRateMin, appRequestRefreshRateMax);
+ return env->NewObject(gDesiredDisplayModeSpecsClassInfo.clazz,
+ gDesiredDisplayModeSpecsClassInfo.ctor, defaultMode, allowGroupSwitching,
+ primaryRefreshRateMin, primaryRefreshRateMax, appRequestRefreshRateMin,
+ appRequestRefreshRateMax);
}
-static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) {
+static jint nativeGetActiveDisplayMode(JNIEnv* env, jclass clazz, jobject tokenObj) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == NULL) return -1;
- return static_cast<jint>(SurfaceComposerClient::getActiveConfig(token));
+ return static_cast<jint>(SurfaceComposerClient::getActiveDisplayModeId(token));
}
static jintArray nativeGetDisplayColorModes(JNIEnv* env, jclass, jobject tokenObj) {
@@ -1409,16 +1405,6 @@
transaction->deferTransactionUntil_legacy(ctrl, barrier, frameNumber);
}
-static void nativeReparentChildren(JNIEnv* env, jclass clazz, jlong transactionObj,
- jlong nativeObject,
- jlong newParentObject) {
-
- auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
- auto newParent = reinterpret_cast<SurfaceControl *>(newParentObject);
- auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
- transaction->reparentChildren(ctrl, newParent);
-}
-
static void nativeReparent(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject,
jlong newParentObject) {
@@ -1794,16 +1780,16 @@
(void*)nativeSetDisplaySize },
{"nativeGetDisplayInfo", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayInfo;",
(void*)nativeGetDisplayInfo },
- {"nativeGetDisplayConfigs", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$DisplayConfig;",
- (void*)nativeGetDisplayConfigs },
- {"nativeGetActiveConfig", "(Landroid/os/IBinder;)I",
- (void*)nativeGetActiveConfig },
- {"nativeSetDesiredDisplayConfigSpecs",
- "(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayConfigSpecs;)Z",
- (void*)nativeSetDesiredDisplayConfigSpecs },
- {"nativeGetDesiredDisplayConfigSpecs",
- "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DesiredDisplayConfigSpecs;",
- (void*)nativeGetDesiredDisplayConfigSpecs },
+ {"nativeGetDisplayModes", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$DisplayMode;",
+ (void*)nativeGetDisplayModes },
+ {"nativeGetActiveDisplayMode", "(Landroid/os/IBinder;)I",
+ (void*)nativeGetActiveDisplayMode },
+ {"nativeSetDesiredDisplayModeSpecs",
+ "(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayModeSpecs;)Z",
+ (void*)nativeSetDesiredDisplayModeSpecs },
+ {"nativeGetDesiredDisplayModeSpecs",
+ "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DesiredDisplayModeSpecs;",
+ (void*)nativeGetDesiredDisplayModeSpecs },
{"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I",
(void*)nativeGetDisplayColorModes},
{"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;",
@@ -1838,8 +1824,6 @@
(void*)nativeGetProtectedContentSupport },
{"nativeDeferTransactionUntil", "(JJJJ)V",
(void*)nativeDeferTransactionUntil },
- {"nativeReparentChildren", "(JJJ)V",
- (void*)nativeReparentChildren } ,
{"nativeReparent", "(JJJ)V",
(void*)nativeReparent },
{"nativeCaptureDisplay",
@@ -1914,19 +1898,19 @@
GetFieldIDOrDie(env, infoClazz, "deviceProductInfo",
"Landroid/hardware/display/DeviceProductInfo;");
- jclass configClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayConfig");
- gDisplayConfigClassInfo.clazz = MakeGlobalRefOrDie(env, configClazz);
- gDisplayConfigClassInfo.ctor = GetMethodIDOrDie(env, configClazz, "<init>", "()V");
- gDisplayConfigClassInfo.width = GetFieldIDOrDie(env, configClazz, "width", "I");
- gDisplayConfigClassInfo.height = GetFieldIDOrDie(env, configClazz, "height", "I");
- gDisplayConfigClassInfo.xDpi = GetFieldIDOrDie(env, configClazz, "xDpi", "F");
- gDisplayConfigClassInfo.yDpi = GetFieldIDOrDie(env, configClazz, "yDpi", "F");
- gDisplayConfigClassInfo.refreshRate = GetFieldIDOrDie(env, configClazz, "refreshRate", "F");
- gDisplayConfigClassInfo.appVsyncOffsetNanos =
- GetFieldIDOrDie(env, configClazz, "appVsyncOffsetNanos", "J");
- gDisplayConfigClassInfo.presentationDeadlineNanos =
- GetFieldIDOrDie(env, configClazz, "presentationDeadlineNanos", "J");
- gDisplayConfigClassInfo.configGroup = GetFieldIDOrDie(env, configClazz, "configGroup", "I");
+ jclass modeClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayMode");
+ gDisplayModeClassInfo.clazz = MakeGlobalRefOrDie(env, modeClazz);
+ gDisplayModeClassInfo.ctor = GetMethodIDOrDie(env, modeClazz, "<init>", "()V");
+ gDisplayModeClassInfo.width = GetFieldIDOrDie(env, modeClazz, "width", "I");
+ gDisplayModeClassInfo.height = GetFieldIDOrDie(env, modeClazz, "height", "I");
+ gDisplayModeClassInfo.xDpi = GetFieldIDOrDie(env, modeClazz, "xDpi", "F");
+ gDisplayModeClassInfo.yDpi = GetFieldIDOrDie(env, modeClazz, "yDpi", "F");
+ gDisplayModeClassInfo.refreshRate = GetFieldIDOrDie(env, modeClazz, "refreshRate", "F");
+ gDisplayModeClassInfo.appVsyncOffsetNanos =
+ GetFieldIDOrDie(env, modeClazz, "appVsyncOffsetNanos", "J");
+ gDisplayModeClassInfo.presentationDeadlineNanos =
+ GetFieldIDOrDie(env, modeClazz, "presentationDeadlineNanos", "J");
+ gDisplayModeClassInfo.group = GetFieldIDOrDie(env, modeClazz, "group", "I");
jclass rectClazz = FindClassOrDie(env, "android/graphics/Rect");
gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClazz, "bottom", "I");
@@ -2017,24 +2001,23 @@
gDisplayPrimariesClassInfo.white = GetFieldIDOrDie(env, displayPrimariesClazz, "white",
"Landroid/view/SurfaceControl$CieXyz;");
- jclass desiredDisplayConfigSpecsClazz =
- FindClassOrDie(env, "android/view/SurfaceControl$DesiredDisplayConfigSpecs");
- gDesiredDisplayConfigSpecsClassInfo.clazz =
- MakeGlobalRefOrDie(env, desiredDisplayConfigSpecsClazz);
- gDesiredDisplayConfigSpecsClassInfo.ctor =
- GetMethodIDOrDie(env, gDesiredDisplayConfigSpecsClassInfo.clazz, "<init>", "(IZFFFF)V");
- gDesiredDisplayConfigSpecsClassInfo.defaultConfig =
- GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "defaultConfig", "I");
- gDesiredDisplayConfigSpecsClassInfo.allowGroupSwitching =
- GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "allowGroupSwitching", "Z");
- gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMin =
- GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "primaryRefreshRateMin", "F");
- gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMax =
- GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "primaryRefreshRateMax", "F");
- gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMin =
- GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "appRequestRefreshRateMin", "F");
- gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMax =
- GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "appRequestRefreshRateMax", "F");
+ jclass DesiredDisplayModeSpecsClazz =
+ FindClassOrDie(env, "android/view/SurfaceControl$DesiredDisplayModeSpecs");
+ gDesiredDisplayModeSpecsClassInfo.clazz = MakeGlobalRefOrDie(env, DesiredDisplayModeSpecsClazz);
+ gDesiredDisplayModeSpecsClassInfo.ctor =
+ GetMethodIDOrDie(env, gDesiredDisplayModeSpecsClassInfo.clazz, "<init>", "(IZFFFF)V");
+ gDesiredDisplayModeSpecsClassInfo.defaultMode =
+ GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "defaultMode", "I");
+ gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching =
+ GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "allowGroupSwitching", "Z");
+ gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMin =
+ GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "primaryRefreshRateMin", "F");
+ gDesiredDisplayModeSpecsClassInfo.primaryRefreshRateMax =
+ GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "primaryRefreshRateMax", "F");
+ gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMin =
+ GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "appRequestRefreshRateMin", "F");
+ gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMax =
+ GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "appRequestRefreshRateMax", "F");
jclass captureArgsClazz = FindClassOrDie(env, "android/view/SurfaceControl$CaptureArgs");
gCaptureArgsClassInfo.pixelFormat = GetFieldIDOrDie(env, captureArgsClazz, "mPixelFormat", "I");
diff --git a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp
index 7249238..d8446ca 100644
--- a/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp
+++ b/core/jni/com_android_internal_os_KernelCpuTotalBpfMapReader.cpp
@@ -20,6 +20,10 @@
namespace android {
+static jboolean KernelCpuTotalBpfMapReader_isSupported(JNIEnv *, jobject) {
+ return android::bpf::isTrackingUidTimesSupported() ? JNI_TRUE : JNI_FALSE;
+}
+
static jboolean KernelCpuTotalBpfMapReader_read(JNIEnv *env, jobject, jobject callback) {
jclass callbackClass = env->GetObjectClass(callback);
jmethodID callbackMethod = env->GetMethodID(callbackClass, "accept", "(IIJ)V");
@@ -47,6 +51,7 @@
static const JNINativeMethod methods[] = {
{"read", "(Lcom/android/internal/os/KernelCpuTotalBpfMapReader$Callback;)Z",
(void *)KernelCpuTotalBpfMapReader_read},
+ {"isSupported", "()Z", (void *)KernelCpuTotalBpfMapReader_isSupported},
};
int register_com_android_internal_os_KernelCpuTotalBpfMapReader(JNIEnv *env) {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 3d1c38d..e801725 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -2506,6 +2506,36 @@
#endif
}
+static jint com_android_internal_os_Zygote_nativeCurrentTaggingLevel(JNIEnv* env, jclass) {
+#if defined(__aarch64__)
+ int level = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+ if (level < 0) {
+ ALOGE("Failed to get memory tag level: %s", strerror(errno));
+ return 0;
+ } else if (!(level & PR_TAGGED_ADDR_ENABLE)) {
+ return 0;
+ }
+ // TBI is only possible on non-MTE hardware.
+ if (!mte_supported()) {
+ return MEMORY_TAG_LEVEL_TBI;
+ }
+
+ switch (level & PR_MTE_TCF_MASK) {
+ case PR_MTE_TCF_NONE:
+ return 0;
+ case PR_MTE_TCF_SYNC:
+ return MEMORY_TAG_LEVEL_SYNC;
+ case PR_MTE_TCF_ASYNC:
+ return MEMORY_TAG_LEVEL_ASYNC;
+ default:
+ ALOGE("Unknown memory tagging level: %i", level);
+ return 0;
+ }
+#else // defined(__aarch64__)
+ return 0;
+#endif // defined(__aarch64__)
+}
+
static const JNINativeMethod gMethods[] = {
{"nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/"
@@ -2545,6 +2575,8 @@
(void*)com_android_internal_os_Zygote_nativeSupportsMemoryTagging},
{"nativeSupportsTaggedPointers", "()Z",
(void*)com_android_internal_os_Zygote_nativeSupportsTaggedPointers},
+ {"nativeCurrentTaggingLevel", "()I",
+ (void*)com_android_internal_os_Zygote_nativeCurrentTaggingLevel},
};
int register_com_android_internal_os_Zygote(JNIEnv* env) {
diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto
index bb39ea8..5c6116a 100644
--- a/core/proto/android/content/package_item_info.proto
+++ b/core/proto/android/content/package_item_info.proto
@@ -114,4 +114,5 @@
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 6794c8c..0041f19 100644
--- a/core/proto/android/net/networkrequest.proto
+++ b/core/proto/android/net/networkrequest.proto
@@ -63,6 +63,9 @@
// 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 08c907b..856657a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -690,10 +690,6 @@
<!-- Made protected in S (was added in R) -->
<protected-broadcast android:name="com.android.internal.intent.action.BUGREPORT_REQUESTED" />
- <!-- Added in S -->
- <protected-broadcast android:name="android.app.action.MANAGED_PROFILE_CREATED" />
- <protected-broadcast android:name="android.app.action.PROVISIONED_MANAGED_DEVICE" />
-
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -2702,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},
@@ -5431,7 +5427,7 @@
<permission android:name="android.permission.INPUT_CONSUMER"
android:protectionLevel="signature" />
- <!-- @hide Allows an application to control the system's device state managed by the
+ <!-- @hide @TestApi Allows an application to control the system's device state managed by the
{@link android.service.devicestate.DeviceStateManagerService}. For example, on foldable
devices this would grant access to toggle between the folded and unfolded states. -->
<permission android:name="android.permission.CONTROL_DEVICE_STATE"
@@ -5451,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/drawable/btn_notification_emphasized.xml b/core/res/res/drawable/btn_notification_emphasized.xml
index 1a574fe..ad68054 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 0000000..77c0ad1
--- /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 0000000..a5ee8f4
--- /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/notification_material_action_emphasized.xml b/core/res/res/layout/notification_material_action_emphasized.xml
index a6b7b38..cd1f1ab 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 0000000..b018676
--- /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 0000000..e9ec7ce
--- /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 0000000..471d874
--- /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 f9364d5..f3aa540 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_template_part_chronometer.xml b/core/res/res/layout/notification_template_part_chronometer.xml
index c5ffbea..e4ebc76 100644
--- a/core/res/res/layout/notification_template_part_chronometer.xml
+++ b/core/res/res/layout/notification_template_part_chronometer.xml
@@ -15,7 +15,7 @@
-->
<Chronometer android:id="@+id/chronometer" xmlns:android="http://schemas.android.com/apk/res/android"
- android:textAppearance="@style/TextAppearance.Material.Notification.Time"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="4dp"
diff --git a/core/res/res/layout/notification_top_line_views.xml b/core/res/res/layout/notification_top_line_views.xml
index 7cda03f..7656dd5 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/layout/work_widget_mask_view.xml b/core/res/res/layout/work_widget_mask_view.xml
index 39e1bbb..9e1692f 100644
--- a/core/res/res/layout/work_widget_mask_view.xml
+++ b/core/res/res/layout/work_widget_mask_view.xml
@@ -22,7 +22,8 @@
android:importantForAccessibility="noHideDescendants"
android:clickable="true">
- <ImageView android:id="@+id/work_widget_app_icon"
+ <com.android.internal.widget.DisableImageView
+ android:id="@+id/work_widget_app_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index eb86709..586c99d7 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. -->
@@ -7969,9 +7979,17 @@
<!-- A class name in the AppWidget's package to be launched to configure.
If not supplied, then no activity will be launched. -->
<attr name="configure" format="string" />
- <!-- A preview of what the AppWidget will look like after it's configured.
- If not supplied, the AppWidget's icon will be used. -->
+ <!-- A preview, in a drawable resource id, of what the AppWidget will look like after it's
+ configured.
+ If not supplied, the AppWidget's icon will be used. -->
<attr name="previewImage" format="reference" />
+ <!-- The layout resource id of a preview of what the AppWidget will look like after it's
+ configured.
+ Unlike previewImage, previewLayout can better showcase AppWidget in different locales,
+ system themes, display sizes & density etc.
+ If supplied, this will take precedence over the previewImage on supported widget hosts.
+ Otherwise, previewImage will be used. -->
+ <attr name="previewLayout" format="reference" />
<!-- The view id of the AppWidget subview which should be auto-advanced.
by the widget's host. -->
<attr name="autoAdvanceViewId" format="reference" />
@@ -8500,6 +8518,9 @@
interaction requests from an Activity. This flag is new in
{@link android.os.Build.VERSION_CODES#N} and not used in previous versions. -->
<attr name="supportsLocalInteraction" format="boolean" />
+ <!-- The service that provides {@link android.service.voice.HotwordDetectionService}.
+ @hide @SystemApi -->
+ <attr name="hotwordDetectionService" format="string" />
</declare-styleable>
<!-- Use <code>voice-enrollment-application</code>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 20a5d37..5546621 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 c8cccc4..f723426 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/config.xml b/core/res/res/values/config.xml
index 3790aa4..beae935 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3477,6 +3477,11 @@
<!-- The alarm window (in milliseconds) that JobScheduler uses to enter the idle state -->
<integer name="config_jobSchedulerIdleWindowSlop">300000</integer>
+ <!-- If true, jobs from background user will be restricted -->
+ <bool name="config_jobSchedulerRestrictBackgroundUser">false</bool>
+ <!-- The length of grace period after user becomes background user -->
+ <integer name="config_jobSchedulerUserGracePeriod">60000</integer>
+
<!-- If true, all guest users created on the device will be ephemeral. -->
<bool name="config_guestUserEphemeral">false</bool>
@@ -4710,4 +4715,7 @@
<!-- 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 cb16af5..3b6e41f 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>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index a4c7293..ab4e0f3 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 a7e8f2a..d3f3ebd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3059,6 +3059,10 @@
<public name="hand_second" />
<public name="memtagMode" />
<public name="nativeHeapZeroInit" />
+ <!-- @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 98b36c5..af5e406 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1585,6 +1585,8 @@
<!-- Template to be used to name enrolled fingerprints by default. -->
<string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string>
+ <!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with their fingerprint. [CHAR LIMIT=70] -->
+ <string name="fingerprint_dialog_default_subtitle">Use your fingerprint to continue</string>
<!-- Array containing custom error messages from vendor. Vendor is expected to add and translate these strings -->
<string-array name="fingerprint_error_vendor">
@@ -4530,9 +4532,8 @@
shown in the warning dialog about the accessibility shortcut. -->
<string name="color_correction_feature_name">Color Correction</string>
- <!-- TODO(b/170970602): remove translatable=false when RBC has official name and strings -->
- <!-- Title of Reduce Bright Colors feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
- <string name="reduce_bright_colors_feature_name" translatable="false">Reduce Bright Colors</string>
+ <!-- Title of Reduce Brightness feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
+ <string name="reduce_bright_colors_feature_name">Reduce Brightness</string>
<!-- Text in toast to alert the user that the accessibility shortcut turned on an accessibility service. [CHAR LIMIT=none] -->
<string name="accessibility_shortcut_enabling_service">Held volume keys. <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> turned on.</string>
@@ -5038,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/symbols.xml b/core/res/res/values/symbols.xml
index 80163b1..4109d4c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2488,6 +2488,7 @@
<java-symbol type="string" name="fingerprint_error_lockout" />
<java-symbol type="string" name="fingerprint_error_lockout_permanent" />
<java-symbol type="string" name="fingerprint_name_template" />
+ <java-symbol type="string" name="fingerprint_dialog_default_subtitle" />
<java-symbol type="string" name="fingerprint_authenticated" />
<java-symbol type="string" name="fingerprint_error_no_fingerprints" />
<java-symbol type="string" name="fingerprint_error_hw_not_present" />
@@ -2646,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" />
@@ -2679,6 +2681,8 @@
<java-symbol type="integer" name="config_jobSchedulerInactivityIdleThreshold" />
<java-symbol type="integer" name="config_jobSchedulerIdleWindowSlop" />
+ <java-symbol type="bool" name="config_jobSchedulerRestrictBackgroundUser" />
+ <java-symbol type="integer" name="config_jobSchedulerUserGracePeriod" />
<java-symbol type="style" name="Animation.ImmersiveModeConfirmation" />
@@ -3085,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" />
@@ -3412,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"/>
@@ -4175,6 +4198,4 @@
<java-symbol type="bool" name="config_telephony5gNonStandalone" />
<java-symbol type="bool" name="config_voice_data_sms_auto_fallback" />
-
- <java-symbol type="bool" name="config_enableOneHandedKeyguard" />
</resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index ee17f6f..ce4ee87 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -222,6 +222,8 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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 @@
<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/tests/coretests/src/android/app/WindowContextTest.java b/core/tests/coretests/src/android/app/WindowContextTest.java
index da7304e..48b58c6 100644
--- a/core/tests/coretests/src/android/app/WindowContextTest.java
+++ b/core/tests/coretests/src/android/app/WindowContextTest.java
@@ -184,7 +184,8 @@
final IBinder token = windowContext.getWindowContextToken();
final IBinder existingToken = new Binder();
- mWms.addWindowToken(existingToken, TYPE_INPUT_METHOD, windowContext.getDisplayId());
+ mWms.addWindowToken(existingToken, TYPE_INPUT_METHOD, windowContext.getDisplayId(),
+ null /* options */);
final WindowManager.LayoutParams params =
new WindowManager.LayoutParams(TYPE_INPUT_METHOD);
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 cfcfcc8..ece37f8 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 @@
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 45adf83..46dbe0f 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 @@
@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 @@
@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 @@
@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 @@
@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 @@
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 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 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 @@
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 @@
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 @@
// 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/android/graphics/FontListParserTest.java b/core/tests/coretests/src/android/graphics/FontListParserTest.java
new file mode 100644
index 0000000..eae41e3
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/FontListParserTest.java
@@ -0,0 +1,245 @@
+/*
+ * 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.graphics;
+
+import static android.graphics.fonts.FontStyle.FONT_SLANT_ITALIC;
+import static android.graphics.fonts.FontStyle.FONT_SLANT_UPRIGHT;
+import static android.graphics.fonts.FontStyle.FONT_WEIGHT_NORMAL;
+import static android.text.FontConfig.FontFamily.VARIANT_COMPACT;
+import static android.text.FontConfig.FontFamily.VARIANT_DEFAULT;
+import static android.text.FontConfig.FontFamily.VARIANT_ELEGANT;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.graphics.fonts.FontStyle;
+import android.os.LocaleList;
+import android.text.FontConfig;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class FontListParserTest {
+
+ @Test
+ public void named() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<family name='sans-serif'>"
+ + " <font>test.ttf</font>"
+ + "</family>";
+ FontConfig.FontFamily expected = new FontConfig.FontFamily(
+ Arrays.asList(
+ new FontConfig.Font(new File("test.ttf"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "", null)),
+ "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
+
+ FontConfig.FontFamily family = readFamily(xml);
+ assertThat(family).isEqualTo(expected);
+
+ String serialized = writeFamily(family);
+ assertWithMessage("serialized = " + serialized)
+ .that(readFamily(serialized)).isEqualTo(expected);
+ }
+
+ @Test
+ public void fallback() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<family lang='en'>"
+ + " <font>test.ttf</font>"
+ + " <font fallbackFor='serif'>test.ttf</font>"
+ + "</family>";
+ FontConfig.FontFamily expected = new FontConfig.FontFamily(
+ Arrays.asList(
+ new FontConfig.Font(new File("test.ttf"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "", null),
+ new FontConfig.Font(new File("test.ttf"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "", "serif")),
+ null, LocaleList.forLanguageTags("en"), VARIANT_DEFAULT);
+
+ FontConfig.FontFamily family = readFamily(xml);
+ assertThat(family).isEqualTo(expected);
+
+ String serialized = writeFamily(family);
+ assertWithMessage("serialized = " + serialized)
+ .that(readFamily(serialized)).isEqualTo(expected);
+ }
+
+ @Test
+ public void compact() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<family lang='en' variant='compact'>"
+ + " <font>test.ttf</font>"
+ + "</family>";
+ FontConfig.FontFamily expected = new FontConfig.FontFamily(
+ Arrays.asList(
+ new FontConfig.Font(new File("test.ttf"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "", null)),
+ null, LocaleList.forLanguageTags("en"), VARIANT_COMPACT);
+
+ FontConfig.FontFamily family = readFamily(xml);
+ assertThat(family).isEqualTo(expected);
+
+ String serialized = writeFamily(family);
+ assertWithMessage("serialized = " + serialized)
+ .that(readFamily(serialized)).isEqualTo(expected);
+ }
+
+ @Test
+ public void elegant() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<family lang='en' variant='elegant'>"
+ + " <font>test.ttf</font>"
+ + "</family>";
+ FontConfig.FontFamily expected = new FontConfig.FontFamily(
+ Arrays.asList(
+ new FontConfig.Font(new File("test.ttf"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "", null)),
+ null, LocaleList.forLanguageTags("en"), VARIANT_ELEGANT);
+
+ FontConfig.FontFamily family = readFamily(xml);
+ assertThat(family).isEqualTo(expected);
+
+ String serialized = writeFamily(family);
+ assertWithMessage("serialized = " + serialized)
+ .that(readFamily(serialized)).isEqualTo(expected);
+ }
+
+ @Test
+ public void styles() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<family name='sans-serif'>"
+ + " <font style='normal'>normal.ttf</font>"
+ + " <font weight='100'>weight.ttf</font>"
+ + " <font style='italic'>italic.ttf</font>"
+ + "</family>";
+ FontConfig.FontFamily expected = new FontConfig.FontFamily(
+ Arrays.asList(
+ new FontConfig.Font(new File("normal.ttf"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "", null),
+ new FontConfig.Font(new File("weight.ttf"), null,
+ new FontStyle(100, FONT_SLANT_UPRIGHT),
+ 0, "", null),
+ new FontConfig.Font(new File("italic.ttf"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_ITALIC),
+ 0, "", null)),
+ "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
+ FontConfig.FontFamily family = readFamily(xml);
+ assertThat(family).isEqualTo(expected);
+
+ String serialized = writeFamily(family);
+ assertWithMessage("serialized = " + serialized)
+ .that(readFamily(serialized)).isEqualTo(expected);
+ }
+
+ @Test
+ public void variable() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<family name='sans-serif'>"
+ + " <font>test-VF.ttf"
+ + " <axis tag='wdth' stylevalue='100' />"
+ + " <axis tag='wght' stylevalue='200' />"
+ + " </font>"
+ + " <font>test-VF.ttf"
+ + " <axis tag='wdth' stylevalue='400' />"
+ + " <axis tag='wght' stylevalue='700' />"
+ + " </font>"
+ + "</family>";
+ FontConfig.FontFamily expected = new FontConfig.FontFamily(
+ Arrays.asList(
+ new FontConfig.Font(new File("test-VF.ttf"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "'wdth' 100.0,'wght' 200.0", null),
+ new FontConfig.Font(new File("test-VF.ttf"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "'wdth' 400.0,'wght' 700.0", null)),
+ "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
+ FontConfig.FontFamily family = readFamily(xml);
+ assertThat(family).isEqualTo(expected);
+
+ String serialized = writeFamily(family);
+ assertWithMessage("serialized = " + serialized)
+ .that(readFamily(serialized)).isEqualTo(expected);
+ }
+
+ @Test
+ public void ttc() throws Exception {
+ String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+ + "<family name='sans-serif'>"
+ + " <font index='0'>test.ttc</font>"
+ + " <font index='1'>test.ttc</font>"
+ + "</family>";
+ FontConfig.FontFamily expected = new FontConfig.FontFamily(
+ Arrays.asList(
+ new FontConfig.Font(new File("test.ttc"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 0, "", null),
+ new FontConfig.Font(new File("test.ttc"), null,
+ new FontStyle(FONT_WEIGHT_NORMAL, FONT_SLANT_UPRIGHT),
+ 1, "", null)),
+ "sans-serif", LocaleList.getEmptyLocaleList(), VARIANT_DEFAULT);
+ FontConfig.FontFamily family = readFamily(xml);
+ assertThat(family).isEqualTo(expected);
+
+ String serialized = writeFamily(family);
+ assertWithMessage("serialized = " + serialized)
+ .that(readFamily(serialized)).isEqualTo(expected);
+ }
+
+ private FontConfig.FontFamily readFamily(String xml)
+ throws IOException, XmlPullParserException {
+ StandardCharsets.UTF_8.name();
+ ByteArrayInputStream buffer = new ByteArrayInputStream(
+ xml.getBytes(StandardCharsets.UTF_8));
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(buffer, "UTF-8");
+ parser.nextTag();
+ return FontListParser.readFamily(parser, "", null);
+ }
+
+ private String writeFamily(FontConfig.FontFamily family) throws IOException {
+ TypedXmlSerializer out = Xml.newFastSerializer();
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ out.setOutput(buffer, "UTF-8");
+ out.startTag(null, "family");
+ FontListParser.writeFamily(out, family);
+ out.endTag(null, "family");
+ out.endDocument();
+ return buffer.toString("UTF-8");
+ }
+}
diff --git a/core/tests/coretests/src/android/os/BrightnessLimit.java b/core/tests/coretests/src/android/os/BrightnessLimit.java
index be79355..219f741 100644
--- a/core/tests/coretests/src/android/os/BrightnessLimit.java
+++ b/core/tests/coretests/src/android/os/BrightnessLimit.java
@@ -42,7 +42,8 @@
public void onClick(View v) {
DisplayManager dm = getSystemService(DisplayManager.class);
- dm.setTemporaryBrightness(0.0f);
+ final int displayId = getBaseContext().getDisplay().getDisplayId();
+ dm.setTemporaryBrightness(displayId, 0.0f);
Settings.System.putInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, 0);
}
}
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index a4284a0..47ce2d8 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -278,29 +278,29 @@
@Test
@UiThreadTest
- public void setSetImeTemporarilyConsumesInput_recoveryToVisible() {
+ public void setSetImeConsumesInput_recoveryToVisible() {
mTextView = new TextView(mActivity);
mTextView.setCursorVisible(true);
assertTrue(mTextView.isCursorVisible());
- mTextView.setImeTemporarilyConsumesInput(true);
+ mTextView.setImeConsumesInput(true);
assertFalse(mTextView.isCursorVisible());
- mTextView.setImeTemporarilyConsumesInput(false);
+ mTextView.setImeConsumesInput(false);
assertTrue(mTextView.isCursorVisible());
}
@Test
@UiThreadTest
- public void setSetImeTemporarilyConsumesInput_recoveryToInvisible() {
+ public void setSetImeConsumesInput_recoveryToInvisible() {
mTextView = new TextView(mActivity);
mTextView.setCursorVisible(false);
assertFalse(mTextView.isCursorVisible());
- mTextView.setImeTemporarilyConsumesInput(true);
+ mTextView.setImeConsumesInput(true);
assertFalse(mTextView.isCursorVisible());
- mTextView.setImeTemporarilyConsumesInput(false);
+ mTextView.setImeConsumesInput(false);
assertFalse(mTextView.isCursorVisible());
}
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 64906bb..e16d448 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 @@
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 143e07a..0fac4f7 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 @@
BluetoothPowerCalculatorTest.class,
BstatsCpuTimesValidationTest.class,
CameraPowerCalculatorTest.class,
+ CpuPowerCalculatorTest.class,
FlashlightPowerCalculatorTest.class,
GnssPowerCalculatorTest.class,
IdlePowerCalculatorTest.class,
@@ -65,6 +66,7 @@
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 2c71287..0ddc4c0 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.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 @@
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 @@
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 @@
}
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 0000000..9cf0d37
--- /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/OWNERS b/core/tests/coretests/src/com/android/internal/os/OWNERS
index 4068e2b..3f8f9e2 100644
--- a/core/tests/coretests/src/com/android/internal/os/OWNERS
+++ b/core/tests/coretests/src/com/android/internal/os/OWNERS
@@ -1 +1,4 @@
include /BATTERY_STATS_OWNERS
+
+# CPU
+per-file *Cpu* = file:/core/java/com/android/internal/os/CPU_OWNERS
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 0000000..6fa1d3b
--- /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/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index 36f01f9..e7fdfb8 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -17,8 +17,11 @@
package android.hardware.devicestate;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
import android.annotation.Nullable;
+import android.os.IBinder;
import android.os.RemoteException;
import androidx.test.filters.SmallTest;
@@ -30,6 +33,7 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
@@ -41,6 +45,9 @@
@RunWith(JUnit4.class)
@SmallTest
public final class DeviceStateManagerGlobalTest {
+ private static final int DEFAULT_DEVICE_STATE = 0;
+ private static final int OTHER_DEVICE_STATE = 1;
+
private TestDeviceStateManagerService mService;
private DeviceStateManagerGlobal mDeviceStateManagerGlobal;
@@ -52,7 +59,7 @@
@Test
public void registerListener() {
- mService.setDeviceState(0);
+ mService.setBaseState(DEFAULT_DEVICE_STATE);
TestDeviceStateListener listener1 = new TestDeviceStateListener();
TestDeviceStateListener listener2 = new TestDeviceStateListener();
@@ -61,28 +68,58 @@
ConcurrentUtils.DIRECT_EXECUTOR);
mDeviceStateManagerGlobal.registerDeviceStateListener(listener2,
ConcurrentUtils.DIRECT_EXECUTOR);
- assertEquals(0, listener1.getLastReportedState().intValue());
- assertEquals(0, listener2.getLastReportedState().intValue());
+ assertEquals(DEFAULT_DEVICE_STATE, listener1.getLastReportedState().intValue());
+ assertEquals(DEFAULT_DEVICE_STATE, listener2.getLastReportedState().intValue());
- mService.setDeviceState(1);
- assertEquals(1, listener1.getLastReportedState().intValue());
- assertEquals(1, listener2.getLastReportedState().intValue());
+ mService.setBaseState(OTHER_DEVICE_STATE);
+ assertEquals(OTHER_DEVICE_STATE, listener1.getLastReportedState().intValue());
+ assertEquals(OTHER_DEVICE_STATE, listener2.getLastReportedState().intValue());
}
@Test
public void unregisterListener() {
- mService.setDeviceState(0);
+ mService.setBaseState(DEFAULT_DEVICE_STATE);
TestDeviceStateListener listener = new TestDeviceStateListener();
mDeviceStateManagerGlobal.registerDeviceStateListener(listener,
ConcurrentUtils.DIRECT_EXECUTOR);
- assertEquals(0, listener.getLastReportedState().intValue());
+ assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
mDeviceStateManagerGlobal.unregisterDeviceStateListener(listener);
- mService.setDeviceState(1);
- assertEquals(0, listener.getLastReportedState().intValue());
+ mService.setBaseState(OTHER_DEVICE_STATE);
+ assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
+ }
+
+ @Test
+ public void submittingRequestRegisteredCallback() {
+ assertTrue(mService.mCallbacks.isEmpty());
+
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build();
+ mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
+
+ assertFalse(mService.mCallbacks.isEmpty());
+ }
+
+ @Test
+ public void submitRequest() {
+ mService.setBaseState(DEFAULT_DEVICE_STATE);
+
+ TestDeviceStateListener listener = new TestDeviceStateListener();
+ mDeviceStateManagerGlobal.registerDeviceStateListener(listener,
+ ConcurrentUtils.DIRECT_EXECUTOR);
+
+ assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
+
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
+ mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
+
+ assertEquals(OTHER_DEVICE_STATE, listener.getLastReportedState().intValue());
+
+ mDeviceStateManagerGlobal.cancelRequest(request);
+
+ assertEquals(DEFAULT_DEVICE_STATE, listener.getLastReportedState().intValue());
}
private final class TestDeviceStateListener implements DeviceStateManager.DeviceStateListener {
@@ -100,8 +137,23 @@
}
}
- private final class TestDeviceStateManagerService extends IDeviceStateManager.Stub {
- private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE;
+ private static final class TestDeviceStateManagerService extends IDeviceStateManager.Stub {
+ public static final class Request {
+ public final IBinder token;
+ public final int state;
+ public final int flags;
+
+ private Request(IBinder token, int state, int flags) {
+ this.token = token;
+ this.state = state;
+ this.flags = flags;
+ }
+ }
+
+ private int mBaseState = DEFAULT_DEVICE_STATE;
+ private int mMergedState = DEFAULT_DEVICE_STATE;
+ private ArrayList<Request> mRequests = new ArrayList<>();
+
private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>();
@Override
@@ -112,19 +164,86 @@
mCallbacks.add(callback);
try {
- callback.onDeviceStateChanged(mDeviceState);
+ callback.onDeviceStateChanged(mMergedState);
} catch (RemoteException e) {
// Do nothing. Should never happen.
}
}
- public void setDeviceState(int deviceState) {
- boolean stateChanged = mDeviceState != deviceState;
- mDeviceState = deviceState;
- if (stateChanged) {
+ @Override
+ public int[] getSupportedDeviceStates() {
+ return new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE };
+ }
+
+ @Override
+ public void requestState(IBinder token, int state, int flags) {
+ if (!mRequests.isEmpty()) {
+ final Request topRequest = mRequests.get(mRequests.size() - 1);
for (IDeviceStateManagerCallback callback : mCallbacks) {
try {
- callback.onDeviceStateChanged(mDeviceState);
+ callback.onRequestSuspended(topRequest.token);
+ } catch (RemoteException e) {
+ // Do nothing. Should never happen.
+ }
+ }
+ }
+
+ final Request request = new Request(token, state, flags);
+ mRequests.add(request);
+ notifyStateChangedIfNeeded();
+
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onRequestActive(token);
+ } catch (RemoteException e) {
+ // Do nothing. Should never happen.
+ }
+ }
+ }
+
+ @Override
+ public void cancelRequest(IBinder token) {
+ int index = -1;
+ for (int i = 0; i < mRequests.size(); i++) {
+ if (mRequests.get(i).token.equals(token)) {
+ index = i;
+ break;
+ }
+ }
+
+ if (index == -1) {
+ throw new IllegalArgumentException("Unknown request: " + token);
+ }
+
+ mRequests.remove(index);
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onRequestCanceled(token);
+ } catch (RemoteException e) {
+ // Do nothing. Should never happen.
+ }
+ }
+ notifyStateChangedIfNeeded();
+ }
+
+ public void setBaseState(int state) {
+ mBaseState = state;
+ notifyStateChangedIfNeeded();
+ }
+
+ private void notifyStateChangedIfNeeded() {
+ final int originalMergedState = mMergedState;
+
+ if (!mRequests.isEmpty()) {
+ mMergedState = mRequests.get(mRequests.size() - 1).state;
+ } else {
+ mMergedState = mBaseState;
+ }
+
+ if (mMergedState != originalMergedState) {
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onDeviceStateChanged(mMergedState);
} catch (RemoteException e) {
// Do nothing. Should never happen.
}
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTests.java b/core/tests/mockingcoretests/src/android/view/DisplayTests.java
deleted file mode 100644
index 5a3ea35..0000000
--- a/core/tests/mockingcoretests/src/android/view/DisplayTests.java
+++ /dev/null
@@ -1,527 +0,0 @@
-/*
- * 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.view;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_90;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManagerGlobal;
-import android.util.DisplayMetrics;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
-
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-import org.mockito.quality.Strictness;
-
-import java.util.function.Consumer;
-
-/**
- * Tests for {@link Display}.
- *
- * <p>Build/Install/Run:
- *
- * atest FrameworksMockingCoreTests:android.view.DisplayTests
- */
-@RunWith(AndroidJUnit4.class)
-public class DisplayTests {
-
- private static final int APP_WIDTH = 272;
- private static final int APP_HEIGHT = 700;
- // Tablet size device, ROTATION_0 corresponds to portrait.
- private static final int LOGICAL_WIDTH = 700;
- private static final int LOGICAL_HEIGHT = 1800;
-
- // Bounds of the app when the device is in portrait mode.
- private static Rect sAppBoundsPortrait = buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT);
- private static Rect sAppBoundsLandscape = buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH);
-
- private StaticMockitoSession mMockitoSession;
-
- private DisplayManagerGlobal mDisplayManagerGlobal;
- private Context mApplicationContext;
- private DisplayInfo mDisplayInfo = new DisplayInfo();
-
- @Before
- public void setupTests() {
- mMockitoSession = mockitoSession()
- .mockStatic(DisplayManagerGlobal.class)
- .strictness(Strictness.LENIENT)
- .startMocking();
-
- // Ensure no adjustments are set before each test.
- mApplicationContext = ApplicationProvider.getApplicationContext();
- DisplayAdjustments displayAdjustments =
- mApplicationContext.getResources().getDisplayAdjustments();
- displayAdjustments.setFixedRotationAdjustments(null);
- mApplicationContext.getResources().overrideDisplayAdjustments(null);
- mApplicationContext.getResources().getConfiguration().windowConfiguration.setAppBounds(
- null);
- mApplicationContext.getResources().getConfiguration().windowConfiguration.setMaxBounds(
- null);
- mDisplayInfo.rotation = ROTATION_0;
-
- mDisplayManagerGlobal = mock(DisplayManagerGlobal.class);
- doReturn(mDisplayInfo).when(mDisplayManagerGlobal).getDisplayInfo(anyInt());
- }
-
- @After
- public void teardownTests() {
- if (mMockitoSession != null) {
- mMockitoSession.finishMocking();
- }
- Mockito.framework().clearInlineMocks();
- }
-
- @Test
- public void testConstructor_defaultDisplayAdjustments_matchesDisplayInfo() {
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- assertThat(display.getDisplayAdjustments()).isEqualTo(
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- DisplayInfo actualDisplayInfo = new DisplayInfo();
- display.getDisplayInfo(actualDisplayInfo);
- verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
- }
-
- @Test
- public void testConstructor_defaultResources_matchesDisplayInfo() {
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- assertThat(display.getDisplayAdjustments()).isEqualTo(
- mApplicationContext.getResources().getDisplayAdjustments());
- DisplayInfo actualDisplayInfo = new DisplayInfo();
- display.getDisplayInfo(actualDisplayInfo);
- verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
- }
-
- @Test
- public void testGetRotation_defaultDisplayAdjustments_rotationNotAdjusted() {
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- assertThat(display.getRotation()).isEqualTo(ROTATION_0);
- }
-
- @Test
- public void testGetRotation_displayAdjustmentsWithoutOverride_rotationNotAdjusted() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, but no override is set.
- DisplayAdjustments displayAdjustments = DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
- final FixedRotationAdjustments fixedRotationAdjustments =
- new FixedRotationAdjustments(ROTATION_90, APP_WIDTH, APP_HEIGHT,
- DisplayCutout.NO_CUTOUT);
- displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
- // GIVEN display is constructed with display adjustments.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- displayAdjustments);
- // THEN rotation is not adjusted since no override was set.
- assertThat(display.getRotation()).isEqualTo(ROTATION_0);
- }
-
- @Test
- public void testGetRotation_resourcesWithoutOverride_rotationNotAdjusted() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, but no override is set.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN rotation is not adjusted since no override is set.
- assertThat(display.getRotation()).isEqualTo(ROTATION_0);
- }
-
- @Test
- public void testGetRotation_resourcesWithOverrideDisplayAdjustments_rotationAdjusted() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, and an override is set.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN rotation is adjusted since an override is set.
- assertThat(display.getRotation()).isEqualTo(ROTATION_90);
- }
-
- @Test
- public void testGetRealSize_defaultResourcesPortrait_matchesLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches display orientation.
- verifyRealSizeIsPortrait(display);
- }
-
- @Test
- public void testGetRealSize_defaultResourcesLandscape_matchesRotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches display orientation.
- verifyRealSizeIsLandscape(display);
- }
-
- @Test
- public void testGetRealSize_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- // THEN real size matches display orientation.
- verifyRealSizeIsPortrait(display);
- }
-
- @Test
- public void testGetRealSize_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- // THEN real size matches display orientation.
- verifyRealSizeIsLandscape(display);
- }
-
- @Test
- public void testGetRealSize_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches display orientation.
- verifyRealSizeIsLandscape(display);
- }
-
- @Test
- public void testGetRealSize_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches display orientation.
- verifyRealSizeIsPortrait(display);
- }
-
- @Test
- public void testGetRealSize_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, and an override is set.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches app orientation.
- verifyRealSizeIsPortrait(display);
- }
-
- @Test
- public void testGetRealSize_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated, and an override is set.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches app orientation.
- verifyRealSizeIsLandscape(display);
- }
-
- @Test
- public void testGetRealSize_resourcesPortraitSandboxed_matchesSandboxBounds() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN app is letterboxed.
- setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
- sAppBoundsPortrait);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches app bounds.
- verifyRealSizeMatchesApp(display, sAppBoundsPortrait);
- }
-
- @Test
- public void testGetRealSize_resourcesLandscapeSandboxed_matchesSandboxBounds() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN app is letterboxed.
- setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
- sAppBoundsLandscape);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real size matches app bounds.
- verifyRealSizeMatchesApp(display, sAppBoundsLandscape);
- }
-
- @Test
- public void testGetRealMetrics_defaultResourcesPortrait_matchesLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsPortrait(display);
- }
-
- @Test
- public void testGetRealMetrics_defaultResourcesLandscape_matchesRotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsLandscape(display);
- }
-
- @Test
- public void testGetRealMetrics_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsPortrait(display);
- }
-
- @Test
- public void testGetRealMetrics_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsLandscape(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsLandscape(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches display orientation.
- verifyRealMetricsIsPortrait(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated with an override.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches app orientation.
- verifyRealMetricsIsPortrait(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN fixed rotation adjustments are rotated.
- setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
- // GIVEN display is constructed with default resources.
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches app orientation.
- verifyRealMetricsIsLandscape(display);
- }
-
- @Test
- public void testGetRealMetrics_resourcesPortraitSandboxed_matchesSandboxBounds() {
- // GIVEN display is not rotated.
- setDisplayInfoPortrait(mDisplayInfo);
- // GIVEN app is letterboxed.
- setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
- sAppBoundsPortrait);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches app bounds.
- verifyRealMetricsMatchesApp(display, sAppBoundsPortrait);
- }
-
- @Test
- public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesSandboxBounds() {
- // GIVEN display is rotated.
- setDisplayInfoLandscape(mDisplayInfo);
- // GIVEN app is letterboxed.
- setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
- sAppBoundsLandscape);
- final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
- mApplicationContext.getResources());
- // THEN real metrics matches app bounds.
- verifyRealMetricsMatchesApp(display, sAppBoundsLandscape);
- }
-
- // Given rotated display dimensions, calculate the letterboxed app bounds.
- private static Rect buildAppBounds(int displayWidth, int displayHeight) {
- final int midWidth = displayWidth / 2;
- final int left = midWidth - (APP_WIDTH / 2);
- final int right = midWidth + (APP_WIDTH / 2);
- final int midHeight = displayHeight / 2;
- // Coordinate system starts at top left.
- final int top = midHeight - (APP_HEIGHT / 2);
- final int bottom = midHeight + (APP_HEIGHT / 2);
- return new Rect(left, top, right, bottom);
- }
-
- private static void setDisplayInfoLandscape(DisplayInfo displayInfo) {
- displayInfo.rotation = ROTATION_90;
- // Flip width & height assignment since the device is rotated.
- displayInfo.logicalWidth = LOGICAL_HEIGHT;
- displayInfo.logicalHeight = LOGICAL_WIDTH;
- }
-
- private static void setDisplayInfoPortrait(DisplayInfo displayInfo) {
- displayInfo.rotation = ROTATION_0;
- displayInfo.logicalWidth = LOGICAL_WIDTH;
- displayInfo.logicalHeight = LOGICAL_HEIGHT;
- }
-
- /**
- * Set max bounds to be sandboxed to the app bounds, indicating the app is in
- * size compat mode or letterbox.
- */
- private static void setMaxBoundsSandboxedToMatchAppBounds(Resources resources, Rect appBounds) {
- resources.getConfiguration().windowConfiguration.setMaxBounds(appBounds);
- }
-
- /**
- * Do not compare entire display info, since it is updated to match display the test is run on.
- */
- private static void verifyDisplayInfo(DisplayInfo actual, DisplayInfo expected) {
- assertThat(actual.displayId).isEqualTo(expected.displayId);
- assertThat(actual.rotation).isEqualTo(expected.rotation);
- assertThat(actual.logicalWidth).isEqualTo(LOGICAL_WIDTH);
- assertThat(actual.logicalHeight).isEqualTo(LOGICAL_HEIGHT);
- }
-
- private static void verifyRealSizeIsLandscape(Display display) {
- Point size = new Point();
- display.getRealSize(size);
- // Flip the width and height check since the device is rotated.
- assertThat(size).isEqualTo(new Point(LOGICAL_HEIGHT, LOGICAL_WIDTH));
- }
-
- private static void verifyRealMetricsIsLandscape(Display display) {
- DisplayMetrics metrics = new DisplayMetrics();
- display.getRealMetrics(metrics);
- // Flip the width and height check since the device is rotated.
- assertThat(metrics.widthPixels).isEqualTo(LOGICAL_HEIGHT);
- assertThat(metrics.heightPixels).isEqualTo(LOGICAL_WIDTH);
- }
-
- private static void verifyRealSizeIsPortrait(Display display) {
- Point size = new Point();
- display.getRealSize(size);
- assertThat(size).isEqualTo(new Point(LOGICAL_WIDTH, LOGICAL_HEIGHT));
- }
-
- private static void verifyRealMetricsIsPortrait(Display display) {
- DisplayMetrics metrics = new DisplayMetrics();
- display.getRealMetrics(metrics);
- assertThat(metrics.widthPixels).isEqualTo(LOGICAL_WIDTH);
- assertThat(metrics.heightPixels).isEqualTo(LOGICAL_HEIGHT);
- }
-
- private static void verifyRealSizeMatchesApp(Display display, Rect appBounds) {
- Point size = new Point();
- display.getRealSize(size);
- assertThat(size).isEqualTo(new Point(appBounds.width(), appBounds.height()));
- }
-
- private static void verifyRealMetricsMatchesApp(Display display, Rect appBounds) {
- DisplayMetrics metrics = new DisplayMetrics();
- display.getRealMetrics(metrics);
- assertThat(metrics.widthPixels).isEqualTo(appBounds.width());
- assertThat(metrics.heightPixels).isEqualTo(appBounds.height());
- }
-
- private static FixedRotationAdjustments setOverrideFixedRotationAdjustments(
- Resources resources, @Surface.Rotation int rotation) {
- FixedRotationAdjustments fixedRotationAdjustments =
- setFixedRotationAdjustments(resources, rotation);
- resources.overrideDisplayAdjustments(
- buildOverrideRotationAdjustments(fixedRotationAdjustments));
- return fixedRotationAdjustments;
- }
-
- private static FixedRotationAdjustments setFixedRotationAdjustments(Resources resources,
- @Surface.Rotation int rotation) {
- final FixedRotationAdjustments fixedRotationAdjustments =
- new FixedRotationAdjustments(rotation, APP_WIDTH, APP_HEIGHT,
- DisplayCutout.NO_CUTOUT);
- resources.getDisplayAdjustments().setFixedRotationAdjustments(fixedRotationAdjustments);
- return fixedRotationAdjustments;
- }
-
- private static Consumer<DisplayAdjustments> buildOverrideRotationAdjustments(
- FixedRotationAdjustments fixedRotationAdjustments) {
- return consumedDisplayAdjustments
- -> consumedDisplayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
- }
-}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index b0ae9b9..ea42246 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -165,6 +165,7 @@
<assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="audioserver" />
<assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="audioserver" />
<assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="audioserver" />
+ <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="audioserver" />
<assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="cameraserver" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="cameraserver" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index cdc61a1..0484a9a 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -465,10 +465,15 @@
<permission name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
<!-- Permission required for CTS test CarrierMessagingServiceWrapperTest -->
<permission name="android.permission.BIND_CARRIER_SERVICES"/>
+ <!-- Permission required for CTS test - MusicRecognitionManagerTest -->
+ <permission name="android.permission.MANAGE_MUSIC_RECOGNITION" />
<!-- Permission required for CTS test - CallLogTest -->
<permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
<permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
<permission name="android.permission.MODIFY_QUIET_MODE" />
+ <!-- Permission required for GTS test - GtsAssistIntentTestCases -->
+ <permission name="android.permission.MANAGE_SOUND_TRIGGER" />
+ <permission name="android.permission.CAPTURE_AUDIO_HOTWORD" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
@@ -482,6 +487,8 @@
<permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/>
<!-- Permissions required for quick settings tile -->
<permission name="android.permission.STATUS_BAR"/>
+ <!-- Permissions required to query Betterbug -->
+ <permission name="android.permission.QUERY_ALL_PACKAGES"/>
</privapp-permissions>
<privapp-permissions package="com.android.tv">
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index ac8a296..222c9bd 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1903,6 +1903,12 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "123161180": {
+ "message": "SEVER CHILDREN",
+ "level": "INFO",
+ "group": "WM_SHOW_TRANSACTIONS",
+ "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+ },
"140319294": {
"message": "IME target changed within ActivityRecord",
"level": "DEBUG",
@@ -2137,12 +2143,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "332390227": {
- "message": "Sandbox max bounds for uid %s to bounds %s due to letterboxing? %s mismatch with parent bounds? %s size compat mode %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_CONFIGURATION",
- "at": "com\/android\/server\/wm\/ActivityRecord.java"
- },
"342460966": {
"message": "DRAG %s: pos=(%d,%d)",
"level": "INFO",
@@ -2623,12 +2623,6 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/WindowOrganizerController.java"
},
- "910200295": {
- "message": "Sandbox max bounds due to mismatched orientation with parent, to %s vs DisplayArea %s",
- "level": "DEBUG",
- "group": "WM_DEBUG_CONFIGURATION",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"913494177": {
"message": "removeAllWindowsIfPossible: removing win=%s",
"level": "WARN",
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index bb795cd..63df0db 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -25,6 +25,8 @@
import android.os.Build;
import android.os.LocaleList;
import android.text.FontConfig;
+import android.text.TextUtils;
+import android.util.TypedXmlSerializer;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
@@ -45,6 +47,27 @@
*/
public class FontListParser {
+ // XML constants for FontFamily.
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_LANG = "lang";
+ private static final String ATTR_VARIANT = "variant";
+ private static final String TAG_FONT = "font";
+ private static final String VARIANT_COMPACT = "compact";
+ private static final String VARIANT_ELEGANT = "elegant";
+
+ // XML constants for Font.
+ public static final String ATTR_INDEX = "index";
+ public static final String ATTR_WEIGHT = "weight";
+ public static final String ATTR_STYLE = "style";
+ public static final String ATTR_FALLBACK_FOR = "fallbackFor";
+ public static final String STYLE_ITALIC = "italic";
+ public static final String STYLE_NORMAL = "normal";
+ public static final String TAG_AXIS = "axis";
+
+ // XML constants for FontVariationAxis.
+ public static final String ATTR_TAG = "tag";
+ public static final String ATTR_STYLEVALUE = "stylevalue";
+
/* Parse fallback list (no names) */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException {
@@ -148,7 +171,7 @@
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
final String tag = parser.getName();
- if (tag.equals("font")) {
+ if (tag.equals(TAG_FONT)) {
fonts.add(readFont(parser, fontDir, updatableFontMap));
} else {
skip(parser);
@@ -156,15 +179,41 @@
}
int intVariant = FontConfig.FontFamily.VARIANT_DEFAULT;
if (variant != null) {
- if (variant.equals("compact")) {
+ if (variant.equals(VARIANT_COMPACT)) {
intVariant = FontConfig.FontFamily.VARIANT_COMPACT;
- } else if (variant.equals("elegant")) {
+ } else if (variant.equals(VARIANT_ELEGANT)) {
intVariant = FontConfig.FontFamily.VARIANT_ELEGANT;
}
}
return new FontConfig.FontFamily(fonts, name, LocaleList.forLanguageTags(lang), intVariant);
}
+ /**
+ * Write a family tag representing {@code fontFamily}. The tag should be started by the caller.
+ */
+ public static void writeFamily(TypedXmlSerializer out, FontConfig.FontFamily fontFamily)
+ throws IOException {
+ if (!TextUtils.isEmpty(fontFamily.getName())) {
+ out.attribute(null, ATTR_NAME, fontFamily.getName());
+ }
+ if (!fontFamily.getLocaleList().isEmpty()) {
+ out.attribute(null, ATTR_LANG, fontFamily.getLocaleList().toLanguageTags());
+ }
+ switch (fontFamily.getVariant()) {
+ case FontConfig.FontFamily.VARIANT_COMPACT:
+ out.attribute(null, ATTR_VARIANT, VARIANT_COMPACT);
+ break;
+ case FontConfig.FontFamily.VARIANT_ELEGANT:
+ out.attribute(null, ATTR_VARIANT, VARIANT_ELEGANT);
+ break;
+ }
+ for (FontConfig.Font font : fontFamily.getFontList()) {
+ out.startTag(null, TAG_FONT);
+ writeFont(out, font);
+ out.endTag(null, TAG_FONT);
+ }
+ }
+
/** Matches leading and trailing XML whitespace. */
private static final Pattern FILENAME_WHITESPACE_PATTERN =
Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$");
@@ -175,13 +224,13 @@
@Nullable Map<String, File> updatableFontMap)
throws XmlPullParserException, IOException {
- String indexStr = parser.getAttributeValue(null, "index");
+ String indexStr = parser.getAttributeValue(null, ATTR_INDEX);
int index = indexStr == null ? 0 : Integer.parseInt(indexStr);
List<FontVariationAxis> axes = new ArrayList<>();
- String weightStr = parser.getAttributeValue(null, "weight");
- int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
- boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
- String fallbackFor = parser.getAttributeValue(null, "fallbackFor");
+ String weightStr = parser.getAttributeValue(null, ATTR_WEIGHT);
+ int weight = weightStr == null ? FontStyle.FONT_WEIGHT_NORMAL : Integer.parseInt(weightStr);
+ boolean isItalic = STYLE_ITALIC.equals(parser.getAttributeValue(null, ATTR_STYLE));
+ String fallbackFor = parser.getAttributeValue(null, ATTR_FALLBACK_FOR);
StringBuilder filename = new StringBuilder();
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() == XmlPullParser.TEXT) {
@@ -189,7 +238,7 @@
}
if (parser.getEventType() != XmlPullParser.START_TAG) continue;
String tag = parser.getName();
- if (tag.equals("axis")) {
+ if (tag.equals(TAG_AXIS)) {
axes.add(readAxis(parser));
} else {
skip(parser);
@@ -237,14 +286,48 @@
return null;
}
+ private static void writeFont(TypedXmlSerializer out, FontConfig.Font font)
+ throws IOException {
+ if (font.getTtcIndex() != 0) {
+ out.attributeInt(null, ATTR_INDEX, font.getTtcIndex());
+ }
+ if (font.getStyle().getWeight() != FontStyle.FONT_WEIGHT_NORMAL) {
+ out.attributeInt(null, ATTR_WEIGHT, font.getStyle().getWeight());
+ }
+ if (font.getStyle().getSlant() == FontStyle.FONT_SLANT_ITALIC) {
+ out.attribute(null, ATTR_STYLE, STYLE_ITALIC);
+ } else {
+ out.attribute(null, ATTR_STYLE, STYLE_NORMAL);
+ }
+ if (!TextUtils.isEmpty(font.getFontFamilyName())) {
+ out.attribute(null, ATTR_FALLBACK_FOR, font.getFontFamilyName());
+ }
+ out.text(font.getFile().getName());
+ FontVariationAxis[] axes =
+ FontVariationAxis.fromFontVariationSettings(font.getFontVariationSettings());
+ if (axes != null) {
+ for (FontVariationAxis axis : axes) {
+ out.startTag(null, TAG_AXIS);
+ writeAxis(out, axis);
+ out.endTag(null, TAG_AXIS);
+ }
+ }
+ }
+
private static FontVariationAxis readAxis(XmlPullParser parser)
throws XmlPullParserException, IOException {
- String tagStr = parser.getAttributeValue(null, "tag");
- String styleValueStr = parser.getAttributeValue(null, "stylevalue");
+ String tagStr = parser.getAttributeValue(null, ATTR_TAG);
+ String styleValueStr = parser.getAttributeValue(null, ATTR_STYLEVALUE);
skip(parser); // axis tag is empty, ignore any contents and consume end tag
return new FontVariationAxis(tagStr, Float.parseFloat(styleValueStr));
}
+ private static void writeAxis(TypedXmlSerializer out, FontVariationAxis axis)
+ throws IOException {
+ out.attribute(null, ATTR_TAG, axis.getTag());
+ out.attributeFloat(null, ATTR_STYLEVALUE, axis.getStyleValue());
+ }
+
/**
* Reads alias elements
*/
@@ -255,7 +338,7 @@
String weightStr = parser.getAttributeValue(null, "weight");
int weight;
if (weightStr == null) {
- weight = 400;
+ weight = FontStyle.FONT_WEIGHT_NORMAL;
} else {
weight = Integer.parseInt(weightStr);
}
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index cb4dd9e..b70fa0e 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -46,7 +46,6 @@
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 @@
// 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 @@
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/graphics/java/android/graphics/RenderEffect.java b/graphics/java/android/graphics/RenderEffect.java
index 496e470..ad4c3fe 100644
--- a/graphics/java/android/graphics/RenderEffect.java
+++ b/graphics/java/android/graphics/RenderEffect.java
@@ -190,7 +190,7 @@
}
/**
- * Create a filter that applies the color filter to the provided RenderEffect
+ * Create a {@link RenderEffect} that applies the color filter to the provided RenderEffect
*
* @param colorFilter ColorFilter applied to the content in the input RenderEffect
* @param renderEffect Source to be transformed by the specified {@link ColorFilter}
@@ -209,7 +209,7 @@
}
/**
- * Create a filter that applies the color filter to the contents of the
+ * Create a {@link RenderEffect} that applies the color filter to the contents of the
* {@link android.graphics.RenderNode} that this RenderEffect is installed on
* @param colorFilter ColorFilter applied to the content in the input RenderEffect
*/
@@ -224,7 +224,7 @@
}
/**
- * {@link RenderEffect} that is a composition of 2 other {@link RenderEffect} instances
+ * Create a {@link RenderEffect} that is a composition of 2 other {@link RenderEffect} instances
* combined by the specified {@link BlendMode}
*
* @param dst The Dst pixels used in blending
@@ -248,8 +248,8 @@
}
/**
- * Create a filter that composes 'inner' with 'outer', such that the results of 'inner' are
- * treated as the source bitmap passed to 'outer', i.e.
+ * Create a {@link RenderEffect} that composes 'inner' with 'outer', such that the results of
+ * 'inner' are treated as the source bitmap passed to 'outer', i.e.
*
* <pre>
* {@code
@@ -278,6 +278,18 @@
);
}
+ /**
+ * Create a {@link RenderEffect} that renders the contents of the input {@link Shader}.
+ * This is useful to create an input for other {@link RenderEffect} types such as
+ * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)}
+ * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, TileMode)} or
+ * {@link RenderEffect#createColorFilterEffect(ColorFilter, RenderEffect)}.
+ */
+ @NonNull
+ public static RenderEffect createShaderEffect(@NonNull Shader shader) {
+ return new RenderEffect(nativeCreateShaderEffect(shader.getNativeInstance()));
+ }
+
private final long mNativeRenderEffect;
/* only constructed from static factory methods */
@@ -305,5 +317,6 @@
private static native long nativeCreateColorFilterEffect(long colorFilter, long nativeInput);
private static native long nativeCreateBlendModeEffect(long dst, long src, int blendmode);
private static native long nativeCreateChainEffect(long outer, long inner);
+ private static native long nativeCreateShaderEffect(long shader);
private static native long nativeGetFinalizer();
}
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 9214ff1..b153c99 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -26,8 +26,7 @@
import android.graphics.RectF;
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
-import android.util.Log;
-import android.util.LongSparseArray;
+import android.text.TextUtils;
import android.util.LongSparseLongArray;
import android.util.TypedValue;
@@ -44,7 +43,6 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
@@ -61,14 +59,9 @@
private static final int STYLE_ITALIC = 1;
private static final int STYLE_NORMAL = 0;
- private static final Object MAP_LOCK = new Object();
- // We need to have mapping from native ptr to Font object for later accessing from TextShape
- // result since Typeface doesn't have reference to Font object and it is not always created from
- // Font object. Sometimes Typeface is created in native layer only and there might not be Font
- // object in Java layer. So, if not found in this cache, create new Font object for API user.
- @GuardedBy("MAP_LOCK")
- private static final LongSparseArray<WeakReference<Font>> FONT_PTR_MAP =
- new LongSparseArray<>();
+ private static final NativeAllocationRegistry BUFFER_REGISTRY =
+ NativeAllocationRegistry.createMalloced(
+ ByteBuffer.class.getClassLoader(), nGetReleaseNativeFont());
private static final Object SOURCE_ID_LOCK = new Object();
@GuardedBy("SOURCE_ID_LOCK")
@@ -79,9 +72,7 @@
* A builder class for creating new Font.
*/
public static final class Builder {
- private static final NativeAllocationRegistry sFontRegistry =
- NativeAllocationRegistry.createMalloced(Font.class.getClassLoader(),
- nGetReleaseNativeFont());
+
private @Nullable ByteBuffer mBuffer;
private @Nullable File mFile;
@@ -484,26 +475,15 @@
final String filePath = mFile == null ? "" : mFile.getAbsolutePath();
long ptr;
- int fontIdentifier;
+ final Font font;
if (mFont == null) {
ptr = nBuild(builderPtr, readonlyBuffer, filePath, mLocaleList, mWeight, italic,
mTtcIndex);
- long fontBufferPtr = nGetFontBufferAddress(ptr);
- synchronized (SOURCE_ID_LOCK) {
- long id = FONT_SOURCE_ID_MAP.get(fontBufferPtr, -1);
- if (id == -1) {
- id = FONT_SOURCE_ID_MAP.size();
- FONT_SOURCE_ID_MAP.put(fontBufferPtr, id);
- }
- fontIdentifier = (int) id;
- }
+ font = new Font(ptr);
} else {
ptr = nClone(mFont.getNativePtr(), builderPtr, mWeight, italic, mTtcIndex);
- fontIdentifier = mFont.mSourceIdentifier;
+ font = new Font(ptr);
}
- final Font font = new Font(ptr, readonlyBuffer, mFile,
- new FontStyle(mWeight, slant), mTtcIndex, mAxes, mLocaleList, fontIdentifier);
- sFontRegistry.registerNativeAllocation(font, ptr);
return font;
}
@@ -525,33 +505,32 @@
}
private final long mNativePtr; // address of the shared ptr of minikin::Font
- private final @NonNull ByteBuffer mBuffer;
- private final @Nullable File mFile;
- private final FontStyle mFontStyle;
- private final @IntRange(from = 0) int mTtcIndex;
- private final @Nullable FontVariationAxis[] mAxes;
- private final @NonNull String mLocaleList;
- private final int mSourceIdentifier; // An identifier of font source data.
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private @NonNull ByteBuffer mBuffer = null;
+ @GuardedBy("mLock")
+ private boolean mIsFileInitialized = false;
+ @GuardedBy("mLock")
+ private @Nullable File mFile = null;
+ @GuardedBy("mLock")
+ private FontStyle mFontStyle = null;
+ @GuardedBy("mLock")
+ private @Nullable FontVariationAxis[] mAxes = null;
+ @GuardedBy("mLock")
+ private @NonNull LocaleList mLocaleList = null;
+ @GuardedBy("mLock")
+ private int mSourceIdentifier = -1;
/**
* Use Builder instead
+ *
+ * Caller must increment underlying minikin::Font ref count.
+ *
+ * @hide
*/
- private Font(long nativePtr, @NonNull ByteBuffer buffer, @Nullable File file,
- @NonNull FontStyle fontStyle, @IntRange(from = 0) int ttcIndex,
- @Nullable FontVariationAxis[] axes, @NonNull String localeList,
- int sourceIdentifier) {
- mBuffer = buffer;
- mFile = file;
- mFontStyle = fontStyle;
+ public Font(long nativePtr) {
mNativePtr = nativePtr;
- mTtcIndex = ttcIndex;
- mAxes = axes;
- mLocaleList = localeList;
- mSourceIdentifier = sourceIdentifier;
-
- synchronized (MAP_LOCK) {
- FONT_PTR_MAP.append(nGetNativeFontPtr(mNativePtr), new WeakReference<>(this));
- }
}
/**
@@ -563,7 +542,22 @@
* @return a font buffer
*/
public @NonNull ByteBuffer getBuffer() {
- return mBuffer;
+ synchronized (mLock) {
+ if (mBuffer == null) {
+ // Create new instance of native FontWrapper, i.e. incrementing ref count of
+ // minikin Font instance for keeping buffer fo ByteBuffer reference which may live
+ // longer than this object.
+ long ref = nCloneFont(mNativePtr);
+ ByteBuffer fromNative = nNewByteBuffer(mNativePtr);
+
+ // Bind ByteBuffer's lifecycle with underlying font object.
+ BUFFER_REGISTRY.registerNativeAllocation(fromNative, ref);
+
+ // JNI NewDirectBuffer creates writable ByteBuffer even if it is mmaped readonly.
+ mBuffer = fromNative.asReadOnlyBuffer();
+ }
+ return mBuffer;
+ }
}
/**
@@ -574,7 +568,16 @@
* @return a file path of the font
*/
public @Nullable File getFile() {
- return mFile;
+ synchronized (mLock) {
+ if (!mIsFileInitialized) {
+ String path = nGetFontPath(mNativePtr);
+ if (!TextUtils.isEmpty(path)) {
+ mFile = new File(path);
+ }
+ mIsFileInitialized = true;
+ }
+ return mFile;
+ }
}
/**
@@ -585,7 +588,16 @@
* @return a font style
*/
public @NonNull FontStyle getStyle() {
- return mFontStyle;
+ synchronized (mLock) {
+ if (mFontStyle == null) {
+ int packedStyle = nGetPackedStyle(mNativePtr);
+ mFontStyle = new FontStyle(
+ FontFileUtil.unpackWeight(packedStyle),
+ FontFileUtil.unpackItalic(packedStyle)
+ ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT);
+ }
+ return mFontStyle;
+ }
}
/**
@@ -597,7 +609,7 @@
* @return a TTC index value
*/
public @IntRange(from = 0) int getTtcIndex() {
- return mTtcIndex;
+ return nGetIndex(mNativePtr);
}
/**
@@ -608,7 +620,23 @@
* @return font variation settings
*/
public @Nullable FontVariationAxis[] getAxes() {
- return mAxes == null ? null : mAxes.clone();
+ synchronized (mLock) {
+ if (mAxes == null) {
+ int axisCount = nGetAxisCount(mNativePtr);
+ mAxes = new FontVariationAxis[axisCount];
+ char[] charBuffer = new char[4];
+ for (int i = 0; i < axisCount; ++i) {
+ long packedAxis = nGetAxisInfo(mNativePtr, i);
+ float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL));
+ charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >>> 56);
+ charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >>> 48);
+ charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >>> 40);
+ charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >>> 32);
+ mAxes[i] = new FontVariationAxis(new String(charBuffer), value);
+ }
+ }
+ }
+ return mAxes;
}
/**
@@ -618,7 +646,17 @@
* @return a locale list
*/
public @NonNull LocaleList getLocaleList() {
- return LocaleList.forLanguageTags(mLocaleList);
+ synchronized (mLock) {
+ if (mLocaleList == null) {
+ String langTags = nGetLocaleList(mNativePtr);
+ if (TextUtils.isEmpty(langTags)) {
+ mLocaleList = LocaleList.getEmptyLocaleList();
+ } else {
+ mLocaleList = LocaleList.forLanguageTags(langTags);
+ }
+ }
+ return mLocaleList;
+ }
}
/**
@@ -713,7 +751,20 @@
* @return an unique identifier for the font source data.
*/
public int getSourceIdentifier() {
- return mSourceIdentifier;
+ synchronized (mLock) {
+ if (mSourceIdentifier == -1) {
+ long bufferAddress = nGetBufferAddress(mNativePtr);
+ synchronized (SOURCE_ID_LOCK) {
+ long id = FONT_SOURCE_ID_MAP.get(bufferAddress, -1);
+ if (id == -1) {
+ id = FONT_SOURCE_ID_MAP.size();
+ FONT_SOURCE_ID_MAP.append(bufferAddress, id);
+ }
+ mSourceIdentifier = (int) id;
+ }
+ }
+ return mSourceIdentifier;
+ }
}
/**
@@ -736,13 +787,16 @@
private boolean isSameSource(@NonNull Font other) {
Objects.requireNonNull(other);
+ ByteBuffer myBuffer = getBuffer();
+ ByteBuffer otherBuffer = other.getBuffer();
+
// Shortcut for the same instance.
- if (mBuffer == other.mBuffer) {
+ if (myBuffer == otherBuffer) {
return true;
}
// Shortcut for different font buffer check by comparing size.
- if (mBuffer.capacity() != other.mBuffer.capacity()) {
+ if (myBuffer.capacity() != otherBuffer.capacity()) {
return false;
}
@@ -750,15 +804,15 @@
// underlying native font object holds buffer address, check if this buffer points exactly
// the same address as a shortcut of equality. For being compatible with of API30 or before,
// check buffer position even if the buffer points the same address.
- if (mSourceIdentifier == other.mSourceIdentifier
- && mBuffer.position() == other.mBuffer.position()) {
+ if (getSourceIdentifier() == other.getSourceIdentifier()
+ && myBuffer.position() == otherBuffer.position()) {
return true;
}
// Unfortunately, need to compare bytes one-by-one since the buffer may be different font
// file but has the same file size, or two font has same content but they are allocated
// differently. For being compatible with API30 ore before, compare with ByteBuffer#equals.
- return mBuffer.equals(other.mBuffer);
+ return myBuffer.equals(otherBuffer);
}
@Override
@@ -769,10 +823,20 @@
if (!(o instanceof Font)) {
return false;
}
+
Font f = (Font) o;
- boolean paramEqual = mFontStyle.equals(f.mFontStyle) && f.mTtcIndex == mTtcIndex
- && Arrays.equals(f.mAxes, mAxes) && Objects.equals(f.mLocaleList, mLocaleList)
- && Objects.equals(mFile, f.mFile);
+
+ // The underlying minikin::Font object is the source of the truth of font information. Thus,
+ // Pointer equality is the object equality.
+ if (nGetMinikinFontPtr(mNativePtr) == nGetMinikinFontPtr(f.mNativePtr)) {
+ return true;
+ }
+
+ boolean paramEqual = f.getStyle().equals(getStyle())
+ && f.getTtcIndex() == getTtcIndex()
+ && Arrays.equals(f.getAxes(), getAxes())
+ && Objects.equals(f.getLocaleList(), getLocaleList())
+ && Objects.equals(getFile(), f.getFile());
if (!paramEqual) {
return false;
@@ -784,64 +848,42 @@
@Override
public int hashCode() {
return Objects.hash(
- mFontStyle,
- mTtcIndex,
- Arrays.hashCode(mAxes),
+ getStyle(),
+ getTtcIndex(),
+ Arrays.hashCode(getAxes()),
// Use Buffer size instead of ByteBuffer#hashCode since ByteBuffer#hashCode traverse
// data which is not performant e.g. for HashMap. The hash collision are less likely
// happens because it is unlikely happens the different font files has exactly the
// same size.
- mLocaleList);
+ getLocaleList());
}
@Override
public String toString() {
return "Font {"
- + "path=" + mFile
- + ", style=" + mFontStyle
- + ", ttcIndex=" + mTtcIndex
- + ", axes=" + FontVariationAxis.toFontVariationSettings(mAxes)
- + ", localeList=" + mLocaleList
- + ", buffer=" + mBuffer
+ + "path=" + getFile()
+ + ", style=" + getStyle()
+ + ", ttcIndex=" + getTtcIndex()
+ + ", axes=" + FontVariationAxis.toFontVariationSettings(getAxes())
+ + ", localeList=" + getLocaleList()
+ + ", buffer=" + getBuffer()
+ "}";
}
- /**
- * Lookup Font object from native pointer or create new one if not found.
- * @hide
- */
- public static Font findOrCreateFontFromNativePtr(long ptr) {
- // First, lookup from known mapps.
- synchronized (MAP_LOCK) {
- WeakReference<Font> fontRef = FONT_PTR_MAP.get(ptr);
- if (fontRef != null) {
- Font font = fontRef.get();
- if (font != null) {
- return font;
- }
- }
+ @CriticalNative
+ private static native long nGetMinikinFontPtr(long font);
- // If not found, create Font object from native object for Java API users.
- ByteBuffer buffer = NativeFontBufferHelper.refByteBuffer(ptr);
- NativeFont.Font font = NativeFont.readNativeFont(ptr);
+ @CriticalNative
+ private static native long nCloneFont(long font);
- Font.Builder builder = new Font.Builder(buffer, font.getFile(), "")
- .setWeight(font.getStyle().getWeight())
- .setSlant(font.getStyle().getSlant())
- .setTtcIndex(font.getIndex())
- .setFontVariationSettings(font.getAxes());
+ @FastNative
+ private static native ByteBuffer nNewByteBuffer(long font);
- Font newFont = null;
- try {
- newFont = builder.build();
- FONT_PTR_MAP.append(ptr, new WeakReference<>(newFont));
- } catch (IOException e) {
- // This must not happen since the buffer was already created once.
- Log.e("Font", "Failed to create font object from existing buffer.", e);
- }
- return newFont;
- }
- }
+ @CriticalNative
+ private static native long nGetBufferAddress(long font);
+
+ @CriticalNative
+ private static native long nGetReleaseNativeFont();
@FastNative
private static native float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect);
@@ -849,9 +891,21 @@
@FastNative
private static native float nGetFontMetrics(long font, long paint, Paint.FontMetrics metrics);
- @CriticalNative
- private static native long nGetNativeFontPtr(long ptr);
+ @FastNative
+ private static native String nGetFontPath(long fontPtr);
+
+ @FastNative
+ private static native String nGetLocaleList(long familyPtr);
@CriticalNative
- private static native long nGetFontBufferAddress(long font);
+ private static native int nGetPackedStyle(long fontPtr);
+
+ @CriticalNative
+ private static native int nGetIndex(long fontPtr);
+
+ @CriticalNative
+ private static native int nGetAxisCount(long fontPtr);
+
+ @CriticalNative
+ private static native long nGetAxisInfo(long fontPtr, int i);
}
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index 77f86fe..8c13d3e 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -143,12 +143,10 @@
private static native long nGetReleaseNativeFamily();
}
- private final ArrayList<Font> mFonts;
private final long mNativePtr;
// Use Builder instead.
private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) {
- mFonts = fonts;
mNativePtr = ptr;
}
@@ -176,7 +174,10 @@
* @return a registered font
*/
public @NonNull Font getFont(@IntRange(from = 0) int index) {
- return mFonts.get(index);
+ if (index < 0 || getSize() <= index) {
+ throw new IndexOutOfBoundsException();
+ }
+ return new Font(nGetFont(mNativePtr, index));
}
/**
@@ -185,7 +186,7 @@
* @return the number of fonts registered in this family.
*/
public @IntRange(from = 1) int getSize() {
- return mFonts.size();
+ return nGetFontSize(mNativePtr);
}
/** @hide */
@@ -193,6 +194,12 @@
return mNativePtr;
}
+ @CriticalNative
+ private static native int nGetFontSize(long family);
+
+ @CriticalNative
+ private static native long nGetFont(long family, int i);
+
@FastNative
private static native String nGetLangTags(long family);
diff --git a/graphics/java/android/graphics/fonts/NativeFont.java b/graphics/java/android/graphics/fonts/NativeFont.java
deleted file mode 100644
index 9e9d76a..0000000
--- a/graphics/java/android/graphics/fonts/NativeFont.java
+++ /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 android.graphics.fonts;
-
-import android.graphics.Typeface;
-
-import dalvik.annotation.optimization.CriticalNative;
-import dalvik.annotation.optimization.FastNative;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Read native font objects.
- *
- * @hide
- */
-public class NativeFont {
-
- /**
- * Represents native font object.
- */
- public static final class Font {
- private final File mFile;
- private final int mIndex;
- private final FontVariationAxis[] mAxes;
- private final FontStyle mStyle;
-
- public Font(File file, int index, FontVariationAxis[] axes, FontStyle style) {
- mFile = file;
- mIndex = index;
- mAxes = axes;
- mStyle = style;
- }
-
- public File getFile() {
- return mFile;
- }
-
- public FontVariationAxis[] getAxes() {
- return mAxes;
- }
-
- public FontStyle getStyle() {
- return mStyle;
- }
-
- public int getIndex() {
- return mIndex;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Font font = (Font) o;
- return mIndex == font.mIndex && mFile.equals(font.mFile)
- && Arrays.equals(mAxes, font.mAxes) && mStyle.equals(font.mStyle);
- }
-
- @Override
- public int hashCode() {
- int result = Objects.hash(mFile, mIndex, mStyle);
- result = 31 * result + Arrays.hashCode(mAxes);
- return result;
- }
- }
-
- /**
- * Represents native font family object.
- */
- public static final class Family {
- private final List<Font> mFonts;
- private final String mLocale;
-
- public Family(List<Font> fonts, String locale) {
- mFonts = fonts;
- mLocale = locale;
- }
-
- public List<Font> getFonts() {
- return mFonts;
- }
-
- public String getLocale() {
- return mLocale;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Family family = (Family) o;
- return mFonts.equals(family.mFonts) && mLocale.equals(family.mLocale);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mFonts, mLocale);
- }
- }
-
- /**
- * Get underlying font families from Typeface
- *
- * @param typeface a typeface
- * @return list of family
- */
- public static List<Family> readTypeface(Typeface typeface) {
- int familyCount = nGetFamilyCount(typeface.native_instance);
- List<Family> result = new ArrayList<>(familyCount);
- for (int i = 0; i < familyCount; ++i) {
- result.add(readNativeFamily(nGetFamily(typeface.native_instance, i)));
- }
- return result;
- }
-
- /**
- * Read family object from native pointer
- *
- * @param familyPtr a font family pointer
- * @return a family
- */
- public static Family readNativeFamily(long familyPtr) {
- int fontCount = nGetFontCount(familyPtr);
- List<Font> result = new ArrayList<>(fontCount);
- for (int i = 0; i < fontCount; ++i) {
- result.add(readNativeFont(nGetFont(familyPtr, i)));
- }
- String localeList = nGetLocaleList(familyPtr);
- return new Family(result, localeList);
- }
-
- /**
- * Read font object from native pointer.
- *
- * @param ptr a font pointer
- * @return a font
- */
- public static Font readNativeFont(long ptr) {
- long packed = nGetFontInfo(ptr);
- int weight = (int) (packed & 0x0000_0000_0000_FFFFL);
- boolean italic = (packed & 0x0000_0000_0001_0000L) != 0;
- int ttcIndex = (int) ((packed & 0x0000_FFFF_0000_0000L) >> 32);
- int axisCount = (int) ((packed & 0xFFFF_0000_0000_0000L) >> 48);
- FontVariationAxis[] axes = new FontVariationAxis[axisCount];
- char[] charBuffer = new char[4];
- for (int i = 0; i < axisCount; ++i) {
- long packedAxis = nGetAxisInfo(ptr, i);
- float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL));
- charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >> 56);
- charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >> 48);
- charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >> 40);
- charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32);
- axes[i] = new FontVariationAxis(new String(charBuffer), value);
- }
- String path = nGetFontPath(ptr);
- File file = (path == null) ? null : new File(path);
- FontStyle style = new FontStyle(weight,
- italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT);
-
- return new Font(file, ttcIndex, axes, style);
- }
-
- @CriticalNative
- private static native int nGetFamilyCount(long ptr);
-
- @CriticalNative
- private static native long nGetFamily(long ptr, int index);
-
- @FastNative
- private static native String nGetLocaleList(long familyPtr);
-
- @CriticalNative
- private static native long nGetFont(long familyPtr, int fontIndex);
-
- @CriticalNative
- private static native int nGetFontCount(long familyPtr);
-
- @CriticalNative
- private static native long nGetFontInfo(long fontPtr);
-
- @CriticalNative
- private static native long nGetAxisInfo(long fontPtr, int i);
-
- @FastNative
- private static native String nGetFontPath(long fontPtr);
-}
diff --git a/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java b/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java
deleted file mode 100644
index 5655e7f..0000000
--- a/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java
+++ /dev/null
@@ -1,62 +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 android.graphics.fonts;
-
-import android.annotation.NonNull;
-
-import dalvik.annotation.optimization.CriticalNative;
-import dalvik.annotation.optimization.FastNative;
-
-import libcore.util.NativeAllocationRegistry;
-
-import java.nio.ByteBuffer;
-
-/**
- * This is a helper class for showing native allocated buffer in Java API.
- *
- * @hide
- */
-public class NativeFontBufferHelper {
- private NativeFontBufferHelper() {}
-
- private static final NativeAllocationRegistry REGISTRY =
- NativeAllocationRegistry.createMalloced(
- ByteBuffer.class.getClassLoader(), nGetReleaseFunc());
-
- /**
- * Wrap native buffer with ByteBuffer with adding reference to it.
- */
- public static @NonNull ByteBuffer refByteBuffer(long fontPtr) {
- long refPtr = nRefFontBuffer(fontPtr);
- ByteBuffer buffer = nWrapByteBuffer(refPtr);
-
- // Releasing native object so that decreasing shared pointer ref count when the byte buffer
- // is GCed.
- REGISTRY.registerNativeAllocation(buffer, refPtr);
-
- return buffer;
- }
-
- @CriticalNative
- private static native long nRefFontBuffer(long fontPtr);
-
- @FastNative
- private static native ByteBuffer nWrapByteBuffer(long refPtr);
-
- @CriticalNative
- private static native long nGetReleaseFunc();
-}
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
index c2de0ac..8d20e9c 100644
--- a/graphics/java/android/graphics/text/PositionedGlyphs.java
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -184,7 +184,7 @@
long ptr = nGetFont(layoutPtr, i);
if (prevPtr != ptr) {
prevPtr = ptr;
- prevFont = Font.findOrCreateFontFromNativePtr(ptr);
+ prevFont = new Font(ptr);
}
mFonts.add(prevFont);
}
@@ -224,9 +224,7 @@
if (getGlyphId(i) != that.getGlyphId(i)) return false;
if (getGlyphX(i) != that.getGlyphX(i)) return false;
if (getGlyphY(i) != that.getGlyphY(i)) return false;
- // Intentionally using reference equality since font equality is heavy due to buffer
- // compare.
- if (getFont(i) != that.getFont(i)) return false;
+ if (!getFont(i).equals(that.getFont(i))) return false;
}
return true;
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index 372add9..d188b65 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -190,7 +190,7 @@
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 16bf546..0871517 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -434,14 +434,16 @@
@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/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index fa4f8b1..3ebca6a 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -20,6 +20,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
+import android.annotation.SystemApi;
+import android.os.Process;
import android.security.KeyStore;
import android.security.keymaster.KeymasterDefs;
@@ -874,9 +876,18 @@
* which it must be configured in SEPolicy.
* @hide
*/
+ @SystemApi
public static final int NAMESPACE_APPLICATION = -1;
/**
+ * The namespace identifier for the WIFI Keystore namespace.
+ * This must be kept in sync with system/sepolicy/private/keystore2_key_contexts
+ * @hide
+ */
+ @SystemApi
+ public static final int NAMESPACE_WIFI = 102;
+
+ /**
* For legacy support, translate namespaces into known UIDs.
* @hide
*/
@@ -884,6 +895,8 @@
switch (namespace) {
case NAMESPACE_APPLICATION:
return KeyStore.UID_SELF;
+ case NAMESPACE_WIFI:
+ return Process.WIFI_UID;
// TODO Translate WIFI and VPN UIDs once the namespaces are defined.
// b/171305388 and b/171305607
default:
@@ -900,6 +913,8 @@
switch (uid) {
case KeyStore.UID_SELF:
return NAMESPACE_APPLICATION;
+ case Process.WIFI_UID:
+ return NAMESPACE_WIFI;
// TODO Translate WIFI and VPN UIDs once the namespaces are defined.
// b/171305388 and b/171305607
default:
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
index 8475ad9..0f77749 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
@@ -164,6 +164,9 @@
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 32650ae..5619585 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKey.java
@@ -21,7 +21,6 @@
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 @@
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/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 6a92980..70e30d2 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -585,6 +585,30 @@
mSpec.getKeyValidityForConsumptionEnd()
));
}
+ if (mSpec.getCertificateNotAfter() != null) {
+ params.add(KeyStore2ParameterUtils.makeDate(
+ KeymasterDefs.KM_TAG_CERTIFICATE_NOT_AFTER,
+ mSpec.getCertificateNotAfter()
+ ));
+ }
+ if (mSpec.getCertificateNotBefore() != null) {
+ params.add(KeyStore2ParameterUtils.makeDate(
+ KeymasterDefs.KM_TAG_CERTIFICATE_NOT_BEFORE,
+ mSpec.getCertificateNotBefore()
+ ));
+ }
+ if (mSpec.getCertificateSerialNumber() != null) {
+ params.add(KeyStore2ParameterUtils.makeBignum(
+ KeymasterDefs.KM_TAG_CERTIFICATE_SERIAL,
+ mSpec.getCertificateSerialNumber()
+ ));
+ }
+ if (mSpec.getCertificateSubject() != null) {
+ params.add(KeyStore2ParameterUtils.makeBytes(
+ KeymasterDefs.KM_TAG_CERTIFICATE_SUBJECT,
+ mSpec.getCertificateSubject().getEncoded()
+ ));
+ }
if (mSpec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
params.add(KeyStore2ParameterUtils.makeInt(
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 75ac61a..e101115 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -352,14 +352,17 @@
try {
response = keyStore.getKeyEntry(descriptor);
} catch (android.security.KeyStoreException e) {
- if (e.getErrorCode() == ResponseCode.KEY_PERMANENTLY_INVALIDATED) {
- throw new KeyPermanentlyInvalidatedException(
- "User changed or deleted their auth credentials",
- e);
- } else {
- throw (UnrecoverableKeyException)
- new UnrecoverableKeyException("Failed to obtain information about key")
- .initCause(e);
+ switch (e.getErrorCode()) {
+ case ResponseCode.KEY_NOT_FOUND:
+ return null;
+ case ResponseCode.KEY_PERMANENTLY_INVALIDATED:
+ throw new KeyPermanentlyInvalidatedException(
+ "User changed or deleted their auth credentials",
+ e);
+ default:
+ throw (UnrecoverableKeyException)
+ new UnrecoverableKeyException("Failed to obtain information about key")
+ .initCause(e);
}
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 8c8acc4..39607ae 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -866,7 +866,8 @@
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/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
index 4c8ab8d..dcdd7de 100644
--- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
@@ -28,6 +28,7 @@
import android.security.keystore.UserAuthArgs;
import android.system.keystore2.Authorization;
+import java.math.BigInteger;
import java.security.ProviderException;
import java.util.ArrayList;
import java.util.Date;
@@ -154,6 +155,23 @@
}
/**
+ * This function constructs a {@link KeyParameter} expressing a Bignum.
+ * @param tag Must be KeyMint tag with the associated type BIGNUM.
+ * @param b A BitInteger to be stored in the new key parameter.
+ * @return An instance of {@link KeyParameter}.
+ * @hide
+ */
+ static @NonNull KeyParameter makeBignum(int tag, @NonNull BigInteger b) {
+ if (KeymasterDefs.getTagType(tag) != KeymasterDefs.KM_BIGNUM) {
+ throw new IllegalArgumentException("Not a bignum tag: " + tag);
+ }
+ KeyParameter p = new KeyParameter();
+ p.tag = tag;
+ p.value = KeyParameterValue.blob(b.toByteArray());
+ return p;
+ }
+
+ /**
* This function constructs a {@link KeyParameter} expressing date.
* @param tag Must be KeyMint tag with the associated type DATE.
* @param date A date
@@ -167,10 +185,6 @@
KeyParameter p = new KeyParameter();
p.tag = tag;
p.value = KeyParameterValue.dateTime(date.getTime());
- if (p.value.getDateTime() < 0) {
- throw new IllegalArgumentException("Date tag value out of range: "
- + p.value.getDateTime());
- }
return p;
}
/**
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml
index 93a6e7b..b581f55 100644
--- a/libs/WindowManager/Shell/res/layout/pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/pip_menu.xml
@@ -97,14 +97,4 @@
android:padding="@dimen/pip_resize_handle_padding"
android:src="@drawable/pip_resize_handle"
android:background="?android:selectableItemBackgroundBorderless" />
-
- <!-- invisible layer to trap the focus, b/169372603 -->
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@null"
- android:defaultFocusHighlightEnabled="false"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:focusedByDefault="true" />
-t </FrameLayout>
+</FrameLayout>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
index fe97e24..982cc00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -25,6 +25,7 @@
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import java.io.PrintWriter;
import java.util.Optional;
@@ -38,7 +39,7 @@
private static final String TAG = ShellCommandHandlerImpl.class.getSimpleName();
private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
- private final Optional<SplitScreen> mSplitScreenOptional;
+ private final Optional<SplitScreenController> mSplitScreenOptional;
private final Optional<Pip> mPipOptional;
private final Optional<OneHanded> mOneHandedOptional;
private final Optional<HideDisplayCutout> mHideDisplayCutout;
@@ -50,7 +51,7 @@
public static ShellCommandHandler create(
ShellTaskOrganizer shellTaskOrganizer,
Optional<LegacySplitScreen> legacySplitScreenOptional,
- Optional<SplitScreen> splitScreenOptional,
+ Optional<SplitScreenController> splitScreenOptional,
Optional<Pip> pipOptional,
Optional<OneHanded> oneHandedOptional,
Optional<HideDisplayCutout> hideDisplayCutout,
@@ -64,7 +65,7 @@
private ShellCommandHandlerImpl(
ShellTaskOrganizer shellTaskOrganizer,
Optional<LegacySplitScreen> legacySplitScreenOptional,
- Optional<SplitScreen> splitScreenOptional,
+ Optional<SplitScreenController> splitScreenOptional,
Optional<Pip> pipOptional,
Optional<OneHanded> oneHandedOptional,
Optional<HideDisplayCutout> hideDisplayCutout,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
index 0958a07..925bf4b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
@@ -25,6 +25,7 @@
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -39,7 +40,7 @@
private final DragAndDropController mDragAndDropController;
private final ShellTaskOrganizer mShellTaskOrganizer;
private final Optional<LegacySplitScreen> mLegacySplitScreenOptional;
- private final Optional<SplitScreen> mSplitScreenOptional;
+ private final Optional<SplitScreenController> mSplitScreenOptional;
private final Optional<AppPairs> mAppPairsOptional;
private final FullscreenTaskListener mFullscreenTaskListener;
private final ShellExecutor mMainExecutor;
@@ -51,7 +52,7 @@
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
Optional<LegacySplitScreen> legacySplitScreenOptional,
- Optional<SplitScreen> splitScreenOptional,
+ Optional<SplitScreenController> splitScreenOptional,
Optional<AppPairs> appPairsOptional,
FullscreenTaskListener fullscreenTaskListener,
Transitions transitions,
@@ -71,7 +72,7 @@
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
Optional<LegacySplitScreen> legacySplitScreenOptional,
- Optional<SplitScreen> splitScreenOptional,
+ Optional<SplitScreenController> splitScreenOptional,
Optional<AppPairs> appPairsOptional,
FullscreenTaskListener fullscreenTaskListener,
Transitions transitions,
@@ -97,7 +98,7 @@
mShellTaskOrganizer.registerOrganizer();
mAppPairsOptional.ifPresent(AppPairs::onOrganizerRegistered);
- mSplitScreenOptional.ifPresent(SplitScreen::onOrganizerRegistered);
+ mSplitScreenOptional.ifPresent(SplitScreenController::onOrganizerRegistered);
// Bind the splitscreen impl to the drag drop controller
mDragAndDropController.initialize(mSplitScreenOptional);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
index 7ea4689..255e4d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.animation
-import android.os.Looper
import android.util.ArrayMap
import android.util.Log
import android.view.View
@@ -847,7 +846,7 @@
* pass to [spring].
*/
data class SpringConfig internal constructor(
- internal var stiffness: Float,
+ var stiffness: Float,
internal var dampingRatio: Float,
internal var startVelocity: Float = 0f,
internal var finalPosition: Float = UNSET
@@ -879,8 +878,8 @@
*/
data class FlingConfig internal constructor(
internal var friction: Float,
- internal var min: Float,
- internal var max: Float,
+ var min: Float,
+ var max: Float,
internal var startVelocity: Float
) {
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 40fdb97..0ee1f06 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 @@
}
}
+ @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 @@
* 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 29458ef..2100428 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 @@
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 @@
}
/**
+ * 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 @@
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 16cd3cf..51d63cf 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 @@
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 39510e5..a3edc20 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.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 @@
@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 @@
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 @@
/** 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 @@
/** 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 @@
// 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 @@
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 @@
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();
+ }
+ });
}
/**
@@ -1478,6 +1539,9 @@
* Update bubble order and pointer position.
*/
public void updateBubbleOrder(List<Bubble> bubbles) {
+ if (isExpansionAnimating()) {
+ return;
+ }
final Runnable reorder = () -> {
for (int i = 0; i < bubbles.size(); i++) {
Bubble bubble = bubbles.get(i);
@@ -1536,7 +1600,8 @@
// 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.
@@ -1572,7 +1637,7 @@
mExpandedViewContainer.setAlpha(0.0f);
mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
if (previouslySelected != null) {
- previouslySelected.setContentVisibility(false);
+ previouslySelected.setTaskViewVisibility(false);
}
updateExpandedBubble();
@@ -1653,6 +1718,58 @@
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();
@@ -1662,6 +1779,7 @@
}
beforeExpandedViewAnimation();
+ updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */);
mBubbleContainer.setActiveController(mExpandedAnimationController);
updateOverflowVisibility();
updatePointerPosition();
@@ -1710,7 +1828,7 @@
// 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);
}
@@ -1724,20 +1842,29 @@
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,
@@ -1792,22 +1919,15 @@
// 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();
@@ -1836,31 +1956,25 @@
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;
@@ -1875,10 +1989,10 @@
mExpandedBubble));
}
updateOverflowVisibility();
-
+ updateBadgesAndZOrder(true /* setBadgeForCollapsedStack */);
afterExpandedViewAnimation();
if (previouslySelected != null) {
- previouslySelected.setContentVisibility(false);
+ previouslySelected.setTaskViewVisibility(false);
}
if (mPositioner.showingInTaskbar()) {
@@ -1899,22 +2013,21 @@
// 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();
}
@@ -1943,7 +2056,8 @@
pivotX, pivotY);
} else {
mExpandedViewContainerMatrix.setScale(
- 0f, 0f,
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
+ 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT,
expandingFromBubbleDestination + mBubbleSize / 2f,
getExpandedViewY());
}
@@ -1968,10 +2082,7 @@
mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
})
.withEndActions(() -> {
- if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
- mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
- }
-
+ mExpandedViewHidden = false;
mIsBubbleSwitchAnimating = false;
})
.start();
@@ -2485,6 +2596,7 @@
&& mExpandedBubble.getExpandedView() != null) {
BubbleExpandedView bev = mExpandedBubble.getExpandedView();
bev.setContentVisibility(false);
+ bev.setAlphaAnimating(!mIsExpansionAnimating);
mExpandedViewContainerMatrix.setScaleX(0f);
mExpandedViewContainerMatrix.setScaleY(0f);
mExpandedViewContainerMatrix.setTranslate(0f, 0f);
@@ -2582,7 +2694,14 @@
mAnimatingOutBubbleBuffer.getHardwareBuffer(),
mAnimatingOutBubbleBuffer.getColorSpace());
- mSurfaceSynchronizer.syncSurfaceAndRun(() -> post(() -> onComplete.accept(true)));
+ mAnimatingOutSurfaceView.setAlpha(1f);
+ mExpandedViewContainer.setVisibility(View.GONE);
+
+ mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
+ post(() -> {
+ onComplete.accept(true);
+ });
+ });
});
}
@@ -2614,7 +2733,9 @@
}
}
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);
@@ -2623,7 +2744,6 @@
}
mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
- updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */);
}
/**
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 ec900be..da4259c 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 @@
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/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 616f24a..fb70cbe5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -40,9 +40,11 @@
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.SurfaceControl;
+import android.view.SurfaceSession;
import android.view.SurfaceControlViewHost;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
import android.window.ClientWindowFrames;
@@ -251,6 +253,8 @@
public class SysUiWindowManager extends WindowlessWindowManager {
final int mDisplayId;
ContainerWindow mContainerWindow;
+ final HashMap<IBinder, SurfaceControl> mLeashForWindow =
+ new HashMap<IBinder, SurfaceControl>();
public SysUiWindowManager(int displayId, Context ctx, SurfaceControl rootSurface,
ContainerWindow container) {
super(ctx.getResources().getConfiguration(), rootSurface, null /* hostInputToken */);
@@ -263,7 +267,33 @@
}
SurfaceControl getSurfaceControlForWindow(View rootView) {
- return getSurfaceControl(rootView);
+ synchronized (this) {
+ return mLeashForWindow.get(getWindowBinder(rootView));
+ }
+ }
+
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+ SurfaceControl leash = new SurfaceControl.Builder(new SurfaceSession())
+ .setContainerLayer()
+ .setName("SystemWindowLeash")
+ .setHidden(false)
+ .setParent(mRootSurface)
+ .setCallsite("SysUiWIndowManager#attachToParentSurface").build();
+ synchronized (this) {
+ mLeashForWindow.put(window.asBinder(), leash);
+ }
+ b.setParent(leash);
+ }
+
+ @Override
+ public void remove(android.view.IWindow window) throws RemoteException {
+ super.remove(window);
+ synchronized(this) {
+ IBinder token = window.asBinder();
+ new SurfaceControl.Transaction().remove(mLeashForWindow.get(token))
+ .apply();
+ mLeashForWindow.remove(token);
+ }
}
void setTouchableRegionForWindow(View rootView, Region region) {
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 7f9c34f..728f60d 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
@@ -87,7 +87,7 @@
}
@Override
- protected void attachToParentSurface(SurfaceControl.Builder b) {
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
mParentContainerCallbacks.attachToParentSurface(b);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
index c8938ad..1770943 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java
@@ -53,6 +53,7 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import java.util.Optional;
@@ -66,7 +67,7 @@
private final Context mContext;
private final DisplayController mDisplayController;
- private SplitScreen mSplitScreen;
+ private SplitScreenController mSplitScreen;
private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>();
private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
@@ -76,7 +77,7 @@
mDisplayController = displayController;
}
- public void initialize(Optional<SplitScreen> splitscreen) {
+ public void initialize(Optional<SplitScreenController> splitscreen) {
mSplitScreen = splitscreen.orElse(null);
mDisplayController.addDisplayWindowListener(this);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 35dcdd5..6f5f2eb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -64,7 +64,9 @@
import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreen.StagePosition;
+import com.android.wm.shell.splitscreen.SplitScreen.StageType;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -81,18 +83,18 @@
private final Context mContext;
private final ActivityTaskManager mActivityTaskManager;
private final Starter mStarter;
- private final SplitScreen mSplitScreen;
+ private final SplitScreenController mSplitScreen;
private final ArrayList<DragAndDropPolicy.Target> mTargets = new ArrayList<>();
private DragSession mSession;
- public DragAndDropPolicy(Context context, SplitScreen splitScreen) {
+ public DragAndDropPolicy(Context context, SplitScreenController splitScreen) {
this(context, ActivityTaskManager.getInstance(), splitScreen, new DefaultStarter(context));
}
@VisibleForTesting
DragAndDropPolicy(Context context, ActivityTaskManager activityTaskManager,
- SplitScreen splitScreen, Starter starter) {
+ SplitScreenController splitScreen, Starter starter) {
mContext = context;
mActivityTaskManager = activityTaskManager;
mSplitScreen = splitScreen;
@@ -200,8 +202,8 @@
final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
final boolean leftOrTop = target.type == TYPE_SPLIT_TOP || target.type == TYPE_SPLIT_LEFT;
- @SplitScreen.StageType int stage = STAGE_TYPE_UNDEFINED;
- @SplitScreen.StagePosition int position = STAGE_POSITION_UNDEFINED;
+ @StageType int stage = STAGE_TYPE_UNDEFINED;
+ @StagePosition int position = STAGE_POSITION_UNDEFINED;
if (target.type != TYPE_FULLSCREEN && mSplitScreen != null) {
// Update launch options for the split side we are targeting.
position = leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT;
@@ -213,7 +215,28 @@
final ClipDescription description = data.getDescription();
final Intent dragData = mSession.dragData;
- mStarter.startClipDescription(description, dragData, stage, position);
+ startClipDescription(description, dragData, stage, position);
+ }
+
+ private void startClipDescription(ClipDescription description, Intent intent,
+ @StageType int stage, @StagePosition int position) {
+ final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
+ final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
+ final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
+ ? intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS) : new Bundle();
+
+ if (isTask) {
+ final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
+ mStarter.startTask(taskId, stage, position, opts);
+ } else if (isShortcut) {
+ final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
+ final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID);
+ final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
+ mStarter.startShortcut(packageName, id, stage, position, opts, user);
+ } else {
+ mStarter.startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), stage, position,
+ opts);
+ }
}
/**
@@ -267,34 +290,13 @@
/**
* Interface for actually committing the task launches.
*/
- @VisibleForTesting
public interface Starter {
- default void startClipDescription(ClipDescription description, Intent intent,
- @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position) {
- final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
- final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
- final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
- ? intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS) : new Bundle();
-
- if (isTask) {
- final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
- startTask(taskId, stage, position, opts);
- } else if (isShortcut) {
- final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
- final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID);
- final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
- startShortcut(packageName, id, stage, position, opts, user);
- } else {
- startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), stage, position, opts);
- }
- }
- void startTask(int taskId, @SplitScreen.StageType int stage,
- @SplitScreen.StagePosition int position, @Nullable Bundle options);
- void startShortcut(String packageName, String shortcutId,
- @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position,
- @Nullable Bundle options, UserHandle user);
- void startIntent(PendingIntent intent, @SplitScreen.StageType int stage,
- @SplitScreen.StagePosition int position, @Nullable Bundle options);
+ void startTask(int taskId, @StageType int stage, @StagePosition int position,
+ @Nullable Bundle options);
+ void startShortcut(String packageName, String shortcutId, @StageType int stage,
+ @StagePosition int position, @Nullable Bundle options, UserHandle user);
+ void startIntent(PendingIntent intent, @StageType int stage, @StagePosition int position,
+ @Nullable Bundle options);
void enterSplitScreen(int taskId, boolean leftOrTop);
void exitSplitScreen();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index 82c4e44..b342336 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -42,7 +42,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import java.util.ArrayList;
@@ -61,7 +61,7 @@
private boolean mIsShowing;
private boolean mHasDropped;
- public DragLayout(Context context, SplitScreen splitscreen) {
+ public DragLayout(Context context, SplitScreenController splitscreen) {
super(context);
mPolicy = new DragAndDropPolicy(context, splitscreen);
mDisplayMargin = context.getResources().getDimensionPixelSize(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
index e13a1db..a18d106 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
@@ -862,14 +862,6 @@
* assigned to it.
*/
private SurfaceControl getWindowSurfaceControl() {
- final ViewRootImpl root = getViewRootImpl();
- if (root == null) {
- return null;
- }
- SurfaceControl out = root.getSurfaceControl();
- if (out != null && out.isValid()) {
- return out;
- }
return mWindowManager.mSystemWindows.getViewSurface(this);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 45aa387..5ffa988 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -16,6 +16,9 @@
package com.android.wm.shell.pip;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
import android.animation.AnimationHandler;
import android.animation.Animator;
import android.animation.RectEvaluator;
@@ -24,11 +27,13 @@
import android.app.TaskInfo;
import android.graphics.Rect;
import android.view.Choreographer;
+import android.view.Surface;
import android.view.SurfaceControl;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.wm.shell.animation.Interpolators;
+import com.android.wm.shell.common.DisplayLayout;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -132,15 +137,21 @@
* leash bounds before transformation/any animation. This is so when we try to construct
* the different transformation matrices for the animation, we are constructing this based off
* the PiP original bounds, rather than the {@param startBounds}, which is post-transformed.
+ *
+ * If non-zero {@param rotationDelta} is given, it means that the display will be rotated by
+ * leaving PiP to fullscreen, and the {@param endBounds} is the fullscreen bounds before the
+ * rotation change.
*/
@VisibleForTesting
public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash,
Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect,
- @PipAnimationController.TransitionDirection int direction, float startingAngle) {
+ @PipAnimationController.TransitionDirection int direction, float startingAngle,
+ @Surface.Rotation int rotationDelta) {
if (mCurrentAnimator == null) {
mCurrentAnimator = setupPipTransitionAnimator(
PipTransitionAnimator.ofBounds(taskInfo, leash, startBounds, startBounds,
- endBounds, sourceHintRect, direction, 0 /* startingAngle */));
+ endBounds, sourceHintRect, direction, 0 /* startingAngle */,
+ rotationDelta));
} else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
&& mCurrentAnimator.isRunning()) {
// If we are still animating the fade into pip, then just move the surface and ensure
@@ -156,7 +167,7 @@
mCurrentAnimator.cancel();
mCurrentAnimator = setupPipTransitionAnimator(
PipTransitionAnimator.ofBounds(taskInfo, leash, baseBounds, startBounds,
- endBounds, sourceHintRect, direction, startingAngle));
+ endBounds, sourceHintRect, direction, startingAngle, rotationDelta));
}
return mCurrentAnimator;
}
@@ -410,7 +421,8 @@
static PipTransitionAnimator<Rect> ofBounds(TaskInfo taskInfo, SurfaceControl leash,
Rect baseValue, Rect startValue, Rect endValue, Rect sourceHintRect,
- @PipAnimationController.TransitionDirection int direction, float startingAngle) {
+ @PipAnimationController.TransitionDirection int direction, float startingAngle,
+ @Surface.Rotation int rotationDelta) {
// Just for simplicity we'll interpolate between the source rect hint insets and empty
// insets to calculate the window crop
final Rect initialSourceValue;
@@ -431,6 +443,16 @@
}
final Rect sourceInsets = new Rect(0, 0, 0, 0);
+ final Rect rotatedEndRect;
+ if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) {
+ // Rotate the end bounds according to the rotation delta because the display will
+ // be rotated to the same orientation.
+ rotatedEndRect = new Rect(endValue);
+ DisplayLayout.rotateBounds(rotatedEndRect, endValue, rotationDelta);
+ } else {
+ rotatedEndRect = null;
+ }
+
// construct new Rect instances in case they are recycled
return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS,
endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue),
@@ -444,6 +466,12 @@
final Rect base = getBaseValue();
final Rect start = getStartValue();
final Rect end = getEndValue();
+ if (rotatedEndRect != null) {
+ // Animate the bounds in a different orientation. It only happens when
+ // leaving PiP to fullscreen.
+ applyRotation(tx, leash, fraction, start, end, rotatedEndRect);
+ return;
+ }
Rect bounds = mRectEvaluator.evaluate(fraction, start, end);
float angle = (1.0f - fraction) * startingAngle;
setCurrentValue(bounds);
@@ -469,6 +497,25 @@
tx.apply();
}
+ private void applyRotation(SurfaceControl.Transaction tx, SurfaceControl leash,
+ float fraction, Rect start, Rect end, Rect rotatedEndRect) {
+ final Rect bounds = mRectEvaluator.evaluate(fraction, start, rotatedEndRect);
+ setCurrentValue(bounds);
+ final float degree, x, y;
+ if (rotationDelta == ROTATION_90) {
+ degree = 90 * fraction;
+ x = fraction * (end.right - start.left) + start.left;
+ y = fraction * (end.top - start.top) + start.top;
+ } else {
+ degree = -90 * fraction;
+ x = fraction * (end.left - start.left) + start.left;
+ y = fraction * (end.bottom - start.top) + start.top;
+ }
+ getSurfaceTransactionHelper().rotateAndScaleWithCrop(tx, leash, bounds,
+ rotatedEndRect, degree, x, y);
+ tx.apply();
+ }
+
@Override
void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
getSurfaceTransactionHelper()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index a777a27..97aeda4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -127,6 +127,32 @@
}
/**
+ * Operates the rotation according to the given degrees and scale (setMatrix) according to the
+ * source bounds and rotated destination bounds. The crop will be the unscaled source bounds.
+ * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
+ */
+ public PipSurfaceTransactionHelper rotateAndScaleWithCrop(SurfaceControl.Transaction tx,
+ SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, float degrees,
+ float positionX, float positionY) {
+ mTmpDestinationRect.set(sourceBounds);
+ final int dw = destinationBounds.width();
+ final int dh = destinationBounds.height();
+ // Scale by the short side so there won't be empty area if the aspect ratio of source and
+ // destination are different.
+ final float scale = dw <= dh
+ ? (float) sourceBounds.width() / dw
+ : (float) sourceBounds.height() / dh;
+ // Inverse scale for crop to fit in screen coordinates.
+ mTmpDestinationRect.scale(1 / scale);
+ mTmpTransform.setRotate(degrees);
+ mTmpTransform.postScale(scale, scale);
+ mTmpTransform.postTranslate(positionX, positionY);
+ tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
+ .setWindowCrop(leash, mTmpDestinationRect.width(), mTmpDestinationRect.height());
+ return this;
+ }
+
+ /**
* Resets the scale (setMatrix) on a given transaction and leash if there's any
*
* @return same {@link PipSurfaceTransactionHelper} instance for method chaining
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index fb83006..ad6f435 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -48,13 +48,12 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
import android.graphics.Rect;
-import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.util.Rational;
import android.view.Display;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TaskOrganizer;
import android.window.WindowContainerToken;
@@ -72,8 +71,6 @@
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
@@ -134,7 +131,6 @@
private final PipUiEventLogger mPipUiEventLoggerLogger;
private final int mEnterExitAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
- private final Map<IBinder, Configuration> mInitialState = new HashMap<>();
private final Optional<LegacySplitScreen> mSplitScreenOptional;
protected final ShellTaskOrganizer mTaskOrganizer;
protected final ShellExecutor mMainExecutor;
@@ -192,6 +188,12 @@
private boolean mWaitForFixedRotation;
/**
+ * The rotation that the display will apply after expanding PiP to fullscreen. This is only
+ * meaningful if {@link #mWaitForFixedRotation} is true.
+ */
+ private @Surface.Rotation int mNextRotation;
+
+ /**
* If set to {@code true}, no entering PiP transition would be kicked off and most likely
* it's due to the fact that Launcher is handling the transition directly when swiping
* auto PiP-able Activity to home.
@@ -313,61 +315,40 @@
return;
}
- final Configuration initialConfig = mInitialState.remove(mToken.asBinder());
- if (initialConfig == null) {
- Log.wtf(TAG, "Token not in record, this should not happen mToken=" + mToken);
- return;
- }
mPipUiEventLoggerLogger.log(
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
- final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation()
- != mPipBoundsState.getDisplayLayout().rotation();
final WindowContainerTransaction wct = new WindowContainerTransaction();
- final Rect destinationBounds = initialConfig.windowConfiguration.getBounds();
+ final Rect destinationBounds = mPipBoundsState.getDisplayBounds();
final int direction = syncWithSplitScreenBounds(destinationBounds)
? TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
: TRANSITION_DIRECTION_LEAVE_PIP;
- if (orientationDiffers) {
- mState = State.EXITING_PIP;
- // Send started callback though animation is ignored.
- sendOnPipTransitionStarted(direction);
- // Don't bother doing an animation if the display rotation differs or if it's in
- // a non-supported windowing mode
- applyWindowingModeChangeOnExit(wct, direction);
- mTaskOrganizer.applyTransaction(wct);
- // Send finished callback though animation is ignored.
- sendOnPipTransitionFinished(direction);
- } else {
- final SurfaceControl.Transaction tx =
- mSurfaceControlTransactionFactory.getTransaction();
- mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds,
- mPipBoundsState.getBounds());
- tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
- // We set to fullscreen here for now, but later it will be set to UNDEFINED for
- // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
- wct.setActivityWindowingMode(mToken,
- direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
- ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- : WINDOWING_MODE_FULLSCREEN);
- wct.setBounds(mToken, destinationBounds);
- wct.setBoundsChangeTransaction(mToken, tx);
- mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() {
- @Override
- public void onTransactionReady(int id, SurfaceControl.Transaction t) {
- mMainExecutor.execute(() -> {
- t.apply();
- // Make sure to grab the latest source hint rect as it could have been
- // updated right after applying the windowing mode change.
- final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
- mPictureInPictureParams, destinationBounds);
- scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds,
- 0 /* startingAngle */, sourceHintRect, direction,
- animationDurationMs, null /* updateBoundsCallback */);
- mState = State.EXITING_PIP;
- });
- }
- });
- }
+ final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+ mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mPipBoundsState.getBounds());
+ tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
+ // We set to fullscreen here for now, but later it will be set to UNDEFINED for
+ // the proper windowing mode to take place. See #applyWindowingModeChangeOnExit.
+ wct.setActivityWindowingMode(mToken,
+ direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN
+ ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ : WINDOWING_MODE_FULLSCREEN);
+ wct.setBounds(mToken, destinationBounds);
+ wct.setBoundsChangeTransaction(mToken, tx);
+ mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() {
+ @Override
+ public void onTransactionReady(int id, SurfaceControl.Transaction t) {
+ mMainExecutor.execute(() -> {
+ t.apply();
+ // Make sure to grab the latest source hint rect as it could have been
+ // updated right after applying the windowing mode change.
+ final Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
+ mPictureInPictureParams, destinationBounds);
+ scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds,
+ 0 /* startingAngle */, sourceHintRect, direction,
+ animationDurationMs, null /* updateBoundsCallback */);
+ mState = State.EXITING_PIP;
+ });
+ }
+ });
}
private void applyWindowingModeChangeOnExit(WindowContainerTransaction wct, int direction) {
@@ -399,7 +380,6 @@
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration)
.start();
- mInitialState.remove(mToken.asBinder());
mState = State.EXITING_PIP;
}
@@ -424,7 +404,6 @@
mToken = mTaskInfo.token;
mState = State.TASK_APPEARED;
mLeash = leash;
- mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration));
mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams,
mTaskInfo.topActivityInfo);
@@ -606,6 +585,7 @@
@Override
public void onFixedRotationStarted(int displayId, int newRotation) {
+ mNextRotation = newRotation;
mWaitForFixedRotation = true;
}
@@ -645,7 +625,7 @@
mPipAnimationController.getCurrentAnimator();
if (animator == null || !animator.isRunning()
|| animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
- if (mState.isInPip() && fromRotation) {
+ if (mState.isInPip() && fromRotation && !mWaitForFixedRotation) {
// Update bounds state to final destination first. It's important to do this
// before finishing & cancelling the transition animation so that the MotionHelper
// bounds are synchronized to the destination bounds when the animation ends.
@@ -1052,11 +1032,14 @@
Log.w(TAG, "Abort animation, invalid leash");
return;
}
+ final int rotationDelta = mWaitForFixedRotation
+ ? ((mNextRotation - mPipBoundsState.getDisplayLayout().rotation()) + 4) % 4
+ : Surface.ROTATION_0;
Rect baseBounds = direction == TRANSITION_DIRECTION_SNAP_AFTER_RESIZE
? mPipBoundsState.getBounds() : currentBounds;
mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseBounds, currentBounds, destinationBounds,
- sourceHintRect, direction, startingAngle)
+ sourceHintRect, direction, startingAngle, rotationDelta)
.setTransitionDirection(direction)
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(durationMs)
@@ -1101,11 +1084,6 @@
pw.println(innerPrefix + "mState=" + mState);
pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
- pw.println(innerPrefix + "mInitialState:");
- for (Map.Entry<IBinder, Configuration> e : mInitialState.entrySet()) {
- pw.println(innerPrefix + " binder=" + e.getKey()
- + " winConfig=" + e.getValue().windowConfiguration);
- }
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 91e8c99..9b6909b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -30,6 +30,7 @@
import android.content.Context;
import android.graphics.Rect;
import android.os.IBinder;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -112,7 +113,7 @@
taskInfo.pictureInPictureParams, currentBounds);
animator = mPipAnimationController.getAnimator(taskInfo, leash,
currentBounds, currentBounds, destinationBounds, sourceHintRect,
- TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */);
+ TRANSITION_DIRECTION_TO_PIP, 0 /* startingAngle */, Surface.ROTATION_0);
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
t.setAlpha(leash, 0f);
t.apply();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index a57eee8..ae53005 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -198,8 +198,7 @@
* {@code null}), it will get the leash that the WindowlessWM has assigned to it.
*/
public SurfaceControl getSurfaceControl() {
- SurfaceControl sf = mPipMenuView.getWindowSurfaceControl();
- return sf != null ? sf : mSystemWindows.getViewSurface(mPipMenuView);
+ return mSystemWindows.getViewSurface(mPipMenuView);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
index 6d12752..3eeba6e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuIconsAlgorithm.java
@@ -31,7 +31,6 @@
private static final String TAG = "PipMenuIconsAlgorithm";
- private boolean mFinishedLayout = false;
protected ViewGroup mViewRoot;
protected ViewGroup mTopEndContainer;
protected View mDragHandle;
@@ -51,27 +50,16 @@
mDragHandle = dragHandle;
mSettingsButton = settingsButton;
mDismissButton = dismissButton;
+
+ bindInitialViewState();
}
/**
* Updates the position of the drag handle based on where the PIP window is on the screen.
*/
public void onBoundsChanged(Rect bounds) {
- if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null
- || mSettingsButton == null || mDismissButton == null) {
- Log.e(TAG, "One if the required views is null.");
- return;
- }
-
- //We only need to calculate the layout once since it does not change.
- if (!mFinishedLayout) {
- mTopEndContainer.removeView(mSettingsButton);
- mViewRoot.addView(mSettingsButton);
-
- setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP);
- setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP);
- mFinishedLayout = true;
- }
+ // On phones, the menu icons are always static and will never move based on the PIP window
+ // position. No need to do anything here.
}
/**
@@ -84,4 +72,22 @@
v.setLayoutParams(params);
}
}
+
+ /** Calculate the initial state of the menu icons. Called when the menu is first created. */
+ private void bindInitialViewState() {
+ if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null
+ || mSettingsButton == null || mDismissButton == null) {
+ Log.e(TAG, "One of the required views is null.");
+ return;
+ }
+ // The menu view layout starts out with the settings button aligned at the top|end of the
+ // view group next to the dismiss button. On phones, the settings button should be aligned
+ // to the top|start of the view, so move it to parent view group to then align it to the
+ // top|start of the menu.
+ mTopEndContainer.removeView(mSettingsButton);
+ mViewRoot.addView(mSettingsButton);
+
+ setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP);
+ setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index b91ba34..53571ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -107,6 +107,7 @@
private boolean mThresholdCrossed0;
private boolean mThresholdCrossed1;
private boolean mUsingPinchToZoom = false;
+ private float mAngle = 0;
int mFirstIndex = -1;
int mSecondIndex = -1;
@@ -420,18 +421,25 @@
float down1X = mDownSecondaryPoint.x;
float down1Y = mDownSecondaryPoint.y;
- // Top right + Bottom left pinch to zoom.
- if ((down0X > focusX && down0Y < focusY && down1X < focusX && down1Y > focusY)
- || (down1X > focusX && down1Y < focusY
- && down0X < focusX && down0Y > focusY)) {
+ if (down0X > focusX && down0Y < focusY && down1X < focusX && down1Y > focusY) {
+ // Top right + Bottom left pinch to zoom.
mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
mLastResizeBounds.centerY(), x0, y0, x1, y1, true);
- } else if ((down0X < focusX && down0Y < focusY
- && down1X > focusX && down1Y > focusY)
- || (down1X < focusX && down1Y < focusY
- && down0X > focusX && down0Y > focusY)) {
+ } else if (down1X > focusX && down1Y < focusY
+ && down0X < focusX && down0Y > focusY) {
+ // Top right + Bottom left pinch to zoom.
+ mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
+ mLastResizeBounds.centerY(), x1, y1, x0, y0, true);
+ } else if (down0X < focusX && down0Y < focusY
+ && down1X > focusX && down1Y > focusY) {
+ // Top left + bottom right pinch to zoom.
mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
mLastResizeBounds.centerY(), x0, y0, x1, y1, false);
+ } else if (down1X < focusX && down1Y < focusY
+ && down0X > focusX && down0Y > focusY) {
+ // Top left + bottom right pinch to zoom.
+ mAngle = calculateRotationAngle(mLastResizeBounds.centerX(),
+ mLastResizeBounds.centerY(), x1, y1, x0, y0, false);
}
mLastResizeBounds.set(PipPinchResizingAlgorithm.pinchResize(x0, y0, x1, y1,
@@ -445,21 +453,26 @@
}
}
- private float mAngle = 0;
-
- private float calculateRotationAngle(int focusX, int focusY, float x0, float y0,
- float x1, float y1, boolean positive) {
+ private float calculateRotationAngle(int pivotX, int pivotY, float topX, float topY,
+ float bottomX, float bottomY, boolean positive) {
// The base angle is the angle formed by taking the angle between the center horizontal
// and one of the corners.
- double baseAngle = Math.toDegrees(Math.atan2(Math.abs(mLastResizeBounds.top - focusY),
- Math.abs(mLastResizeBounds.right - focusX)));
+ double baseAngle = Math.toDegrees(Math.atan2(Math.abs(mLastResizeBounds.top - pivotY),
+ Math.abs(mLastResizeBounds.right - pivotX)));
+
double angle0 = mThresholdCrossed0
- ? Math.toDegrees(Math.atan2(Math.abs(y0 - focusY), Math.abs(x0 - focusX)))
- : baseAngle;
- double angle1 = mThresholdCrossed1
- ? Math.toDegrees(Math.atan2(Math.abs(y1 - focusY), Math.abs(x1 - focusX)))
- : baseAngle;
+ ? Math.toDegrees(Math.atan2(pivotY - topY, topX - pivotX)) : baseAngle;
+ double angle1 = mThresholdCrossed0
+ ? Math.toDegrees(Math.atan2(pivotY - bottomY, bottomX - pivotX)) : baseAngle;
+
+
+ if (positive) {
+ angle1 = angle1 < 0 ? 180 + angle1 : angle1 - 180;
+ } else {
+ angle0 = angle0 < 0 ? -angle0 - 180 : 180 - angle0;
+ angle1 = -angle1;
+ }
// Calculate the percentage difference of [0, 90] compare to the base angle.
double diff0 = (Math.max(0, Math.min(angle0, 90)) - baseAngle) / 90;
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 2c68092..7ca5693 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
@@ -33,6 +33,7 @@
/**
* Interface to engage split-screen feature.
+ * TODO: Figure out which of these are actually needed outside of the Shell
*/
@ExternalThread
public interface SplitScreen extends DragAndDropPolicy.Starter {
@@ -102,18 +103,13 @@
void setSideStagePosition(@StagePosition int sideStagePosition);
/** Hides the side-stage if it is currently visible. */
void setSideStageVisibility(boolean visible);
- default void enterSplitScreen(int taskId, boolean leftOrTop) {
- moveToSideStage(taskId,
- leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT);
- }
+
/** 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);
- /** Dumps current status of split-screen. */
- void dump(@NonNull PrintWriter pw, String prefix);
- /** Called when the shell organizer has been registered. */
- void onOrganizerRegistered();
void registerSplitScreenListener(SplitScreenListener listener);
void unregisterSplitScreenListener(SplitScreenListener listener);
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 18dd53b..b0167af 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
@@ -18,6 +18,13 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
+
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.PendingIntent;
@@ -35,7 +42,9 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.draganddrop.DragAndDropPolicy;
import java.io.PrintWriter;
@@ -44,25 +53,33 @@
* {@link SplitScreen}.
* @see StageCoordinator
*/
-public class SplitScreenController implements SplitScreen {
+public class SplitScreenController implements DragAndDropPolicy.Starter {
private static final String TAG = SplitScreenController.class.getSimpleName();
private final ShellTaskOrganizer mTaskOrganizer;
private final SyncTransactionQueue mSyncQueue;
private final Context mContext;
private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
+ private final ShellExecutor mMainExecutor;
+ private final SplitScreenImpl mImpl = new SplitScreenImpl();
+
private StageCoordinator mStageCoordinator;
public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue, Context context,
- RootTaskDisplayAreaOrganizer rootTDAOrganizer) {
+ RootTaskDisplayAreaOrganizer rootTDAOrganizer,
+ ShellExecutor mainExecutor) {
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
mContext = context;
mRootTDAOrganizer = rootTDAOrganizer;
+ mMainExecutor = mainExecutor;
}
- @Override
+ public SplitScreen asSplitScreen() {
+ return mImpl;
+ }
+
public void onOrganizerRegistered() {
if (mStageCoordinator == null) {
// TODO: Multi-display
@@ -71,13 +88,11 @@
}
}
- @Override
public boolean isSplitScreenVisible() {
return mStageCoordinator.isSplitScreenVisible();
}
- @Override
- public boolean moveToSideStage(int taskId, @StagePosition int sideStagePosition) {
+ public boolean moveToSideStage(int taskId, @SplitScreen.StagePosition int sideStagePosition) {
final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
if (task == null) {
throw new IllegalArgumentException("Unknown taskId" + taskId);
@@ -85,50 +100,50 @@
return moveToSideStage(task, sideStagePosition);
}
- @Override
public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
- @StagePosition int sideStagePosition) {
+ @SplitScreen.StagePosition int sideStagePosition) {
return mStageCoordinator.moveToSideStage(task, sideStagePosition);
}
- @Override
public boolean removeFromSideStage(int taskId) {
return mStageCoordinator.removeFromSideStage(taskId);
}
- @Override
- public void setSideStagePosition(@StagePosition int sideStagePosition) {
+ public void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) {
mStageCoordinator.setSideStagePosition(sideStagePosition);
}
- @Override
public void setSideStageVisibility(boolean visible) {
mStageCoordinator.setSideStageVisibility(visible);
}
- @Override
+ public void enterSplitScreen(int taskId, boolean leftOrTop) {
+ moveToSideStage(taskId,
+ leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT);
+ }
+
public void exitSplitScreen() {
mStageCoordinator.exitSplitScreen();
}
- @Override
+ public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+ mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide);
+ }
+
public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
mStageCoordinator.getStageBounds(outTopOrLeftBounds, outBottomOrRightBounds);
}
- @Override
- public void registerSplitScreenListener(SplitScreenListener listener) {
+ public void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) {
mStageCoordinator.registerSplitScreenListener(listener);
}
- @Override
- public void unregisterSplitScreenListener(SplitScreenListener listener) {
+ public void unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener) {
mStageCoordinator.unregisterSplitScreenListener(listener);
}
- @Override
- public void startTask(int taskId,
- @StageType int stage, @StagePosition int position, @Nullable Bundle options) {
+ public void startTask(int taskId, @SplitScreen.StageType int stage,
+ @SplitScreen.StagePosition int position, @Nullable Bundle options) {
options = resolveStartStage(stage, position, options);
try {
@@ -138,9 +153,9 @@
}
}
- @Override
- public void startShortcut(String packageName, String shortcutId, @StageType int stage,
- @StagePosition int position, @Nullable Bundle options, UserHandle user) {
+ public void startShortcut(String packageName, String shortcutId,
+ @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position,
+ @Nullable Bundle options, UserHandle user) {
options = resolveStartStage(stage, position, options);
try {
@@ -153,9 +168,8 @@
}
}
- @Override
- public void startIntent(PendingIntent intent,
- @StageType int stage, @StagePosition int position, @Nullable Bundle options) {
+ public void startIntent(PendingIntent intent, @SplitScreen.StageType int stage,
+ @SplitScreen.StagePosition int position, @Nullable Bundle options) {
options = resolveStartStage(stage, position, options);
try {
@@ -165,8 +179,8 @@
}
}
- private Bundle resolveStartStage(@StageType int stage, @StagePosition int position,
- @Nullable Bundle options) {
+ private Bundle resolveStartStage(@SplitScreen.StageType int stage,
+ @SplitScreen.StagePosition int position, @Nullable Bundle options) {
switch (stage) {
case STAGE_TYPE_UNDEFINED: {
// Use the stage of the specified position is valid.
@@ -216,7 +230,6 @@
return options;
}
- @Override
public void dump(@NonNull PrintWriter pw, String prefix) {
pw.println(prefix + TAG);
if (mStageCoordinator != null) {
@@ -224,4 +237,120 @@
}
}
+ private class SplitScreenImpl implements SplitScreen {
+ @Override
+ public boolean isSplitScreenVisible() {
+ return mMainExecutor.executeBlockingForResult(() -> {
+ return SplitScreenController.this.isSplitScreenVisible();
+ }, Boolean.class);
+ }
+
+ @Override
+ public boolean moveToSideStage(int taskId, int sideStagePosition) {
+ return mMainExecutor.executeBlockingForResult(() -> {
+ return SplitScreenController.this.moveToSideStage(taskId, sideStagePosition);
+ }, Boolean.class);
+ }
+
+ @Override
+ public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
+ int sideStagePosition) {
+ return mMainExecutor.executeBlockingForResult(() -> {
+ return SplitScreenController.this.moveToSideStage(task, sideStagePosition);
+ }, Boolean.class);
+ }
+
+ @Override
+ public boolean removeFromSideStage(int taskId) {
+ return mMainExecutor.executeBlockingForResult(() -> {
+ return SplitScreenController.this.removeFromSideStage(taskId);
+ }, Boolean.class);
+ }
+
+ @Override
+ public void setSideStagePosition(int sideStagePosition) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.setSideStagePosition(sideStagePosition);
+ });
+ }
+
+ @Override
+ public void setSideStageVisibility(boolean visible) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.setSideStageVisibility(visible);
+ });
+ }
+
+ @Override
+ public void enterSplitScreen(int taskId, boolean leftOrTop) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.enterSplitScreen(taskId, leftOrTop);
+ });
+ }
+
+ @Override
+ public void exitSplitScreen() {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.exitSplitScreen();
+ });
+ }
+
+ @Override
+ public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.exitSplitScreenOnHide(exitSplitScreenOnHide);
+ });
+ }
+
+ @Override
+ public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
+ try {
+ mMainExecutor.executeBlocking(() -> {
+ SplitScreenController.this.getStageBounds(outTopOrLeftBounds,
+ outBottomOrRightBounds);
+ });
+ } catch (InterruptedException e) {
+ Slog.e(TAG, "Failed to get stage bounds in 2s");
+ }
+ }
+
+ @Override
+ public void registerSplitScreenListener(SplitScreenListener listener) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.registerSplitScreenListener(listener);
+ });
+ }
+
+ @Override
+ public void unregisterSplitScreenListener(SplitScreenListener listener) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.unregisterSplitScreenListener(listener);
+ });
+ }
+
+ @Override
+ public void startTask(int taskId, int stage, int position, @Nullable Bundle options) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.startTask(taskId, stage, position, options);
+ });
+ }
+
+ @Override
+ public void startShortcut(String packageName, String shortcutId, int stage, int position,
+ @Nullable Bundle options, UserHandle user) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.startShortcut(packageName, shortcutId, stage, position,
+ options, user);
+ });
+ }
+
+ @Override
+ public void startIntent(PendingIntent intent, int stage, int position,
+ @Nullable Bundle options) {
+ mMainExecutor.execute(() -> {
+ SplitScreenController.this.startIntent(intent, stage, position, options);
+ });
+ }
+ }
+
}
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 176852b..2d4b77e 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 @@
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 @@
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,10 +145,14 @@
}
void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) {
+ if (mSideStagePosition == sideStagePosition) return;
+
mSideStagePosition = sideStagePosition;
if (mSideStageListener.mVisible) {
onStageVisibilityChanged(mSideStageListener);
}
+
+ sendOnStagePositionChanged();
}
void setSideStageVisibility(boolean visible) {
@@ -162,6 +167,10 @@
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 @@
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 @@
}
}
- 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/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 8e24e0b..f374922 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -377,12 +377,10 @@
final int taskId = startingWindowInfo.taskInfo.taskId;
final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken,
snapshot, mMainExecutor, () -> removeWindowSynced(taskId) /* clearWindow */);
- mMainExecutor.execute(() -> {
- mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
- final StartingWindowRecord tView =
- new StartingWindowRecord(null/* decorView */, surface);
- mStartingWindowRecords.put(taskId, tView);
- });
+ mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
+ final StartingWindowRecord tView =
+ new StartingWindowRecord(null/* decorView */, surface);
+ mStartingWindowRecords.put(taskId, tView);
}
/**
@@ -392,42 +390,40 @@
if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
Slog.d(TAG, "Task start finish, remove starting surface for task " + taskId);
}
- mMainExecutor.execute(() -> removeWindowSynced(taskId));
+ removeWindowSynced(taskId);
}
protected void postAddWindow(int taskId, IBinder appToken,
- View view, WindowManager wm, WindowManager.LayoutParams params) {
- mMainExecutor.execute(() -> {
- boolean shouldSaveView = true;
- try {
- wm.addView(view, params);
- } catch (WindowManager.BadTokenException e) {
- // ignore
- Slog.w(TAG, appToken + " already running, starting window not displayed. "
- + e.getMessage());
+ View view, WindowManager wm, WindowManager.LayoutParams params) {
+ boolean shouldSaveView = true;
+ try {
+ wm.addView(view, params);
+ } catch (WindowManager.BadTokenException e) {
+ // ignore
+ Slog.w(TAG, appToken + " already running, starting window not displayed. "
+ + e.getMessage());
+ shouldSaveView = false;
+ } catch (RuntimeException e) {
+ // don't crash if something else bad happens, for example a
+ // failure loading resources because we are loading from an app
+ // on external storage that has been unmounted.
+ Slog.w(TAG, appToken + " failed creating starting window", e);
+ shouldSaveView = false;
+ } finally {
+ if (view != null && view.getParent() == null) {
+ Slog.w(TAG, "view not successfully added to wm, removing view");
+ wm.removeViewImmediate(view);
shouldSaveView = false;
- } catch (RuntimeException e) {
- // don't crash if something else bad happens, for example a
- // failure loading resources because we are loading from an app
- // on external storage that has been unmounted.
- Slog.w(TAG, appToken + " failed creating starting window", e);
- shouldSaveView = false;
- } finally {
- if (view != null && view.getParent() == null) {
- Slog.w(TAG, "view not successfully added to wm, removing view");
- wm.removeViewImmediate(view);
- shouldSaveView = false;
- }
}
+ }
- if (shouldSaveView) {
- removeWindowSynced(taskId);
- mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
- final StartingWindowRecord tView =
- new StartingWindowRecord(view, null /* TaskSnapshotWindow */);
- mStartingWindowRecords.put(taskId, tView);
- }
- });
+ if (shouldSaveView) {
+ removeWindowSynced(taskId);
+ mMainExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
+ final StartingWindowRecord tView =
+ new StartingWindowRecord(view, null /* TaskSnapshotWindow */);
+ mStartingWindowRecords.put(taskId, tView);
+ }
}
protected void removeWindowSynced(int taskId) {
@@ -445,7 +441,7 @@
if (DEBUG_TASK_SNAPSHOT) {
Slog.v(TAG, "Removing task snapshot window for " + taskId);
}
- record.mTaskSnapshotWindow.remove(mMainExecutor);
+ record.mTaskSnapshotWindow.remove();
}
mStartingWindowRecords.remove(taskId);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index b5e1896..a6f44ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -82,6 +82,7 @@
import com.android.internal.policy.DecorView;
import com.android.internal.view.BaseIWindow;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.annotations.ExternalThread;
/**
* This class represents a starting window that shows a snapshot.
@@ -121,6 +122,7 @@
private final Window mWindow;
private final Surface mSurface;
private final Runnable mClearWindowHandler;
+ private final ShellExecutor mMainExecutor;
private SurfaceControl mSurfaceControl;
private SurfaceControl mChildSurfaceControl;
private final IWindowSession mSession;
@@ -213,7 +215,7 @@
final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow(
surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, appearance,
windowFlags, windowPrivateFlags, taskBounds, orientation, activityType,
- topWindowInsetsState, clearWindowHandler);
+ topWindowInsetsState, clearWindowHandler, mainExecutor);
final Window window = snapshotSurface.mWindow;
final InsetsState mTmpInsetsState = new InsetsState();
@@ -229,7 +231,7 @@
} catch (RemoteException e) {
snapshotSurface.clearWindowSynced();
}
- window.setOuter(snapshotSurface, mainExecutor);
+ window.setOuter(snapshotSurface);
try {
session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1,
tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
@@ -249,7 +251,8 @@
TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription,
int appearance, int windowFlags, int windowPrivateFlags, Rect taskBounds,
int currentOrientation, int activityType, InsetsState topWindowInsetsState,
- Runnable clearWindowHandler) {
+ Runnable clearWindowHandler, ShellExecutor mainExecutor) {
+ mMainExecutor = mainExecutor;
mSurface = new Surface();
mSession = WindowManagerGlobal.getWindowSession();
mWindow = new Window();
@@ -286,28 +289,26 @@
mSystemBarBackgroundPainter.drawNavigationBarBackground(c);
}
- void remove(ShellExecutor mainExecutor) {
+ void remove() {
final long now = SystemClock.uptimeMillis();
if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS
// Show the latest content as soon as possible for unlocking to home.
&& mActivityType != ACTIVITY_TYPE_HOME) {
final long delayTime = mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS - now;
- mainExecutor.executeDelayed(() -> remove(mainExecutor), delayTime);
+ mMainExecutor.executeDelayed(() -> remove(), delayTime);
if (DEBUG) {
Slog.d(TAG, "Defer removing snapshot surface in " + delayTime);
}
return;
}
- mainExecutor.execute(() -> {
- try {
- if (DEBUG) {
- Slog.d(TAG, "Removing snapshot surface, mHasDrawn: " + mHasDrawn);
- }
- mSession.remove(mWindow);
- } catch (RemoteException e) {
- // nothing
+ try {
+ if (DEBUG) {
+ Slog.d(TAG, "Removing snapshot surface, mHasDrawn: " + mHasDrawn);
}
- });
+ mSession.remove(mWindow);
+ } catch (RemoteException e) {
+ // nothing
+ }
}
/**
@@ -497,13 +498,12 @@
}
}
+ @ExternalThread
static class Window extends BaseIWindow {
private TaskSnapshotWindow mOuter;
- private ShellExecutor mMainExecutor;
- public void setOuter(TaskSnapshotWindow outer, ShellExecutor mainExecutor) {
+ public void setOuter(TaskSnapshotWindow outer) {
mOuter = outer;
- mMainExecutor = mainExecutor;
}
@Override
@@ -511,22 +511,20 @@
MergedConfiguration mergedConfiguration, boolean forceLayout,
boolean alwaysConsumeSystemBars, int displayId) {
if (mOuter != null) {
- if (mergedConfiguration != null
- && mOuter.mOrientationOnCreation
- != mergedConfiguration.getMergedConfiguration().orientation) {
- // The orientation of the screen is changing. We better remove the snapshot ASAP
- // as we are going to wait on the new window in any case to unfreeze the screen,
- // and the starting window is not needed anymore.
- mMainExecutor.execute(() -> {
+ mOuter.mMainExecutor.execute(() -> {
+ if (mergedConfiguration != null
+ && mOuter.mOrientationOnCreation
+ != mergedConfiguration.getMergedConfiguration().orientation) {
+ // The orientation of the screen is changing. We better remove the snapshot
+ // ASAP as we are going to wait on the new window in any case to unfreeze
+ // the screen, and the starting window is not needed anymore.
mOuter.clearWindowSynced();
- });
- } else if (reportDraw) {
- mMainExecutor.execute(() -> {
+ } else if (reportDraw) {
if (mOuter.mHasDrawn) {
mOuter.reportDrawn();
}
- });
- }
+ }
+ });
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java
new file mode 100644
index 0000000..85bbf74
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitions.java
@@ -0,0 +1,40 @@
+/*
+ * 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.transition;
+
+import android.annotation.NonNull;
+import android.window.IRemoteTransition;
+import android.window.TransitionFilter;
+
+import com.android.wm.shell.common.annotations.ExternalThread;
+
+/**
+ * Interface to manage remote transitions.
+ */
+@ExternalThread
+public interface RemoteTransitions {
+ /**
+ * Registers a remote transition.
+ */
+ void registerRemote(@NonNull TransitionFilter filter,
+ @NonNull IRemoteTransition remoteTransition);
+
+ /**
+ * Unregisters a remote transition.
+ */
+ void unregisterRemote(@NonNull IRemoteTransition remoteTransition);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index d8687bd..1034489 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -67,6 +67,7 @@
private final ShellExecutor mAnimExecutor;
private final TransitionPlayerImpl mPlayerImpl;
private final RemoteTransitionHandler mRemoteTransitionHandler;
+ private final RemoteTransitionImpl mImpl = new RemoteTransitionImpl();
/** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>();
@@ -78,6 +79,10 @@
/** Keeps track of currently tracked transitions and all the animations associated with each */
private final ArrayMap<IBinder, ActiveTransition> mActiveTransitions = new ArrayMap<>();
+ public static RemoteTransitions asRemoteTransitions(Transitions transitions) {
+ return transitions.mImpl;
+ }
+
public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
@NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
mOrganizer = organizer;
@@ -101,8 +106,20 @@
/** Create an empty/non-registering transitions object for system-ui tests. */
@VisibleForTesting
- public static Transitions createEmptyForTesting() {
- return new Transitions();
+ public static RemoteTransitions createEmptyForTesting() {
+ return new RemoteTransitions() {
+ @Override
+ public void registerRemote(@androidx.annotation.NonNull TransitionFilter filter,
+ @androidx.annotation.NonNull IRemoteTransition remoteTransition) {
+ // Do nothing
+ }
+
+ @Override
+ public void unregisterRemote(
+ @androidx.annotation.NonNull IRemoteTransition remoteTransition) {
+ // Do nothing
+ }
+ };
}
/** Register this transition handler with Core */
@@ -134,16 +151,14 @@
}
/** Register a remote transition to be used when `filter` matches an incoming transition */
- @ExternalThread
public void registerRemote(@NonNull TransitionFilter filter,
@NonNull IRemoteTransition remoteTransition) {
- mMainExecutor.execute(() -> mRemoteTransitionHandler.addFiltered(filter, remoteTransition));
+ mRemoteTransitionHandler.addFiltered(filter, remoteTransition);
}
/** Unregisters a remote transition and all associated filters */
- @ExternalThread
public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) {
- mMainExecutor.execute(() -> mRemoteTransitionHandler.removeFiltered(remoteTransition));
+ mRemoteTransitionHandler.removeFiltered(remoteTransition);
}
/** @return true if the transition was triggered by opening something vs closing something */
@@ -371,4 +386,22 @@
mMainExecutor.execute(() -> Transitions.this.requestStartTransition(iBinder, request));
}
}
+
+ @ExternalThread
+ private class RemoteTransitionImpl implements RemoteTransitions {
+ @Override
+ public void registerRemote(@NonNull TransitionFilter filter,
+ @NonNull IRemoteTransition remoteTransition) {
+ mMainExecutor.execute(() -> {
+ Transitions.this.registerRemote(filter, remoteTransition);
+ });
+ }
+
+ @Override
+ public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) {
+ mMainExecutor.execute(() -> {
+ Transitions.this.unregisterRemote(remoteTransition);
+ });
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
index 8258630..f06d57c 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
@@ -25,8 +25,6 @@
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.wm.shell.flicker"/>
- <option name="include-annotation" value="androidx.test.filters.RequiresDevice" />
- <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
<option name="shell-timeout" value="6600s" />
<option name="test-timeout" value="6000s" />
<option name="hidden-api-checks" value="false" />
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 7ad7553..3282ece 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -18,6 +18,7 @@
import android.graphics.Region
import android.view.Surface
+import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy
import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER
@@ -25,6 +26,109 @@
import com.android.server.wm.flicker.traces.layers.getVisibleBounds
@JvmOverloads
+fun LayersAssertionBuilder.appPairsDividerIsVisible(bugId: Int = 0) {
+ end("appPairsDividerIsVisible", bugId) {
+ this.isVisible(APP_PAIR_SPLIT_DIVIDER)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.appPairsDividerIsInvisible(bugId: Int = 0) {
+ end("appPairsDividerIsInVisible", bugId) {
+ this.notExists(APP_PAIR_SPLIT_DIVIDER)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.appPairsDividerBecomesVisible(bugId: Int = 0) {
+ all("dividerLayerBecomesVisible", bugId) {
+ this.hidesLayer(DOCKED_STACK_DIVIDER)
+ .then()
+ .showsLayer(DOCKED_STACK_DIVIDER)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackDividerIsVisible(bugId: Int = 0) {
+ end("dockedStackDividerIsVisible", bugId) {
+ this.isVisible(DOCKED_STACK_DIVIDER)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackDividerBecomesVisible(bugId: Int = 0) {
+ all("dividerLayerBecomesVisible", bugId) {
+ this.hidesLayer(DOCKED_STACK_DIVIDER)
+ .then()
+ .showsLayer(DOCKED_STACK_DIVIDER)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackDividerBecomesInvisible(bugId: Int = 0) {
+ all("dividerLayerBecomesInvisible", bugId) {
+ this.showsLayer(DOCKED_STACK_DIVIDER)
+ .then()
+ .hidesLayer(DOCKED_STACK_DIVIDER)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackDividerIsInvisible(bugId: Int = 0) {
+ end("dockedStackDividerIsInvisible", bugId) {
+ this.notExists(DOCKED_STACK_DIVIDER)
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.appPairsPrimaryBoundsIsVisible(
+ rotation: Int,
+ primaryLayerName: String,
+ bugId: Int = 0
+) {
+ end("PrimaryAppBounds", bugId) {
+ val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
+ this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation))
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.appPairsSecondaryBoundsIsVisible(
+ rotation: Int,
+ secondaryLayerName: String,
+ bugId: Int = 0
+) {
+ end("SecondaryAppBounds", bugId) {
+ val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
+ this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation))
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackPrimaryBoundsIsVisible(
+ rotation: Int,
+ primaryLayerName: String,
+ bugId: Int = 0
+) {
+ end("PrimaryAppBounds", bugId) {
+ val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
+ this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation))
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.dockedStackSecondaryBoundsIsVisible(
+ rotation: Int,
+ secondaryLayerName: String,
+ bugId: Int = 0
+) {
+ end("SecondaryAppBounds", bugId) {
+ val dividerRegion = entry.getVisibleBounds(DOCKED_STACK_DIVIDER)
+ this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation))
+ }
+}
+
+@JvmOverloads
fun LayersAssertionBuilderLegacy.appPairsDividerIsVisible(
bugId: Int = 0,
enabled: Boolean = bugId == 0
@@ -51,8 +155,8 @@
) {
all("dividerLayerBecomesVisible", bugId, enabled) {
this.hidesLayer(DOCKED_STACK_DIVIDER)
- .then()
- .showsLayer(DOCKED_STACK_DIVIDER)
+ .then()
+ .showsLayer(DOCKED_STACK_DIVIDER)
}
}
@@ -73,8 +177,8 @@
) {
all("dividerLayerBecomesVisible", bugId, enabled) {
this.hidesLayer(DOCKED_STACK_DIVIDER)
- .then()
- .showsLayer(DOCKED_STACK_DIVIDER)
+ .then()
+ .showsLayer(DOCKED_STACK_DIVIDER)
}
}
@@ -85,8 +189,8 @@
) {
all("dividerLayerBecomesInvisible", bugId, enabled) {
this.showsLayer(DOCKED_STACK_DIVIDER)
- .then()
- .hidesLayer(DOCKED_STACK_DIVIDER)
+ .then()
+ .hidesLayer(DOCKED_STACK_DIVIDER)
}
}
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 0000000..1869d83
--- /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/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index d257749..c3fd663 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.flicker.apppairs
import android.os.Bundle
-import android.platform.test.annotations.Presubmit
import android.os.SystemClock
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -40,7 +39,6 @@
* Test cold launch app from launcher.
* To run this test: `atest WMShellFlickerTests:AppPairsTestCannotPairNonResizeableApps`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -51,10 +49,9 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
- val testTag = "testAppPairs_cannotPairNonResizeableApps"
val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
withTestName {
- buildTestTag(testTag, configuration)
+ buildTestTag(configuration)
}
transitions {
nonResizeableApp?.launchViaIntent(wmHelper)
@@ -64,17 +61,20 @@
SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
}
assertions {
- layersTrace {
- appPairsDividerIsInvisible()
- }
- windowManagerTrace {
- end("onlyResizeableAppWindowVisible") {
+ presubmit {
+ layersTrace {
+ appPairsDividerIsInvisible()
+ }
+ windowManagerTrace {
val nonResizeableApp = nonResizeableApp
require(nonResizeableApp != null) {
"Non resizeable app not initialized"
}
- isVisible(nonResizeableApp.defaultWindowName)
- isInvisible(primaryApp.defaultWindowName)
+
+ end("onlyResizeableAppWindowVisible") {
+ isVisible(nonResizeableApp.defaultWindowName)
+ isInvisible(primaryApp.defaultWindowName)
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index 257350b..7a2a5e4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -18,7 +18,6 @@
import android.os.Bundle
import android.os.SystemClock
-import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
@@ -38,7 +37,6 @@
* Test cold launch app from launcher.
* To run this test: `atest WMShellFlickerTests:AppPairsTestPairPrimaryAndSecondaryApps`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -49,10 +47,9 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
- val testTag = "testAppPairs_pairPrimaryAndSecondaryApps"
val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
withTestName {
- buildTestTag(testTag, configuration)
+ buildTestTag(configuration)
}
transitions {
// TODO pair apps through normal UX flow
@@ -61,20 +58,27 @@
SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
}
assertions {
- layersTrace {
- appPairsDividerIsVisible()
- end("appsEndingBounds", enabled = false) {
- val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- this.hasVisibleRegion(primaryApp.defaultWindowName,
- appPairsHelper.getPrimaryBounds(dividerRegion))
- .hasVisibleRegion(secondaryApp.defaultWindowName,
- appPairsHelper.getSecondaryBounds(dividerRegion))
+ presubmit {
+ layersTrace {
+ appPairsDividerIsVisible()
+ }
+ windowManagerTrace {
+ end("bothAppWindowsVisible") {
+ isVisible(primaryApp.defaultWindowName)
+ isVisible(secondaryApp.defaultWindowName)
+ }
}
}
- windowManagerTrace {
- end("bothAppWindowsVisible") {
- isVisible(primaryApp.defaultWindowName)
- isVisible(secondaryApp.defaultWindowName)
+
+ flaky {
+ layersTrace {
+ end("appsEndingBounds") {
+ val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
+ this.hasVisibleRegion(primaryApp.defaultWindowName,
+ appPairsHelper.getPrimaryBounds(dividerRegion))
+ .hasVisibleRegion(secondaryApp.defaultWindowName,
+ appPairsHelper.getSecondaryBounds(dividerRegion))
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 0b001f5..d8dc4c2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -18,7 +18,6 @@
import android.os.Bundle
import android.os.SystemClock
-import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.APP_PAIR_SPLIT_DIVIDER
@@ -38,7 +37,6 @@
* Test cold launch app from launcher.
* To run this test: `atest WMShellFlickerTests:AppPairsTestUnpairPrimaryAndSecondaryApps`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -49,10 +47,9 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
- val testTag = "testAppPairs_unpairPrimaryAndSecondaryApps"
val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
withTestName {
- buildTestTag(testTag, configuration)
+ buildTestTag(configuration)
}
setup {
executeShellCommand(
@@ -66,24 +63,31 @@
SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
}
assertions {
- layersTrace {
- appPairsDividerIsInvisible()
- start("appsStartingBounds", enabled = false) {
- val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
- this.hasVisibleRegion(primaryApp.defaultWindowName,
- appPairsHelper.getPrimaryBounds(dividerRegion))
- .hasVisibleRegion(secondaryApp.defaultWindowName,
- appPairsHelper.getSecondaryBounds(dividerRegion))
+ presubmit {
+ layersTrace {
+ appPairsDividerIsInvisible()
}
- end("appsEndingBounds", enabled = false) {
- this.notExists(primaryApp.defaultWindowName)
- .notExists(secondaryApp.defaultWindowName)
+ windowManagerTrace {
+ end("bothAppWindowsInvisible") {
+ isInvisible(primaryApp.defaultWindowName)
+ isInvisible(secondaryApp.defaultWindowName)
+ }
}
}
- windowManagerTrace {
- end("bothAppWindowsInvisible") {
- isInvisible(primaryApp.defaultWindowName)
- isInvisible(secondaryApp.defaultWindowName)
+
+ flaky {
+ layersTrace {
+ start("appsStartingBounds") {
+ val dividerRegion = entry.getVisibleBounds(APP_PAIR_SPLIT_DIVIDER)
+ this.hasVisibleRegion(primaryApp.defaultWindowName,
+ appPairsHelper.getPrimaryBounds(dividerRegion))
+ .hasVisibleRegion(secondaryApp.defaultWindowName,
+ appPairsHelper.getSecondaryBounds(dividerRegion))
+ }
+ end("appsEndingBounds") {
+ this.notExists(primaryApp.defaultWindowName)
+ .notExists(secondaryApp.defaultWindowName)
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index aafa9bf..8aee005 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -18,7 +18,6 @@
import android.os.Bundle
import android.os.SystemClock
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -46,7 +45,6 @@
* Test open apps to app pairs and rotate.
* To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppsInAppPairsMode`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -60,7 +58,7 @@
fun getParams(): Collection<Array<Any>> {
val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
withTestName {
- buildTestTag("testRotateTwoLaunchedAppsInAppPairsMode", configuration)
+ buildTestTag(configuration)
}
transitions {
executeShellCommand(composePairsCommand(
@@ -69,23 +67,28 @@
setRotation(configuration.endRotation)
}
assertions {
- layersTrace {
- navBarLayerRotatesAndScales(Surface.ROTATION_0, configuration.endRotation,
- enabled = false)
- statusBarLayerRotatesScales(Surface.ROTATION_0, configuration.endRotation,
- enabled = false)
- appPairsDividerIsVisible(enabled = false)
- appPairsPrimaryBoundsIsVisible(configuration.endRotation,
- primaryApp.defaultWindowName, bugId = 172776659)
- appPairsSecondaryBoundsIsVisible(configuration.endRotation,
- secondaryApp.defaultWindowName, bugId = 172776659)
+ presubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ end("bothAppWindowsVisible") {
+ isVisible(primaryApp.defaultWindowName)
+ .isVisible(secondaryApp.defaultWindowName)
+ }
+ }
}
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- end("bothAppWindowsVisible") {
- isVisible(primaryApp.defaultWindowName)
- .isVisible(secondaryApp.defaultWindowName)
+
+ flaky {
+ layersTrace {
+ appPairsDividerIsVisible()
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ appPairsPrimaryBoundsIsVisible(configuration.endRotation,
+ primaryApp.defaultWindowName, bugId = 172776659)
+ appPairsSecondaryBoundsIsVisible(configuration.endRotation,
+ secondaryApp.defaultWindowName, bugId = 172776659)
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index 19ca31f..bc99c94 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -62,7 +62,7 @@
fun getParams(): Collection<Array<Any>> {
val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
withTestName {
- buildTestTag("testRotateAndEnterAppPairsMode", configuration)
+ buildTestTag(configuration)
}
transitions {
this.setRotation(configuration.endRotation)
@@ -71,22 +71,37 @@
SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
}
assertions {
- layersTrace {
- navBarLayerRotatesAndScales(Surface.ROTATION_0, configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(Surface.ROTATION_0, configuration.endRotation)
- appPairsDividerIsVisible()
- appPairsPrimaryBoundsIsVisible(configuration.endRotation,
- primaryApp.defaultWindowName, 172776659)
- appPairsSecondaryBoundsIsVisible(configuration.endRotation,
- secondaryApp.defaultWindowName, 172776659)
+ val isRotated = configuration.startRotation.isRotated()
+ presubmit {
+ layersTrace {
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ appPairsDividerIsVisible()
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ }
+ }
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ end("bothAppWindowsVisible") {
+ isVisible(primaryApp.defaultWindowName)
+ isVisible(secondaryApp.defaultWindowName)
+ }
+ }
}
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- end("bothAppWindowsVisible") {
- isVisible(primaryApp.defaultWindowName)
- isVisible(secondaryApp.defaultWindowName)
+ flaky {
+ layersTrace {
+ appPairsPrimaryBoundsIsVisible(configuration.endRotation,
+ primaryApp.defaultWindowName, 172776659)
+ appPairsSecondaryBoundsIsVisible(configuration.endRotation,
+ secondaryApp.defaultWindowName, 172776659)
+
+ if (isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ }
}
}
}
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 7ec22bb..cac46fe 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 @@
/**
* 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 @@
"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 @@
/**
* 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 b90e865..111362a 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 @@
}
}
+ /** {@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 @@
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 @@
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()
}
+ }
- if (!waitUntilClosed()) {
- fail("Couldn't close Pip")
+ /**
+ * 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()
+ }
+
+ /**
+ * 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 2015f49..bc42d5e 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 @@
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 5a3d18d..a14b46e 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 @@
* 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)
- }
- 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()
- }
- }
- }
- }
-
- companion object {
+ testSpec: FlickerTestRunnerFactory.TestSpec
+) : FlickerTestRunner(testSpec) {
+ companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ 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)
+ }
+ }
+ 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()
+ }
+ }
+ }
+ }
+ 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 af62eb9..99a40da 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.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 @@
@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 0000000..eaaa2f6
--- /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 0000000..707d28d
--- /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 c21b594..7576e24 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 @@
* 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()
-
- 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()
- }
- 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)
- }
- }
- }
- }
- }
-
- /** 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()
- }
- }
- assertions {
- windowManagerTrace {
- end("imeWindowAboveApp") {
- isAboveWindow(IME_WINDOW_NAME, testApp.defaultWindowName)
- }
- }
- }
- }
- }
-
- companion object {
- private const val TEST_REPETITIONS = 5
+class PipKeyboardTest(testSpec: FlickerTestRunnerFactory.TestSpec) : FlickerTestRunner(testSpec) {
+ companion object : PipTransitionBase(InstrumentationRegistry.getInstrumentation()) {
+ private const val TAG_IME_VISIBLE = "imeIsVisible"
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<Array<Any>> {
- val supportedRotations = intArrayOf(Surface.ROTATION_0)
- return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+ val imeApp = ImeAppHelper(instrumentation)
+ val baseConfig = getTransitionLaunch(eachRun = false)
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ setup {
+ test {
+ imeApp.launchViaIntent(wmHelper)
+ setRotation(configuration.startRotation)
+ }
+ }
+ teardown {
+ test {
+ imeApp.exit()
+ setRotation(Surface.ROTATION_0)
+ }
+ }
+ transitions {
+ // open the soft keyboard
+ imeApp.openIME(wmHelper)
+ createTag(TAG_IME_VISIBLE)
+
+ // then close it again
+ imeApp.closeIME(wmHelper)
+ }
+ 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)
+ }
+ }
+ }
+ }
+ }
+
+ 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 e579096..f10bd7f 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.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 5e0760c..0000000
--- 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 a00c5f4..adab5e8 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.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 @@
* 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)
- }
+ 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()
}
- transitions {
- setRotation(configuration.endRotation)
+
+ layersTrace {
+ noUncoveredRegions(configuration.startRotation,
+ configuration.endRotation, allStates = false)
}
- teardown {
- eachRun {
- setRotation(Surface.ROTATION_0)
+ }
+
+ 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)
}
- test {
- AppTestBase.removeAllTasksButHome()
- }
- }
- 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)
- }
- 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 3e7eb134..4b826ff 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 @@
@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()
- }
+ val baseConfig = getTransitionLaunch(eachRun = true)
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ setup {
+ eachRun {
+ this.setRotation(configuration.startRotation)
}
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- }
- test {
- if (device.hasPipWindow()) {
- device.closePipWindow()
- }
- testApp.exit()
- }
+ }
+ teardown {
+ eachRun {
+ this.setRotation(Surface.ROTATION_0)
}
- transitions {
- device.expandPipWindow()
- device.waitForIdle()
- }
- assertions {
+ }
+ transitions {
+ pipApp.expandPipWindowToApp(wmHelper)
+ }
+ assertions {
+ presubmit {
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
@@ -100,34 +76,42 @@
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 5d3bc13..62e8221 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 @@
@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()
- }
+ val baseConfig = getTransitionLaunch(eachRun = true)
+ val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
+ setup {
+ eachRun {
+ this.setRotation(configuration.startRotation)
}
- teardown {
- eachRun {
- this.setRotation(Surface.ROTATION_0)
- if (device.hasPipWindow()) {
- device.closePipWindow()
- }
- }
- test {
- if (device.hasPipWindow()) {
- device.closePipWindow()
- }
- testApp.exit()
- }
+ }
+ teardown {
+ eachRun {
+ this.setRotation(Surface.ROTATION_0)
}
- transitions {
- testApp.closePipWindow()
- }
- assertions {
+ }
+ transitions {
+ pipApp.closePipWindow(wmHelper)
+ }
+ assertions {
+ presubmit {
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
@@ -106,12 +81,7 @@
}
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 @@
.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 0000000..eb7bae1
--- /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 0000000..c01bc94
--- /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/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index dca2732..c0ab20d 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -15,7 +15,10 @@
android_test {
name: "WMShellUnitTests",
- srcs: ["**/*.java"],
+ srcs: [
+ "**/*.java",
+ "**/*.kt",
+ ],
static_libs: [
"WindowManager-Shell",
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
index 4bd9bed..17ed396 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
@@ -26,7 +26,7 @@
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.dynamicanimation.animation.SpringForce
import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
+import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.animation.PhysicsAnimator.EndListener
import com.android.wm.shell.animation.PhysicsAnimator.UpdateListener
import com.android.wm.shell.animation.PhysicsAnimatorTestUtils.clearAnimationUpdateFrames
@@ -54,8 +54,7 @@
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner::class)
@SmallTest
-@Ignore("Blocking presubmits - investigating in b/158697054")
-class PhysicsAnimatorTest : SysuiTestCase() {
+class PhysicsAnimatorTest : ShellTestCase() {
private lateinit var viewGroup: ViewGroup
private lateinit var testView: View
private lateinit var testView2: View
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
index 4fab9a5..dd1a6a5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -20,12 +20,12 @@
import android.os.UserHandle
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
-import com.android.systemui.util.mockito.eq
import com.android.wm.shell.ShellTestCase
import junit.framework.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
index fe53641..9f1ee6c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
@@ -21,8 +21,8 @@
import android.view.View
import androidx.dynamicanimation.animation.FloatPropertyCompat
import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.animation.PhysicsAnimatorTestUtils
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.animation.PhysicsAnimatorTestUtils
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -43,7 +43,7 @@
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner::class)
@SmallTest
-class MagnetizedObjectTest : SysuiTestCase() {
+class MagnetizedObjectTest : ShellTestCase() {
/** Incrementing value for fake MotionEvent timestamps. */
private var time = 0L
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index 2572106..19ecc49 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -44,6 +44,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
@@ -65,7 +66,7 @@
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.draganddrop.DragAndDropPolicy.Target;
-import com.android.wm.shell.splitscreen.SplitScreen;
+import com.android.wm.shell.splitscreen.SplitScreenController;
import org.junit.Before;
import org.junit.Test;
@@ -92,7 +93,7 @@
// Both the split-screen and start interface.
@Mock
- private SplitScreen mSplitScreenStarter;
+ private SplitScreenController mSplitScreenStarter;
private DisplayLayout mLandscapeDisplayLayout;
private DisplayLayout mPortraitDisplayLayout;
@@ -127,8 +128,8 @@
mPortraitDisplayLayout = new DisplayLayout(info2, res, false, false);
mInsets = Insets.of(0, 0, 0, 0);
- mPolicy = new DragAndDropPolicy(
- mContext, mActivityTaskManager, mSplitScreenStarter, mSplitScreenStarter);
+ mPolicy = spy(new DragAndDropPolicy(
+ mContext, mActivityTaskManager, mSplitScreenStarter, mSplitScreenStarter));
mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
setClipDataResizeable(mNonResizeableActivityClipData, false);
@@ -204,8 +205,8 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
}
@Test
@@ -216,13 +217,13 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
@@ -233,13 +234,13 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
@@ -250,8 +251,8 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
}
@Test
@@ -262,8 +263,8 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
}
@Test
@@ -275,14 +276,14 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
@@ -294,14 +295,14 @@
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
- verify(mSplitScreenStarter).startClipDescription(any(), any(),
- eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
+ verify(mSplitScreenStarter).startIntent(any(),
+ eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index 0087d91..3147dab 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -16,6 +16,9 @@
package com.android.wm.shell.pip;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
+
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
@@ -34,6 +37,7 @@
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
import org.junit.Before;
import org.junit.Test;
@@ -84,7 +88,7 @@
public void getAnimator_withBounds_returnBoundsAnimator() {
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, new Rect(), new Rect(), new Rect(), null,
- TRANSITION_DIRECTION_TO_PIP, 0);
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
assertEquals("Expect ANIM_TYPE_BOUNDS animation",
animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
@@ -98,13 +102,13 @@
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
- TRANSITION_DIRECTION_TO_PIP, 0);
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
oldAnimator.start();
final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue2, null,
- TRANSITION_DIRECTION_TO_PIP, 0);
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
assertEquals("getAnimator with same type returns same animator",
oldAnimator, newAnimator);
@@ -128,6 +132,21 @@
}
@Test
+ public void pipTransitionAnimator_rotatedEndValue() {
+ final Rect startBounds = new Rect(200, 700, 400, 800);
+ final Rect endBounds = new Rect(0, 0, 500, 1000);
+ final PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController
+ .getAnimator(mTaskInfo, mLeash, null, startBounds, endBounds, null,
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_90);
+ // Apply fraction 1 to compute the end value.
+ animator.applySurfaceControlTransaction(mLeash, new DummySurfaceControlTx(), 1);
+ final Rect rotatedEndBounds = new Rect(endBounds);
+ DisplayLayout.rotateBounds(rotatedEndBounds, endBounds, ROTATION_90);
+
+ assertEquals("Expect 90 degree rotated bounds", rotatedEndBounds, animator.mCurrentValue);
+ }
+
+ @Test
@SuppressWarnings("unchecked")
public void pipTransitionAnimator_updateEndValue() {
final Rect baseValue = new Rect(0, 0, 100, 100);
@@ -136,7 +155,7 @@
final Rect endValue2 = new Rect(200, 200, 300, 300);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
- TRANSITION_DIRECTION_TO_PIP, 0);
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
animator.updateEndValue(endValue2);
@@ -150,7 +169,7 @@
final Rect endValue = new Rect(100, 100, 200, 200);
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
.getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null,
- TRANSITION_DIRECTION_TO_PIP, 0);
+ TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
animator.setPipAnimationCallback(mPipAnimationCallback);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
index 414a0a7..27e5f51 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
@@ -47,6 +47,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.startingsurface.TaskSnapshotWindow;
import org.junit.Test;
@@ -83,7 +84,7 @@
createTaskDescription(Color.WHITE, Color.RED, Color.BLUE),
0 /* appearance */, windowFlags /* windowFlags */, 0 /* privateWindowFlags */,
taskBounds, ORIENTATION_PORTRAIT, ACTIVITY_TYPE_STANDARD, new InsetsState(),
- null /* clearWindow */);
+ null /* clearWindow */, new TestShellExecutor());
}
private TaskSnapshot createTaskSnapshot(int width, int height, Point taskSize,
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index ce1d96c..f481228 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -335,7 +335,6 @@
"jni/YuvToJpegEncoder.cpp",
"jni/fonts/Font.cpp",
"jni/fonts/FontFamily.cpp",
- "jni/fonts/NativeFont.cpp",
"jni/text/LineBreaker.cpp",
"jni/text/MeasuredText.cpp",
"jni/text/TextShaper.cpp",
diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h
index 27be622..d5fee3f 100644
--- a/libs/hwui/DeviceInfo.h
+++ b/libs/hwui/DeviceInfo.h
@@ -35,7 +35,6 @@
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 @@
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 @@
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 ca2ada9..b14ade9 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -54,7 +54,7 @@
}
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 e798f2a..971a53a 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -79,7 +79,6 @@
bool Properties::isolatedProcess = false;
int Properties::contextPriority = 0;
-int Properties::defaultRenderAhead = -1;
float Properties::defaultSdrWhitePoint = 200.f;
bool Properties::load() {
@@ -129,10 +128,6 @@
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 1639143..dcb79ba 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -162,8 +162,6 @@
*/
#define PROPERTY_QEMU_KERNEL "ro.kernel.qemu"
-#define PROPERTY_RENDERAHEAD "debug.hwui.render_ahead"
-
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
@@ -247,8 +245,6 @@
static int contextPriority;
- static int defaultRenderAhead;
-
static float defaultSdrWhitePoint;
private:
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index 0fad2d5..e1f5abd7 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -69,7 +69,6 @@
extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
extern int register_android_graphics_fonts_Font(JNIEnv* env);
extern int register_android_graphics_fonts_FontFamily(JNIEnv* env);
-extern int register_android_graphics_fonts_NativeFont(JNIEnv* env);
extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env);
extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
@@ -136,7 +135,6 @@
REG_JNI(register_android_graphics_drawable_VectorDrawable),
REG_JNI(register_android_graphics_fonts_Font),
REG_JNI(register_android_graphics_fonts_FontFamily),
- REG_JNI(register_android_graphics_fonts_NativeFont),
REG_JNI(register_android_graphics_pdf_PdfDocument),
REG_JNI(register_android_graphics_pdf_PdfEditor),
REG_JNI(register_android_graphics_pdf_PdfRenderer),
diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp
index 97c40d6..fa1752c 100644
--- a/libs/hwui/jni/RenderEffect.cpp
+++ b/libs/hwui/jni/RenderEffect.cpp
@@ -115,6 +115,18 @@
return reinterpret_cast<jlong>(composeFilter.release());
}
+static jlong createShaderEffect(
+ JNIEnv* env,
+ jobject,
+ jlong shaderHandle
+) {
+ auto* shader = reinterpret_cast<const SkShader*>(shaderHandle);
+ sk_sp<SkImageFilter> shaderFilter = SkImageFilters::Shader(
+ sk_ref_sp(shader), nullptr
+ );
+ return reinterpret_cast<jlong>(shaderFilter.release());
+}
+
static void RenderEffect_safeUnref(SkImageFilter* filter) {
SkSafeUnref(filter);
}
@@ -130,7 +142,8 @@
{"nativeCreateBitmapEffect", "(JFFFFFFFF)J", (void*)createBitmapEffect},
{"nativeCreateColorFilterEffect", "(JJ)J", (void*)createColorFilterEffect},
{"nativeCreateBlendModeEffect", "(JJI)J", (void*)createBlendModeEffect},
- {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect}
+ {"nativeCreateChainEffect", "(JJ)J", (void*)createChainEffect},
+ {"nativeCreateShaderEffect", "(J)J", (void*)createShaderEffect}
};
int register_android_graphics_RenderEffect(JNIEnv* env) {
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index a146b64..4966bfa 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_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 @@
{"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 b769d40..c8471a9 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -34,6 +34,7 @@
#include <hwui/Typeface.h>
#include <minikin/FontFamily.h>
#include <minikin/FontFileParser.h>
+#include <minikin/LocaleList.h>
#include <ui/FatVector.h>
#include <memory>
@@ -149,12 +150,8 @@
return reinterpret_cast<jlong>(new FontWrapper(std::move(newFont)));
}
-// Critical Native
-static jlong Font_Builder_getReleaseNativeFont(CRITICAL_JNI_PARAMS) {
- return reinterpret_cast<jlong>(releaseFont);
-}
-
///////////////////////////////////////////////////////////////////////////////
+// Font JNI functions
// Fast Native
static jfloat Font_getGlyphBounds(JNIEnv* env, jobject, jlong fontHandle, jint glyphId,
@@ -195,51 +192,92 @@
}
// Critical Native
-static jlong Font_getNativeFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
- FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
- return reinterpret_cast<jlong>(font->font.get());
+static jlong Font_getMinikinFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ return reinterpret_cast<jlong>(font->font->typeface().get());
}
// Critical Native
-static jlong Font_GetBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
- FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
- const void* bufferPtr = font->font->typeface()->GetFontData();
- return reinterpret_cast<jlong>(bufferPtr);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-struct FontBufferWrapper {
- FontBufferWrapper(const std::shared_ptr<minikin::MinikinFont>& font) : minikinFont(font) {}
- // MinikinFont holds a shared pointer of SkTypeface which has reference to font data.
- std::shared_ptr<minikin::MinikinFont> minikinFont;
-};
-
-static void unrefBuffer(jlong nativePtr) {
- FontBufferWrapper* wrapper = reinterpret_cast<FontBufferWrapper*>(nativePtr);
- delete wrapper;
-}
-
-// Critical Native
-static jlong FontBufferHelper_refFontBuffer(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
- const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
- return reinterpret_cast<jlong>(new FontBufferWrapper(font->typeface()));
+static jlong Font_cloneFont(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ std::shared_ptr<minikin::Font> ref = font->font;
+ return reinterpret_cast<jlong>(new FontWrapper(std::move(ref)));
}
// Fast Native
-static jobject FontBufferHelper_wrapByteBuffer(JNIEnv* env, jobject, jlong nativePtr) {
- FontBufferWrapper* wrapper = reinterpret_cast<FontBufferWrapper*>(nativePtr);
- return env->NewDirectByteBuffer(
- const_cast<void*>(wrapper->minikinFont->GetFontData()),
- wrapper->minikinFont->GetFontSize());
+static jobject Font_newByteBuffer(JNIEnv* env, jobject, jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+ return env->NewDirectByteBuffer(const_cast<void*>(minikinFont->GetFontData()),
+ minikinFont->GetFontSize());
}
// Critical Native
-static jlong FontBufferHelper_getReleaseFunc(CRITICAL_JNI_PARAMS) {
- return reinterpret_cast<jlong>(unrefBuffer);
+static jlong Font_getBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ return reinterpret_cast<jlong>(font->font->typeface()->GetFontData());
}
-///////////////////////////////////////////////////////////////////////////////
+// Critical Native
+static jlong Font_getReleaseNativeFontFunc(CRITICAL_JNI_PARAMS) {
+ return reinterpret_cast<jlong>(releaseFont);
+}
+
+// Fast Native
+static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+ const std::string& path = minikinFont->GetFontPath();
+ if (path.empty()) {
+ return nullptr;
+ }
+ return env->NewStringUTF(path.c_str());
+}
+
+// Fast Native
+static jstring Font_getLocaleList(JNIEnv* env, jobject, jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ uint32_t localeListId = font->font->getLocaleListId();
+ if (localeListId == 0) {
+ return nullptr;
+ }
+ std::string langTags = minikin::getLocaleString(localeListId);
+ if (langTags.empty()) {
+ return nullptr;
+ }
+ return env->NewStringUTF(langTags.c_str());
+}
+
+// Critical Native
+static jint Font_getPackedStyle(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ uint32_t weight = font->font->style().weight();
+ uint32_t isItalic = font->font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 1 : 0;
+ return (isItalic << 16) | weight;
+}
+
+// Critical Native
+static jint Font_getIndex(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+ return minikinFont->GetFontIndex();
+}
+
+// Critical Native
+static jint Font_getAxisCount(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+ return minikinFont->GetAxes().size();
+}
+
+// Critical Native
+static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr, jint index) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ const std::shared_ptr<minikin::MinikinFont>& minikinFont = font->font->typeface();
+ minikin::FontVariation var = minikinFont->GetAxes().at(index);
+ uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value);
+ return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary);
+}
// Fast Native
static jlong FontFileUtil_getFontRevision(JNIEnv* env, jobject, jobject buffer, jint index) {
@@ -314,20 +352,23 @@
{"nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;Ljava/lang/String;IZI)J",
(void*)Font_Builder_build},
{"nClone", "(JJIZI)J", (void*)Font_Builder_clone},
- {"nGetReleaseNativeFont", "()J", (void*)Font_Builder_getReleaseNativeFont},
};
static const JNINativeMethod gFontMethods[] = {
- { "nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*) Font_getGlyphBounds },
- { "nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*) Font_getFontMetrics },
- { "nGetNativeFontPtr", "(J)J", (void*) Font_getNativeFontPtr },
- { "nGetFontBufferAddress", "(J)J", (void*) Font_GetBufferAddress },
-};
-
-static const JNINativeMethod gFontBufferHelperMethods[] = {
- { "nRefFontBuffer", "(J)J", (void*) FontBufferHelper_refFontBuffer },
- { "nWrapByteBuffer", "(J)Ljava/nio/ByteBuffer;", (void*) FontBufferHelper_wrapByteBuffer },
- { "nGetReleaseFunc", "()J", (void*) FontBufferHelper_getReleaseFunc },
+ {"nGetMinikinFontPtr", "(J)J", (void*)Font_getMinikinFontPtr},
+ {"nCloneFont", "(J)J", (void*)Font_cloneFont},
+ {"nNewByteBuffer", "(J)Ljava/nio/ByteBuffer;", (void*)Font_newByteBuffer},
+ {"nGetBufferAddress", "(J)J", (void*)Font_getBufferAddress},
+ {"nGetReleaseNativeFont", "()J", (void*)Font_getReleaseNativeFontFunc},
+ {"nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*)Font_getGlyphBounds},
+ {"nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F",
+ (void*)Font_getFontMetrics},
+ {"nGetFontPath", "(J)Ljava/lang/String;", (void*)Font_getFontPath},
+ {"nGetLocaleList", "(J)Ljava/lang/String;", (void*)Font_getLocaleList},
+ {"nGetPackedStyle", "(J)I", (void*)Font_getPackedStyle},
+ {"nGetIndex", "(J)I", (void*)Font_getIndex},
+ {"nGetAxisCount", "(J)I", (void*)Font_getAxisCount},
+ {"nGetAxisInfo", "(JI)J", (void*)Font_getAxisInfo},
};
static const JNINativeMethod gFontFileUtilMethods[] = {
@@ -343,8 +384,6 @@
NELEM(gFontBuilderMethods)) +
RegisterMethodsOrDie(env, "android/graphics/fonts/Font", gFontMethods,
NELEM(gFontMethods)) +
- RegisterMethodsOrDie(env, "android/graphics/fonts/NativeFontBufferHelper",
- gFontBufferHelperMethods, NELEM(gFontBufferHelperMethods)) +
RegisterMethodsOrDie(env, "android/graphics/fonts/FontFileUtil", gFontFileUtilMethods,
NELEM(gFontFileUtilMethods));
}
diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp
index a07723f..b682135 100644
--- a/libs/hwui/jni/fonts/FontFamily.cpp
+++ b/libs/hwui/jni/fonts/FontFamily.cpp
@@ -95,22 +95,36 @@
}
// CriticalNative
-static jint FontFamily_getVariant(jlong familyPtr) {
+static jint FontFamily_getVariant(CRITICAL_JNI_PARAMS_COMMA jlong familyPtr) {
FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
return static_cast<jint>(family->family->variant());
}
+// CriticalNative
+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(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)));
+}
+
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gFontFamilyBuilderMethods[] = {
{ "nInitBuilder", "()J", (void*) FontFamily_Builder_initBuilder },
{ "nAddFont", "(JJ)V", (void*) FontFamily_Builder_addFont },
{ "nBuild", "(JLjava/lang/String;IZ)J", (void*) FontFamily_Builder_build },
-
{ "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc },
};
static const JNINativeMethod gFontFamilyMethods[] = {
+ {"nGetFontSize", "(J)I", (void*)FontFamily_getFontSize},
+ {"nGetFont", "(JI)J", (void*)FontFamily_getFont},
{"nGetLangTags", "(J)Ljava/lang/String;", (void*)FontFamily_getLangTags},
{"nGetVariant", "(J)I", (void*)FontFamily_getVariant},
};
diff --git a/libs/hwui/jni/fonts/NativeFont.cpp b/libs/hwui/jni/fonts/NativeFont.cpp
deleted file mode 100644
index c5c5d46..0000000
--- a/libs/hwui/jni/fonts/NativeFont.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * 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.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "Minikin"
-
-#include "Font.h"
-#include "SkData.h"
-#include "SkFont.h"
-#include "SkFontMetrics.h"
-#include "SkFontMgr.h"
-#include "SkRefCnt.h"
-#include "SkTypeface.h"
-#include "GraphicsJNI.h"
-#include <nativehelper/ScopedUtfChars.h>
-#include "Utils.h"
-#include "FontUtils.h"
-
-#include <hwui/MinikinSkia.h>
-#include <hwui/Paint.h>
-#include <hwui/Typeface.h>
-#include <minikin/FontFamily.h>
-#include <minikin/LocaleList.h>
-#include <ui/FatVector.h>
-
-#include <memory>
-
-namespace android {
-
-// Critical Native
-static jint NativeFont_getFamilyCount(CRITICAL_JNI_PARAMS_COMMA jlong typefaceHandle) {
- Typeface* tf = reinterpret_cast<Typeface*>(typefaceHandle);
- return tf->fFontCollection->getFamilies().size();
-}
-
-// Critical Native
-static jlong NativeFont_getFamily(CRITICAL_JNI_PARAMS_COMMA jlong typefaceHandle, jint index) {
- Typeface* tf = reinterpret_cast<Typeface*>(typefaceHandle);
- return reinterpret_cast<jlong>(tf->fFontCollection->getFamilies()[index].get());
-
-}
-
-// Fast Native
-static jstring NativeFont_getLocaleList(JNIEnv* env, jobject, jlong familyHandle) {
- minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle);
- uint32_t localeListId = family->localeListId();
- return env->NewStringUTF(minikin::getLocaleString(localeListId).c_str());
-}
-
-// Critical Native
-static jint NativeFont_getFontCount(CRITICAL_JNI_PARAMS_COMMA jlong familyHandle) {
- minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle);
- return family->getNumFonts();
-}
-
-// Critical Native
-static jlong NativeFont_getFont(CRITICAL_JNI_PARAMS_COMMA jlong familyHandle, jint index) {
- minikin::FontFamily* family = reinterpret_cast<minikin::FontFamily*>(familyHandle);
- return reinterpret_cast<jlong>(family->getFont(index));
-}
-
-// Critical Native
-static jlong NativeFont_getFontInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
- const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
- MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
-
- uint64_t result = font->style().weight();
- result |= font->style().slant() == minikin::FontStyle::Slant::ITALIC ? 0x10000 : 0x00000;
- result |= ((static_cast<uint64_t>(minikinSkia->GetFontIndex())) << 32);
- result |= ((static_cast<uint64_t>(minikinSkia->GetAxes().size())) << 48);
- return result;
-}
-
-// Critical Native
-static jlong NativeFont_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle, jint index) {
- const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
- MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
- const minikin::FontVariation& var = minikinSkia->GetAxes().at(index);
- uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value);
- return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary);
-}
-
-// FastNative
-static jstring NativeFont_getFontPath(JNIEnv* env, jobject, jlong fontHandle) {
- const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle);
- MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get());
- const std::string& filePath = minikinSkia->getFilePath();
- if (filePath.empty()) {
- return nullptr;
- }
- return env->NewStringUTF(filePath.c_str());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static const JNINativeMethod gNativeFontMethods[] = {
- { "nGetFamilyCount", "(J)I", (void*) NativeFont_getFamilyCount },
- { "nGetFamily", "(JI)J", (void*) NativeFont_getFamily },
- { "nGetLocaleList", "(J)Ljava/lang/String;", (void*) NativeFont_getLocaleList },
- { "nGetFontCount", "(J)I", (void*) NativeFont_getFontCount },
- { "nGetFont", "(JI)J", (void*) NativeFont_getFont },
- { "nGetFontInfo", "(J)J", (void*) NativeFont_getFontInfo },
- { "nGetAxisInfo", "(JI)J", (void*) NativeFont_getAxisInfo },
- { "nGetFontPath", "(J)Ljava/lang/String;", (void*) NativeFont_getFontPath },
-};
-
-int register_android_graphics_fonts_NativeFont(JNIEnv* env) {
- return RegisterMethodsOrDie(env, "android/graphics/fonts/NativeFont", gNativeFontMethods,
- NELEM(gNativeFontMethods));
-}
-
-} // namespace android
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
index 9785aa5..a6fb958 100644
--- a/libs/hwui/jni/text/TextShaper.cpp
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -23,13 +23,14 @@
#include <set>
#include <algorithm>
-#include "SkPaint.h"
-#include "SkTypeface.h"
#include <hwui/MinikinSkia.h>
#include <hwui/MinikinUtils.h>
#include <hwui/Paint.h>
-#include <minikin/MinikinPaint.h>
#include <minikin/MinikinFont.h>
+#include <minikin/MinikinPaint.h>
+#include "FontUtils.h"
+#include "SkPaint.h"
+#include "SkTypeface.h"
namespace android {
@@ -149,7 +150,8 @@
// CriticalNative
static jlong TextShaper_Result_getFont(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
- return reinterpret_cast<jlong>(layout->layout.getFont(i));
+ std::shared_ptr<minikin::Font> fontRef = layout->layout.getFontRef(i);
+ return reinterpret_cast<jlong>(new FontWrapper(std::move(fontRef)));
}
// CriticalNative
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 34df5dd..471a7f7 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -34,7 +34,7 @@
}
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 3b8caeb..7cfccb5 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.cpp
@@ -52,7 +52,7 @@
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 @@
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 85924c5..d998e50 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -101,7 +101,8 @@
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 @@
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 37a6ee7..65afcc3 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -108,7 +108,6 @@
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
mProfiler.setDensity(DeviceInfo::getDensity());
- setRenderAheadDepth(Properties::defaultRenderAhead);
}
CanvasContext::~CanvasContext() {
@@ -157,24 +156,17 @@
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 @@
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 @@
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 @@
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 cc4eb32..b31883b 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -193,9 +193,6 @@
return mUseForceDark;
}
- // Must be called before setSurface
- void setRenderAheadDepth(int renderAhead);
-
SkISize getNextFrameSize() const;
private:
@@ -211,7 +208,6 @@
bool isSwapChainStuffed();
bool surfaceRequiresRedraw();
- void setPresentTime();
void setupPipelineSurface();
SkRect computeDirtyRect(const Frame& frame, SkRect* dirty);
@@ -232,9 +228,6 @@
// 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 b51f6dc..0ade8dd 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -295,11 +295,6 @@
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 33dabc9..a4adb16 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -123,23 +123,6 @@
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/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp
index 06f158f..6a9a98d 100644
--- a/libs/hwui/tests/common/TestContext.cpp
+++ b/libs/hwui/tests/common/TestContext.cpp
@@ -40,9 +40,9 @@
return info;
}
-const DisplayConfig& getActiveDisplayConfig() {
- static DisplayConfig config = [] {
- DisplayConfig config;
+const ui::DisplayMode& getActiveDisplayMode() {
+ static ui::DisplayMode config = [] {
+ ui::DisplayMode config;
#if HWUI_NULL_GPU
config.resolution = ui::Size(1080, 1920);
config.xDpi = config.yDpi = 320.f;
@@ -51,7 +51,7 @@
const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken();
LOG_ALWAYS_FATAL_IF(!token, "%s: No internal display", __FUNCTION__);
- const status_t status = SurfaceComposerClient::getActiveDisplayConfig(token, &config);
+ const status_t status = SurfaceComposerClient::getActiveDisplayMode(token, &config);
LOG_ALWAYS_FATAL_IF(status, "%s: Failed to get active display config", __FUNCTION__);
#endif
return config;
diff --git a/libs/hwui/tests/common/TestContext.h b/libs/hwui/tests/common/TestContext.h
index a012ecb..7d2f6d8 100644
--- a/libs/hwui/tests/common/TestContext.h
+++ b/libs/hwui/tests/common/TestContext.h
@@ -23,8 +23,8 @@
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
-#include <ui/DisplayConfig.h>
#include <ui/DisplayInfo.h>
+#include <ui/DisplayMode.h>
#include <utils/Looper.h>
#include <atomic>
@@ -37,10 +37,10 @@
namespace test {
const DisplayInfo& getDisplayInfo();
-const DisplayConfig& getActiveDisplayConfig();
+const ui::DisplayMode& getActiveDisplayMode();
inline const ui::Size& getActiveDisplayResolution() {
- return getActiveDisplayConfig().resolution;
+ return getActiveDisplayMode().resolution;
}
class TestContext {
diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h
index 74a039b..91022cf 100644
--- a/libs/hwui/tests/common/TestScene.h
+++ b/libs/hwui/tests/common/TestScene.h
@@ -38,7 +38,6 @@
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 eda5d22..8c7d261 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -153,11 +153,6 @@
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 88d33c3..174a140 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -69,7 +69,6 @@
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 @@
Onscreen,
Offscreen,
Renderer,
- RenderAhead,
};
}
@@ -187,7 +185,6 @@
{"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 @@
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 62bf39c..1d3f9d7 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/libs/tracingproxy/Android.bp b/libs/tracingproxy/Android.bp
new file mode 100644
index 0000000..67f407f
--- /dev/null
+++ b/libs/tracingproxy/Android.bp
@@ -0,0 +1,42 @@
+// 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.
+
+// Provides C++ wrappers for system services.
+
+cc_library_shared {
+ name: "libtracingproxy",
+
+ aidl: {
+ export_aidl_headers: true,
+ include_dirs: [
+ "frameworks/base/core/java",
+ ],
+ },
+
+ srcs: [
+ ":ITracingServiceProxy.aidl",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 65721cc..adf58da 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -36,6 +36,7 @@
import android.location.Location;
import android.location.LocationRequest;
import android.location.LocationTime;
+import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
import android.os.Bundle;
import android.os.ICancellationSignal;
@@ -91,6 +92,9 @@
void addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, String packageName, @nullable String attributionTag);
void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
+ void addProviderRequestListener(in IProviderRequestListener listener);
+ void removeProviderRequestListener(in IProviderRequestListener listener);
+
int getGnssBatchSize();
void startGnssBatch(long periodNanos, in ILocationListener listener, String packageName, @nullable String attributionTag, @nullable String listenerId);
void flushGnssBatch();
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index d569482..088b789 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -49,7 +49,10 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
+import android.location.provider.ProviderRequest;
+import android.location.provider.ProviderRequest.Listener;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -436,6 +439,9 @@
new GnssNavigationTransportManager();
}
+ private static final ProviderRequestTransportManager sProviderRequestListeners =
+ new ProviderRequestTransportManager();
+
private final Context mContext;
private final ILocationManager mService;
@@ -2772,6 +2778,37 @@
}
/**
+ * Registers a {@link ProviderRequest.Listener} to all providers.
+ *
+ * @param executor the executor that the callback runs on
+ * @param listener the listener to register
+ * @return {@code true} always
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public boolean registerProviderRequestListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Listener listener) {
+ sProviderRequestListeners.addListener(listener,
+ new ProviderRequestTransport(executor, listener));
+ return true;
+ }
+
+ /**
+ * Unregisters a {@link ProviderRequest.Listener}.
+ *
+ * @param listener the listener to remove.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public void unregisterProviderRequestListener(
+ @NonNull Listener listener) {
+ sProviderRequestListeners.removeListener(listener);
+ }
+
+ /**
* Returns the batch size (in number of Location objects) that are supported by the batching
* interface.
*
@@ -2960,6 +2997,22 @@
}
}
+ private static class ProviderRequestTransportManager extends
+ ListenerTransportManager<ProviderRequestTransport> {
+
+ @Override
+ protected void registerTransport(ProviderRequestTransport transport)
+ throws RemoteException {
+ getService().addProviderRequestListener(transport);
+ }
+
+ @Override
+ protected void unregisterTransport(ProviderRequestTransport transport)
+ throws RemoteException {
+ getService().removeProviderRequestListener(transport);
+ }
+ }
+
private static class GetCurrentLocationTransport extends ILocationCallback.Stub implements
ListenerExecutor, CancellationSignal.OnCancelListener {
@@ -3359,6 +3412,36 @@
}
}
+ private static class ProviderRequestTransport extends IProviderRequestListener.Stub
+ implements ListenerTransport<ProviderRequest.Listener> {
+
+ private final Executor mExecutor;
+
+ private volatile @Nullable ProviderRequest.Listener mListener;
+
+ ProviderRequestTransport(Executor executor, ProviderRequest.Listener listener) {
+ Preconditions.checkArgument(executor != null, "invalid null executor");
+ Preconditions.checkArgument(listener != null, "invalid null callback");
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ @Override
+ public void unregister() {
+ mListener = null;
+ }
+
+ @Override
+ public @Nullable ProviderRequest.Listener getListener() {
+ return mListener;
+ }
+
+ @Override
+ public void onProviderRequestChanged(String provider, ProviderRequest request) {
+ execute(mExecutor, listener -> listener.onProviderRequestChanged(provider, request));
+ }
+ }
+
private static class BatchedLocationCallbackWrapper implements LocationListener {
private final BatchedLocationCallback mCallback;
diff --git a/tools/hiddenapi/Android.bp b/location/java/android/location/provider/IProviderRequestListener.aidl
similarity index 60%
copy from tools/hiddenapi/Android.bp
copy to location/java/android/location/provider/IProviderRequestListener.aidl
index e0eb06cb..89d454a 100644
--- a/tools/hiddenapi/Android.bp
+++ b/location/java/android/location/provider/IProviderRequestListener.aidl
@@ -1,11 +1,11 @@
/*
- * 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.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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,
@@ -14,17 +14,13 @@
* limitations under the License.
*/
-python_binary_host {
- name: "merge_csv",
- main: "merge_csv.py",
- srcs: ["merge_csv.py"],
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- embedded_launcher: true
- },
- },
+package android.location.provider;
+
+import android.location.provider.ProviderRequest;
+
+/**
+ * {@hide}
+ */
+oneway interface IProviderRequestListener {
+ void onProviderRequestChanged(String provider, in ProviderRequest request);
}
diff --git a/location/java/android/location/provider/ProviderRequest.java b/location/java/android/location/provider/ProviderRequest.java
index e543b04..b6ec323 100644
--- a/location/java/android/location/provider/ProviderRequest.java
+++ b/location/java/android/location/provider/ProviderRequest.java
@@ -53,6 +53,17 @@
private final boolean mLocationSettingsIgnored;
private final WorkSource mWorkSource;
+ /**
+ * Listener to be invoked when a new request is set to the provider.
+ */
+ public interface Listener {
+
+ /**
+ * Invoked when a new request is set.
+ */
+ void onProviderRequestChanged(@NonNull String provider, @NonNull ProviderRequest request);
+ }
+
private ProviderRequest(
long intervalMillis,
@Quality int quality,
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index d40de76..205c1f4 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -354,7 +354,7 @@
/**
* @hide
- * @return the internal device tyoe
+ * @return the internal device type
*/
public int getInternalType() {
return mPort.type();
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 08deb15..8d090f8 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1,5 +1,4 @@
/*
-/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index 812dac9..27f72687 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -20,6 +20,7 @@
import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -195,6 +196,8 @@
private int mDeviceId;
+ private int mSessionId;
+
/**
* Never use without initializing parameters afterwards
*/
@@ -207,7 +210,10 @@
* @hide
*/
public AudioPlaybackConfiguration(PlayerBase.PlayerIdCard pic, int piid, int uid, int pid) {
- if (DEBUG) { Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer); }
+ if (DEBUG) {
+ Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer
+ + " sessionId=" + pic.mSessionId);
+ }
mPlayerIId = piid;
mPlayerType = pic.mPlayerType;
mClientUid = uid;
@@ -220,6 +226,7 @@
} else {
mIPlayerShell = null;
}
+ mSessionId = pic.mSessionId;
}
/**
@@ -259,6 +266,7 @@
anonymCopy.mClientUid = PLAYER_UPID_INVALID;
anonymCopy.mClientPid = PLAYER_UPID_INVALID;
anonymCopy.mIPlayerShell = null;
+ anonymCopy.mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE;
return anonymCopy;
}
@@ -303,6 +311,17 @@
/**
* @hide
+ * Return the audio session ID associated with this player.
+ * See {@link AudioManager#generateAudioSessionId()}.
+ * @return an audio session ID
+ */
+ @SystemApi
+ public @IntRange(from = 0) int getSessionId() {
+ return mSessionId;
+ }
+
+ /**
+ * @hide
* Return the type of player linked to this configuration.
* <br>Note that player types not exposed in the system API will be represented as
* {@link #PLAYER_TYPE_UNKNOWN}.
@@ -381,6 +400,17 @@
/**
* @hide
+ * Handle a change of audio session Id
+ * @param sessionId the audio session ID
+ */
+ public boolean handleSessionIdEvent(int sessionId) {
+ final boolean changed = sessionId != mSessionId;
+ mSessionId = sessionId;
+ return changed;
+ }
+
+ /**
+ * @hide
* Handle a player state change
* @param event
* @param deviceId active device id or {@Code PLAYER_DEVICEID_INVALID}
@@ -476,7 +506,8 @@
@Override
public int hashCode() {
- return Objects.hash(mPlayerIId, mDeviceId, mPlayerType, mClientUid, mClientPid);
+ return Objects.hash(mPlayerIId, mDeviceId, mPlayerType, mClientUid, mClientPid,
+ mSessionId);
}
@Override
@@ -498,6 +529,7 @@
ips = mIPlayerShell;
}
dest.writeStrongInterface(ips == null ? null : ips.getIPlayer());
+ dest.writeInt(mSessionId);
}
private AudioPlaybackConfiguration(Parcel in) {
@@ -510,6 +542,7 @@
mPlayerAttr = AudioAttributes.CREATOR.createFromParcel(in);
final IPlayer p = IPlayer.Stub.asInterface(in.readStrongBinder());
mIPlayerShell = (p == null) ? null : new IPlayerShell(null, p);
+ mSessionId = in.readInt();
}
@Override
@@ -523,7 +556,8 @@
&& (mDeviceId == that.mDeviceId)
&& (mPlayerType == that.mPlayerType)
&& (mClientUid == that.mClientUid)
- && (mClientPid == that.mClientPid));
+ && (mClientPid == that.mClientPid))
+ && (mSessionId == that.mSessionId);
}
@Override
@@ -533,7 +567,8 @@
+ " type:" + toLogFriendlyPlayerType(mPlayerType)
+ " u/pid:" + mClientUid + "/" + mClientPid
+ " state:" + toLogFriendlyPlayerState(mPlayerState)
- + " attr:" + mPlayerAttr;
+ + " attr:" + mPlayerAttr
+ + " sessionId:" + mSessionId;
}
//=====================================================================
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 175d36f..e056d43 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -836,7 +836,7 @@
mState = STATE_INITIALIZED;
}
- baseRegisterPlayer();
+ baseRegisterPlayer(mSessionId);
}
/**
@@ -866,7 +866,7 @@
// other initialization...
if (nativeTrackInJavaObj != 0) {
- baseRegisterPlayer();
+ baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE);
deferred_connect(nativeTrackInJavaObj);
} else {
mState = STATE_UNINITIALIZED;
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index 06bf5f7..9c6b276 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -119,9 +119,14 @@
*/
public static final int QUALITY_2K = 12;
+ /**
+ * Quality level corresponding to 8K UHD (7680 x 4320) resolution
+ */
+ public static final int QUALITY_8KUHD = 13;
+
// Start and end of quality list
private static final int QUALITY_LIST_START = QUALITY_LOW;
- private static final int QUALITY_LIST_END = QUALITY_2K;
+ private static final int QUALITY_LIST_END = QUALITY_8KUHD;
/**
* Time lapse quality level corresponding to the lowest available resolution.
@@ -188,10 +193,14 @@
*/
public static final int QUALITY_TIME_LAPSE_2K = 1012;
+ /**
+ * Time lapse quality level corresponding to the 8K UHD (7680 x 4320) resolution.
+ */
+ public static final int QUALITY_TIME_LAPSE_8KUHD = 1013;
// Start and end of timelapse quality list
private static final int QUALITY_TIME_LAPSE_LIST_START = QUALITY_TIME_LAPSE_LOW;
- private static final int QUALITY_TIME_LAPSE_LIST_END = QUALITY_TIME_LAPSE_2K;
+ private static final int QUALITY_TIME_LAPSE_LIST_END = QUALITY_TIME_LAPSE_8KUHD;
/**
* High speed ( >= 100fps) quality level corresponding to the lowest available resolution.
diff --git a/media/java/android/media/HwAudioSource.java b/media/java/android/media/HwAudioSource.java
index bbf632a..e339ae8 100644
--- a/media/java/android/media/HwAudioSource.java
+++ b/media/java/android/media/HwAudioSource.java
@@ -54,7 +54,7 @@
Preconditions.checkArgument(device.isSource(), "Requires a source device");
mAudioDeviceInfo = device;
mAudioAttributes = attributes;
- baseRegisterPlayer();
+ baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE);
}
/**
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index dd42aab..71ee57e 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -73,6 +73,8 @@
oneway void releaseRecorder(in int riid);
+ oneway void playerSessionId(in int piid, in int sessionId);
+
// Java-only methods below.
oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index dd0bc61..ca0d29f 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -663,6 +663,10 @@
* result in an exception.</p>
*/
public MediaPlayer() {
+ this(AudioSystem.AUDIO_SESSION_ALLOCATE);
+ }
+
+ private MediaPlayer(int sessionId) {
super(new AudioAttributes.Builder().build(),
AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER);
@@ -684,7 +688,7 @@
native_setup(new WeakReference<MediaPlayer>(this),
getCurrentOpPackageName());
- baseRegisterPlayer();
+ baseRegisterPlayer(sessionId);
}
/*
@@ -913,7 +917,7 @@
AudioAttributes audioAttributes, int audioSessionId) {
try {
- MediaPlayer mp = new MediaPlayer();
+ MediaPlayer mp = new MediaPlayer(audioSessionId);
final AudioAttributes aa = audioAttributes != null ? audioAttributes :
new AudioAttributes.Builder().build();
mp.setAudioAttributes(aa);
@@ -978,7 +982,7 @@
AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);
if (afd == null) return null;
- MediaPlayer mp = new MediaPlayer();
+ MediaPlayer mp = new MediaPlayer(audioSessionId);
final AudioAttributes aa = audioAttributes != null ? audioAttributes :
new AudioAttributes.Builder().build();
@@ -2365,7 +2369,13 @@
* This method must be called before one of the overloaded <code> setDataSource </code> methods.
* @throws IllegalStateException if it is called in an invalid state
*/
- public native void setAudioSessionId(int sessionId) throws IllegalArgumentException, IllegalStateException;
+ public void setAudioSessionId(int sessionId)
+ throws IllegalArgumentException, IllegalStateException {
+ native_setAudioSessionId(sessionId);
+ baseUpdateSessionId(sessionId);
+ }
+
+ private native void native_setAudioSessionId(int sessionId);
/**
* Returns the audio session ID.
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index 58ae279..4407efa 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -97,6 +97,7 @@
* Constructor. Must be given audio attributes, as they are required for AppOps.
* @param attr non-null audio attributes
* @param class non-null class of the implementation of this abstract class
+ * @param sessionId the audio session Id
*/
PlayerBase(@NonNull AudioAttributes attr, int implType) {
if (attr == null) {
@@ -110,7 +111,7 @@
/**
* Call from derived class when instantiation / initialization is successful
*/
- protected void baseRegisterPlayer() {
+ protected void baseRegisterPlayer(int sessionId) {
if (!USE_AUDIOFLINGER_MUTING_FOR_OP) {
IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
mAppOps = IAppOpsService.Stub.asInterface(b);
@@ -128,7 +129,8 @@
}
try {
mPlayerIId = getService().trackPlayer(
- new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)));
+ new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this),
+ sessionId));
} catch (RemoteException e) {
Log.e(TAG, "Error talking to audio service, player will not be tracked", e);
}
@@ -145,7 +147,7 @@
try {
getService().playerAttributes(mPlayerIId, attr);
} catch (RemoteException e) {
- Log.e(TAG, "Error talking to audio service, STARTED state will not be tracked", e);
+ Log.e(TAG, "Error talking to audio service, audio attributes will not be updated", e);
}
synchronized (mLock) {
boolean attributesChanged = (mAttributes != attr);
@@ -154,6 +156,18 @@
}
}
+ /**
+ * To be called whenever the session ID of the player changes
+ * @param sessionId, the new session Id
+ */
+ void baseUpdateSessionId(int sessionId) {
+ try {
+ getService().playerSessionId(mPlayerIId, sessionId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to audio service, the session ID will not be updated", e);
+ }
+ }
+
void baseUpdateDeviceId(@Nullable AudioDeviceInfo deviceInfo) {
int deviceId = 0;
if (deviceInfo != null) {
@@ -566,16 +580,19 @@
public static final int AUDIO_ATTRIBUTES_DEFINED = 1;
public final AudioAttributes mAttributes;
public final IPlayer mIPlayer;
+ public final int mSessionId;
- PlayerIdCard(int type, @NonNull AudioAttributes attr, @NonNull IPlayer iplayer) {
+ PlayerIdCard(int type, @NonNull AudioAttributes attr, @NonNull IPlayer iplayer,
+ int sessionId) {
mPlayerType = type;
mAttributes = attr;
mIPlayer = iplayer;
+ mSessionId = sessionId;
}
@Override
public int hashCode() {
- return Objects.hash(mPlayerType);
+ return Objects.hash(mPlayerType, mSessionId);
}
@Override
@@ -588,6 +605,7 @@
dest.writeInt(mPlayerType);
mAttributes.writeToParcel(dest, 0);
dest.writeStrongBinder(mIPlayer == null ? null : mIPlayer.asBinder());
+ dest.writeInt(mSessionId);
}
public static final @android.annotation.NonNull Parcelable.Creator<PlayerIdCard> CREATOR
@@ -611,6 +629,7 @@
// IPlayer can be null if unmarshalling a Parcel coming from who knows where
final IBinder b = in.readStrongBinder();
mIPlayer = (b == null ? null : IPlayer.Stub.asInterface(b));
+ mSessionId = in.readInt();
}
@Override
@@ -621,7 +640,8 @@
PlayerIdCard that = (PlayerIdCard) o;
// FIXME change to the binder player interface once supported as a member
- return ((mPlayerType == that.mPlayerType) && mAttributes.equals(that.mAttributes));
+ return ((mPlayerType == that.mPlayerType) && mAttributes.equals(that.mAttributes)
+ && (mSessionId == that.mSessionId));
}
}
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 797caf3..32413dc 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -155,7 +155,8 @@
}
mAttributes = attributes;
- baseRegisterPlayer();
+ // FIXME: b/174876164 implement session id for soundpool
+ baseRegisterPlayer(AudioSystem.AUDIO_SESSION_ALLOCATE);
}
/**
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 31fb8d0..9bf126b 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -35,7 +35,7 @@
ISessionController getController();
void setFlags(int flags);
void setActive(boolean active);
- void setMediaButtonReceiver(in PendingIntent mbr);
+ void setMediaButtonReceiver(in PendingIntent mbr, String sessionPackageName);
void setMediaButtonBroadcastReceiver(in ComponentName broadcastReceiver);
void setLaunchPendingIntent(in PendingIntent pi);
void destroySession();
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 24118b0..20fa53d 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -286,7 +286,7 @@
@Deprecated
public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
try {
- mBinder.setMediaButtonReceiver(mbr);
+ mBinder.setMediaButtonReceiver(mbr, mContext.getPackageName());
} catch (RemoteException e) {
Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 8525e99..ee0be01 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -65,6 +65,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -245,6 +246,8 @@
private static final int MSG_ON_FILTER_STATUS = 3;
private static final int MSG_ON_LNB_EVENT = 4;
+ private static final int FILTER_CLEANUP_THRESHOLD = 256;
+
/** @hide */
@IntDef(prefix = "DVR_TYPE_", value = {DVR_TYPE_RECORD, DVR_TYPE_PLAYBACK})
@Retention(RetentionPolicy.SOURCE)
@@ -1208,6 +1211,15 @@
synchronized (mFilters) {
WeakReference<Filter> weakFilter = new WeakReference<Filter>(filter);
mFilters.add(weakFilter);
+ if (mFilters.size() > FILTER_CLEANUP_THRESHOLD) {
+ Iterator<WeakReference<Filter>> iterator = mFilters.iterator();
+ while (iterator.hasNext()) {
+ WeakReference<Filter> wFilter = iterator.next();
+ if (wFilter.get() == null) {
+ iterator.remove();
+ }
+ }
+ }
}
}
return filter;
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 67a2c49..65b64d7 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -31,8 +31,8 @@
],
shared_libs: [
- "audioclient-types-aidl-unstable-cpp",
- "av-types-aidl-unstable-cpp",
+ "audioclient-types-aidl-cpp",
+ "av-types-aidl-cpp",
"libandroid_runtime",
"libaudioclient",
"libnativehelper",
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index bd8d2e9..98ac5b9 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1409,7 +1409,7 @@
{"native_setup", "(Ljava/lang/Object;Ljava/lang/String;)V",(void *)android_media_MediaPlayer_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize},
{"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id},
- {"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id},
+ {"native_setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id},
{"_setAuxEffectSendLevel", "(F)V", (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
{"attachAuxEffect", "(I)V", (void *)android_media_MediaPlayer_attachAuxEffect},
{"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData},
diff --git a/media/jni/tuner/DemuxClient.h b/media/jni/tuner/DemuxClient.h
index 31eb35a..c38a8fa 100644
--- a/media/jni/tuner/DemuxClient.h
+++ b/media/jni/tuner/DemuxClient.h
@@ -100,6 +100,11 @@
*/
Result close();
+ /**
+ * Get the Aidl demux to set as source.
+ */
+ shared_ptr<ITunerDemux> getAidlDemux() { return mTunerDemux; }
+
void setId(int id) { mId = id; }
int getId() { return mId; }
diff --git a/media/jni/tuner/DescramblerClient.cpp b/media/jni/tuner/DescramblerClient.cpp
index 979beea..c9bacda 100644
--- a/media/jni/tuner/DescramblerClient.cpp
+++ b/media/jni/tuner/DescramblerClient.cpp
@@ -27,13 +27,12 @@
/////////////// DescramblerClient ///////////////////////
-// TODO: pending aidl interface
-DescramblerClient::DescramblerClient() {
- //mTunerDescrambler = tunerDescrambler;
+DescramblerClient::DescramblerClient(shared_ptr<ITunerDescrambler> tunerDescrambler) {
+ mTunerDescrambler = tunerDescrambler;
}
DescramblerClient::~DescramblerClient() {
- //mTunerDescrambler = NULL;
+ mTunerDescrambler = NULL;
mDescrambler = NULL;
}
@@ -47,7 +46,10 @@
return Result::INVALID_ARGUMENT;
}
- // TODO: pending aidl interface
+ if (mTunerDescrambler != NULL) {
+ Status s = mTunerDescrambler->setDemuxSource(demuxClient->getAidlDemux());
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mDescrambler != NULL) {
return mDescrambler->setDemuxSource(demuxClient->getId());
@@ -57,7 +59,10 @@
}
Result DescramblerClient::setKeyToken(vector<uint8_t> keyToken) {
- // TODO: pending aidl interface
+ if (mTunerDescrambler != NULL) {
+ Status s = mTunerDescrambler->setKeyToken(keyToken);
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mDescrambler != NULL) {
return mDescrambler->setKeyToken(keyToken);
@@ -67,7 +72,11 @@
}
Result DescramblerClient::addPid(DemuxPid pid, sp<FilterClient> optionalSourceFilter) {
- // TODO: pending aidl interface
+ if (mTunerDescrambler != NULL) {
+ Status s = mTunerDescrambler->addPid(
+ getAidlDemuxPid(pid), optionalSourceFilter->getAidlFilter());
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mDescrambler != NULL) {
return mDescrambler->addPid(pid, optionalSourceFilter->getHalFilter());
@@ -76,16 +85,24 @@
return Result::INVALID_STATE;}
Result DescramblerClient::removePid(DemuxPid pid, sp<FilterClient> optionalSourceFilter) {
- // TODO: pending aidl interface
-
- if (mDescrambler != NULL) {
- return mDescrambler->addPid(pid, optionalSourceFilter->getHalFilter());
+ if (mTunerDescrambler != NULL) {
+ Status s = mTunerDescrambler->removePid(
+ getAidlDemuxPid(pid), optionalSourceFilter->getAidlFilter());
+ return ClientHelper::getServiceSpecificErrorCode(s);
}
- return Result::INVALID_STATE;}
+ if (mDescrambler != NULL) {
+ return mDescrambler->removePid(pid, optionalSourceFilter->getHalFilter());
+ }
+
+ return Result::INVALID_STATE;
+}
Result DescramblerClient::close() {
- // TODO: pending aidl interface
+ if (mTunerDescrambler != NULL) {
+ Status s = mTunerDescrambler->close();
+ return ClientHelper::getServiceSpecificErrorCode(s);
+ }
if (mDescrambler != NULL) {
return mDescrambler->close();
@@ -95,4 +112,16 @@
/////////////// DescramblerClient Helper Methods ///////////////////////
+TunerDemuxPid DescramblerClient::getAidlDemuxPid(DemuxPid& pid) {
+ TunerDemuxPid aidlPid;
+ switch (pid.getDiscriminator()) {
+ case DemuxPid::hidl_discriminator::tPid:
+ aidlPid.set<TunerDemuxPid::tPid>((int)pid.tPid());
+ break;
+ case DemuxPid::hidl_discriminator::mmtpPid:
+ aidlPid.set<TunerDemuxPid::mmtpPid>((int)pid.mmtpPid());
+ break;
+ }
+ return aidlPid;
+}
} // namespace android
diff --git a/media/jni/tuner/DescramblerClient.h b/media/jni/tuner/DescramblerClient.h
index 8af6883..a8fa1e2 100644
--- a/media/jni/tuner/DescramblerClient.h
+++ b/media/jni/tuner/DescramblerClient.h
@@ -17,14 +17,15 @@
#ifndef _ANDROID_MEDIA_TV_DESCRAMBLER_CLIENT_H_
#define _ANDROID_MEDIA_TV_DESCRAMBLER_CLIENT_H_
-//#include <aidl/android/media/tv/tuner/ITunerDescrambler.h>
+#include <aidl/android/media/tv/tuner/ITunerDescrambler.h>
#include <android/hardware/tv/tuner/1.0/IDescrambler.h>
#include <android/hardware/tv/tuner/1.1/types.h>
#include "DemuxClient.h"
#include "FilterClient.h"
-//using ::aidl::android::media::tv::tuner::ITunerDescrambler;
+using ::aidl::android::media::tv::tuner::ITunerDescrambler;
+using ::aidl::android::media::tv::tuner::TunerDemuxPid;
using ::android::hardware::tv::tuner::V1_0::IDescrambler;
using ::android::hardware::tv::tuner::V1_0::Result;
@@ -37,8 +38,7 @@
struct DescramblerClient : public RefBase {
public:
- // TODO: pending hidl interface
- DescramblerClient();
+ DescramblerClient(shared_ptr<ITunerDescrambler> tunerDescrambler);
~DescramblerClient();
// TODO: remove after migration to Tuner Service is done.
@@ -70,12 +70,13 @@
Result close();
private:
+ TunerDemuxPid getAidlDemuxPid(DemuxPid& pid);
+
/**
* An AIDL Tuner Descrambler Singleton assigned at the first time the Tuner Client
* opens a descrambler. Default null when descrambler is not opened.
*/
- // TODO: pending on aidl interface
- //shared_ptr<ITunerDescrambler> mTunerDescrambler;
+ shared_ptr<ITunerDescrambler> mTunerDescrambler;
/**
* A Descrambler HAL interface that is ready before migrating to the TunerDescrambler.
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index 2aaf96c..8b4ca37 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -35,7 +35,7 @@
using ::aidl::android::media::tv::tuner::TunerFilterSharedHandleInfo;
using ::aidl::android::media::tv::tuner::TunerFilterTlvConfiguration;
using ::aidl::android::media::tv::tuner::TunerFilterTsConfiguration;
-
+using ::android::hardware::hidl_vec;
using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
@@ -703,10 +703,10 @@
void TunerFilterCallback::getHidlMediaEvent(
const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+ event.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
hidl_handle handle = hidl_handle(makeFromAidl(filterEvents[i]
.get<TunerFilterEvent::media>().avMemory));
- event.events.resize(i + 1);
event.events[i].media({
.avMemory = handle,
.streamId = static_cast<DemuxStreamId>(filterEvents[i]
@@ -752,9 +752,9 @@
void TunerFilterCallback::getHidlSectionEvent(
const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+ event.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto section = filterEvents[i].get<TunerFilterEvent::section>();
- event.events.resize(i + 1);
event.events[i].section({
.tableId = static_cast<uint16_t>(section.tableId),
.version = static_cast<uint16_t>(section.version),
@@ -766,9 +766,9 @@
void TunerFilterCallback::getHidlPesEvent(
const vector<TunerFilterEvent>& filterEvents, DemuxFilterEvent& event) {
+ event.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto pes = filterEvents[i].get<TunerFilterEvent::pes>();
- event.events.resize(i + 1);
event.events[i].pes({
.streamId = static_cast<DemuxStreamId>(pes.streamId),
.dataLength = static_cast<uint16_t>(pes.dataLength),
@@ -779,9 +779,10 @@
void TunerFilterCallback::getHidlTsRecordEvent(const vector<TunerFilterEvent>& filterEvents,
DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
+ event.events.resize(filterEvents.size());
+ eventExt.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto ts = filterEvents[i].get<TunerFilterEvent::tsRecord>();
- event.events.resize(i + 1);
event.events[i].tsRecord({
.tsIndexMask = static_cast<uint32_t>(ts.tsIndexMask),
.byteNumber = static_cast<uint64_t>(ts.byteNumber),
@@ -803,7 +804,6 @@
break;
}
- eventExt.events.resize(i + 1);
if (ts.isExtended) {
eventExt.events[i].tsRecord({
.pts = static_cast<uint64_t>(ts.pts),
@@ -817,15 +817,15 @@
void TunerFilterCallback::getHidlMmtpRecordEvent(const vector<TunerFilterEvent>& filterEvents,
DemuxFilterEvent& event, DemuxFilterEventExt& eventExt) {
+ event.events.resize(filterEvents.size());
+ eventExt.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto mmtp = filterEvents[i].get<TunerFilterEvent::mmtpRecord>();
- event.events.resize(i + 1);
event.events[i].mmtpRecord({
.scHevcIndexMask = static_cast<uint32_t>(mmtp.scHevcIndexMask),
.byteNumber = static_cast<uint64_t>(mmtp.byteNumber),
});
- eventExt.events.resize(i + 1);
if (mmtp.isExtended) {
eventExt.events[i].mmtpRecord({
.pts = static_cast<uint64_t>(mmtp.pts),
@@ -841,9 +841,9 @@
void TunerFilterCallback::getHidlDownloadEvent(const vector<TunerFilterEvent>& filterEvents,
DemuxFilterEvent& event) {
+ event.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto download = filterEvents[i].get<TunerFilterEvent::download>();
- event.events.resize(i + 1);
event.events[i].download({
.itemId = static_cast<uint32_t>(download.itemId),
.mpuSequenceNumber = static_cast<uint32_t>(download.mpuSequenceNumber),
@@ -856,9 +856,9 @@
void TunerFilterCallback::getHidlIpPayloadEvent(const vector<TunerFilterEvent>& filterEvents,
DemuxFilterEvent& event) {
+ event.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto ip = filterEvents[i].get<TunerFilterEvent::ipPayload>();
- event.events.resize(i + 1);
event.events[i].ipPayload({
.dataLength = static_cast<uint16_t>(ip.dataLength),
});
@@ -867,15 +867,15 @@
void TunerFilterCallback::getHidlTemiEvent(const vector<TunerFilterEvent>& filterEvents,
DemuxFilterEvent& event) {
+ event.events.resize(filterEvents.size());
for (int i = 0; i < filterEvents.size(); i++) {
auto temi = filterEvents[i].get<TunerFilterEvent::temi>();
- event.events.resize(i + 1);
event.events[i].temi({
.pts = static_cast<uint64_t>(temi.pts),
.descrTag = static_cast<uint8_t>(temi.descrTag),
});
- vector<uint8_t> descrData(temi.descrData.size());
- copy(temi.descrData.begin(), temi.descrData.end(), descrData.begin());
+ hidl_vec<uint8_t> descrData(temi.descrData.begin(), temi.descrData.end());
+ event.events[i].temi().descrData = descrData;
}
}
diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp
index 08573a6..3a00133 100644
--- a/media/jni/tuner/FrontendClient.cpp
+++ b/media/jni/tuner/FrontendClient.cpp
@@ -27,23 +27,43 @@
using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard;
using ::android::hardware::tv::tuner::V1_0::FrontendAnalogType;
using ::android::hardware::tv::tuner::V1_0::FrontendAtscModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Bandwidth;
using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3Modulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendAtsc3TimeInterleaveMode;
using ::android::hardware::tv::tuner::V1_0::FrontendDvbcAnnex;
using ::android::hardware::tv::tuner::V1_0::FrontendDvbcModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbcSpectralInversion;
using ::android::hardware::tv::tuner::V1_0::FrontendDvbsModulation;
using ::android::hardware::tv::tuner::V1_0::FrontendDvbsStandard;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbsRolloff;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth;
+using ::android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval;
using ::android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy;
using ::android::hardware::tv::tuner::V1_0::FrontendDvbtStandard;
+using ::android::hardware::tv::tuner::V1_0::FrontendInnerFec;
using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsModulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbsRolloff;
using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Modulation;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbs3Rolloff;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtBandwidth;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtGuardInterval;
+using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtMode;
using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtModulation;
using ::android::hardware::tv::tuner::V1_0::FrontendScanAtsc3PlpInfo;
-using ::android::hardware::tv::tuner::V1_0::FrontendType;
+using ::android::hardware::tv::tuner::V1_0::LnbVoltage;
using ::android::hardware::tv::tuner::V1_1::Constant;
+using ::android::hardware::tv::tuner::V1_1::FrontendCableTimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbBandwidth;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbGuardInterval;
using ::android::hardware::tv::tuner::V1_1::FrontendDtmbModulation;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTransmissionMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbcBandwidth;
using ::android::hardware::tv::tuner::V1_1::FrontendDvbtConstellation;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbtTransmissionMode;
using ::android::hardware::tv::tuner::V1_1::FrontendModulation;
using ::android::hardware::tv::tuner::V1_1::FrontendSpectralInversion;
+using ::android::hardware::tv::tuner::V1_1::FrontendType;
namespace android {
@@ -71,8 +91,8 @@
if (mTunerFrontend != NULL) {
mAidlCallback = ::ndk::SharedRefBase::make<TunerFrontendCallback>(frontendClientCallback);
mAidlCallback->setFrontendType(mType);
- mTunerFrontend->setCallback(mAidlCallback);
- return Result::SUCCESS;
+ Status s = mTunerFrontend->setCallback(mAidlCallback);
+ return ClientHelper::getServiceSpecificErrorCode(s);
}
mHidlCallback = new HidlFrontendCallback(frontendClientCallback);
@@ -160,9 +180,16 @@
vector<FrontendStatus> status;
if (mTunerFrontend != NULL) {
- // TODO: handle error message.
- /*status = mTunerFrontend->getStatus(statusTypes);
- return status;*/
+ vector<TunerFrontendStatus> aidlStatus;
+ vector<int> types;
+ for (auto t : statusTypes) {
+ types.push_back((int)t);
+ }
+ Status s = mTunerFrontend->getStatus(types, &aidlStatus);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return status;
+ }
+ return getHidlStatus(aidlStatus);
}
if (mFrontend != NULL && statusTypes.size() > 0) {
@@ -180,14 +207,22 @@
return status;
}
+
vector<FrontendStatusExt1_1> FrontendClient::getStatusExtended_1_1(
vector<FrontendStatusTypeExt1_1> statusTypes) {
vector<FrontendStatusExt1_1> status;
if (mTunerFrontend != NULL) {
- // TODO: handle error message.
- /*status = mTunerFrontend->getStatusExtended_1_1(statusTypes);
- return status;*/
+ vector<TunerFrontendStatus> aidlStatus;
+ vector<int> types;
+ for (auto t : statusTypes) {
+ types.push_back((int)t);
+ }
+ Status s = mTunerFrontend->getStatusExtended_1_1(types, &aidlStatus);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return status;
+ }
+ return getHidlStatusExt(aidlStatus);
}
if (mFrontend_1_1 != NULL && statusTypes.size() > 0) {
@@ -208,9 +243,8 @@
Result FrontendClient::setLnb(sp<LnbClient> lnbClient) {
if (mTunerFrontend != NULL) {
- // TODO: handle error message.
- /*mTunerFrontend->setLnb(lnbClient->getAidlLnb());
- return Result::SUCCESS;*/
+ Status s = mTunerFrontend->setLnb(lnbClient->getAidlLnb());
+ return ClientHelper::getServiceSpecificErrorCode(s);
}
if (mFrontend != NULL) {
@@ -223,9 +257,8 @@
Result FrontendClient::setLna(bool bEnable) {
if (mTunerFrontend != NULL) {
- // TODO: handle error message.
- /*mTunerFrontend->setLna(bEnable);
- return Result::SUCCESS;*/
+ Status s = mTunerFrontend->setLna(bEnable);
+ return ClientHelper::getServiceSpecificErrorCode(s);
}
if (mFrontend != NULL) {
@@ -240,9 +273,11 @@
int ltsId = (int)Constant::INVALID_LTS_ID;
if (mTunerFrontend != NULL) {
- // TODO: handle error message.
- /*mTunerFrontend->linkCiCamToFrontend(ciCamId, ltsId);
- return ltsId;*/
+ Status s = mTunerFrontend->linkCiCamToFrontend(ciCamId, <sId);
+ if (ClientHelper::getServiceSpecificErrorCode(s) == Result::SUCCESS) {
+ return ltsId;
+ }
+ return (int)Constant::INVALID_LTS_ID;
}
if (mFrontend_1_1 != NULL) {
@@ -262,9 +297,8 @@
Result FrontendClient::unlinkCiCamToFrontend(int ciCamId) {
if (mTunerFrontend != NULL) {
- // TODO: handle error message.
- /*mTunerFrontend->unlinkCiCamToFrontend(ciCamId);
- return Result::SUCCESS;*/
+ Status s = mTunerFrontend->unlinkCiCamToFrontend(ciCamId);
+ return ClientHelper::getServiceSpecificErrorCode(s);
}
if (mFrontend_1_1 != NULL) {
@@ -302,6 +336,400 @@
return mId;
}
+vector<FrontendStatus> FrontendClient::getHidlStatus(vector<TunerFrontendStatus>& aidlStatus) {
+ vector<FrontendStatus> hidlStatus;
+ for (TunerFrontendStatus s : aidlStatus) {
+ FrontendStatus status;
+ switch (s.getTag()) {
+ case TunerFrontendStatus::isDemodLocked: {
+ status.isDemodLocked(s.get<TunerFrontendStatus::isDemodLocked>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::snr: {
+ status.snr(s.get<TunerFrontendStatus::snr>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::ber: {
+ status.ber((uint32_t)s.get<TunerFrontendStatus::ber>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::per: {
+ status.per((uint32_t)s.get<TunerFrontendStatus::per>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::preBer: {
+ status.preBer((uint32_t)s.get<TunerFrontendStatus::preBer>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::signalQuality: {
+ status.signalQuality((uint32_t)s.get<TunerFrontendStatus::signalQuality>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::signalStrength: {
+ status.signalStrength(s.get<TunerFrontendStatus::signalStrength>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::symbolRate: {
+ status.symbolRate((uint32_t)s.get<TunerFrontendStatus::symbolRate>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::innerFec: {
+ status.innerFec(static_cast<FrontendInnerFec>(
+ s.get<TunerFrontendStatus::innerFec>()));
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::modulation: {
+ auto aidlMod = s.get<TunerFrontendStatus::modulation>();
+ switch (mType) {
+ case (int)FrontendType::DVBC:
+ status.modulation().dvbc(static_cast<FrontendDvbcModulation>(aidlMod));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::DVBS:
+ status.modulation().dvbs(static_cast<FrontendDvbsModulation>(aidlMod));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBS:
+ status.modulation().isdbs(static_cast<FrontendIsdbsModulation>(aidlMod));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBS3:
+ status.modulation().isdbs3(static_cast<FrontendIsdbs3Modulation>(aidlMod));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBT:
+ status.modulation().isdbt(static_cast<FrontendIsdbtModulation>(aidlMod));
+ hidlStatus.push_back(status);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case TunerFrontendStatus::inversion: {
+ status.inversion(static_cast<FrontendDvbcSpectralInversion>(
+ s.get<TunerFrontendStatus::inversion>()));
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::lnbVoltage: {
+ status.lnbVoltage(static_cast<LnbVoltage>(
+ s.get<TunerFrontendStatus::lnbVoltage>()));
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::plpId: {
+ status.plpId((uint8_t)s.get<TunerFrontendStatus::plpId>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isEWBS: {
+ status.isEWBS(s.get<TunerFrontendStatus::isEWBS>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::agc: {
+ status.agc((uint8_t)s.get<TunerFrontendStatus::agc>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isLnaOn: {
+ status.isLnaOn(s.get<TunerFrontendStatus::isLnaOn>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isLayerError: {
+ auto aidlE = s.get<TunerFrontendStatus::isLayerError>();
+ hidl_vec<bool> e(aidlE.begin(), aidlE.end());
+ status.isLayerError(e);
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::mer: {
+ status.mer(s.get<TunerFrontendStatus::mer>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::freqOffset: {
+ status.freqOffset(s.get<TunerFrontendStatus::freqOffset>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::hierarchy: {
+ status.hierarchy(static_cast<FrontendDvbtHierarchy>(
+ s.get<TunerFrontendStatus::freqOffset>()));
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isRfLocked: {
+ status.isRfLocked(s.get<TunerFrontendStatus::isRfLocked>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::plpInfo: {
+ int size = s.get<TunerFrontendStatus::plpInfo>().size();
+ status.plpInfo().resize(size);
+ for (int i = 0; i < size; i++) {
+ auto aidlInfo = s.get<TunerFrontendStatus::plpInfo>()[i];
+ status.plpInfo()[i] = {
+ .plpId = (uint8_t)aidlInfo.plpId,
+ .isLocked = aidlInfo.isLocked,
+ .uec = (uint32_t)aidlInfo.uec,
+ };
+ }
+ hidlStatus.push_back(status);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return hidlStatus;
+}
+
+vector<FrontendStatusExt1_1> FrontendClient::getHidlStatusExt(
+ vector<TunerFrontendStatus>& aidlStatus) {
+ vector<FrontendStatusExt1_1> hidlStatus;
+ for (TunerFrontendStatus s : aidlStatus) {
+ FrontendStatusExt1_1 status;
+ switch (s.getTag()) {
+ case TunerFrontendStatus::modulations: {
+ for (auto aidlMod : s.get<TunerFrontendStatus::modulations>()) {
+ int size = status.modulations().size();
+ status.modulations().resize(size + 1);
+ switch (mType) {
+ case (int)FrontendType::DVBC:
+ status.modulations()[size].dvbc(
+ static_cast<FrontendDvbcModulation>(aidlMod));
+ break;
+ case (int)FrontendType::DVBS:
+ status.modulations()[size].dvbs(
+ static_cast<FrontendDvbsModulation>(aidlMod));
+ break;
+ case (int)FrontendType::DVBT:
+ status.modulations()[size].dvbt(
+ static_cast<FrontendDvbtConstellation>(aidlMod));
+ break;
+ case (int)FrontendType::ISDBS:
+ status.modulations()[size].isdbs(
+ static_cast<FrontendIsdbsModulation>(aidlMod));
+ break;
+ case (int)FrontendType::ISDBS3:
+ status.modulations()[size].isdbs3(
+ static_cast<FrontendIsdbs3Modulation>(aidlMod));
+ break;
+ case (int)FrontendType::ISDBT:
+ status.modulations()[size].isdbt(
+ static_cast<FrontendIsdbtModulation>(aidlMod));
+ break;
+ case (int)FrontendType::ATSC:
+ status.modulations()[size].atsc(
+ static_cast<FrontendAtscModulation>(aidlMod));
+ break;
+ case (int)FrontendType::ATSC3:
+ status.modulations()[size].atsc3(
+ static_cast<FrontendAtsc3Modulation>(aidlMod));
+ break;
+ case (int)FrontendType::DTMB:
+ status.modulations()[size].dtmb(
+ static_cast<FrontendDtmbModulation>(aidlMod));
+ break;
+ default:
+ status.modulations().resize(size);
+ break;
+ }
+ }
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::bers: {
+ auto aidlB = s.get<TunerFrontendStatus::bers>();
+ hidl_vec<uint32_t> b(aidlB.begin(), aidlB.end());
+ status.bers(b);
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::codeRates: {
+ int size = s.get<TunerFrontendStatus::codeRates>().size();
+ status.codeRates().resize(size);
+ for (int i = 0; i < size; i++) {
+ auto aidlCodeRate = s.get<TunerFrontendStatus::codeRates>()[i];
+ status.codeRates()[i] =
+ static_cast<hardware::tv::tuner::V1_1::FrontendInnerFec>(aidlCodeRate);
+ }
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::bandwidth: {
+ auto aidlBand = s.get<TunerFrontendStatus::bandwidth>();
+ switch (mType) {
+ case (int)FrontendType::ATSC3:
+ status.bandwidth().atsc3(static_cast<FrontendAtsc3Bandwidth>(aidlBand));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::DVBC:
+ status.bandwidth().dvbc(static_cast<FrontendDvbcBandwidth>(aidlBand));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::DVBT:
+ status.bandwidth().dvbt(static_cast<FrontendDvbtBandwidth>(aidlBand));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBT:
+ status.bandwidth().isdbt(static_cast<FrontendIsdbtBandwidth>(aidlBand));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::DTMB:
+ status.bandwidth().dtmb(static_cast<FrontendDtmbBandwidth>(aidlBand));
+ hidlStatus.push_back(status);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case TunerFrontendStatus::interval: {
+ auto aidlInter = s.get<TunerFrontendStatus::interval>();
+ switch (mType) {
+ case (int)FrontendType::DVBT:
+ status.interval().dvbt(static_cast<FrontendDvbtGuardInterval>(aidlInter));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBT:
+ status.interval().isdbt(static_cast<FrontendIsdbtGuardInterval>(aidlInter));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::DTMB:
+ status.interval().dtmb(static_cast<FrontendDtmbGuardInterval>(aidlInter));
+ hidlStatus.push_back(status);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case TunerFrontendStatus::transmissionMode: {
+ auto aidlTran = s.get<TunerFrontendStatus::transmissionMode>();
+ switch (mType) {
+ case (int)FrontendType::DVBT:
+ status.transmissionMode().dvbt(
+ static_cast<FrontendDvbtTransmissionMode>(aidlTran));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBT:
+ status.transmissionMode().isdbt(static_cast<FrontendIsdbtMode>(aidlTran));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::DTMB:
+ status.transmissionMode().dtmb(
+ static_cast<FrontendDtmbTransmissionMode>(aidlTran));
+ hidlStatus.push_back(status);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case TunerFrontendStatus::uec: {
+ status.uec((uint32_t)s.get<TunerFrontendStatus::uec>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::systemId: {
+ status.systemId((uint16_t)s.get<TunerFrontendStatus::systemId>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::interleaving: {
+ for (auto aidlInter : s.get<TunerFrontendStatus::interleaving>()) {
+ int size = status.interleaving().size();
+ status.interleaving().resize(size + 1);
+ switch (mType) {
+ case (int)FrontendType::DVBC:
+ status.interleaving()[size].dvbc(
+ static_cast<FrontendCableTimeInterleaveMode>(aidlInter));
+ break;
+ case (int)FrontendType::ATSC3:
+ status.interleaving()[size].atsc3(
+ static_cast<FrontendAtsc3TimeInterleaveMode>(aidlInter));
+ break;
+ case (int)FrontendType::DTMB:
+ status.interleaving()[size].dtmb(
+ static_cast<FrontendDtmbTimeInterleaveMode>(aidlInter));
+ break;
+ default:
+ status.interleaving().resize(size);
+ break;
+ }
+ }
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isdbtSegment: {
+ auto aidlSeg = s.get<TunerFrontendStatus::isdbtSegment>();
+ hidl_vec<uint8_t> s(aidlSeg.begin(), aidlSeg.end());
+ status.isdbtSegment(s);
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::tsDataRate: {
+ auto aidlTs = s.get<TunerFrontendStatus::tsDataRate>();
+ hidl_vec<uint32_t> ts(aidlTs.begin(), aidlTs.end());
+ status.tsDataRate(ts);
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::rollOff: {
+ auto aidlRoll = s.get<TunerFrontendStatus::rollOff>();
+ switch (mType) {
+ case (int)FrontendType::DVBS:
+ status.rollOff().dvbs(static_cast<FrontendDvbsRolloff>(aidlRoll));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBS:
+ status.rollOff().isdbs(static_cast<FrontendIsdbsRolloff>(aidlRoll));
+ hidlStatus.push_back(status);
+ break;
+ case (int)FrontendType::ISDBS3:
+ status.rollOff().isdbs3(static_cast<FrontendIsdbs3Rolloff>(aidlRoll));
+ hidlStatus.push_back(status);
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case TunerFrontendStatus::isMiso: {
+ status.isMiso(s.get<TunerFrontendStatus::isMiso>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isLinear: {
+ status.isLinear(s.get<TunerFrontendStatus::isLinear>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ case TunerFrontendStatus::isShortFrames: {
+ status.isShortFrames(s.get<TunerFrontendStatus::isShortFrames>());
+ hidlStatus.push_back(status);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ return hidlStatus;
+}
+
TunerFrontendSettings FrontendClient::getAidlFrontendSettings(const FrontendSettings& settings,
const FrontendSettingsExt1_1& settingsExt1_1) {
bool isExtended = validateExtendedSettings(settingsExt1_1);
@@ -686,14 +1114,15 @@
vector<TunerFrontendScanAtsc3PlpInfo> plp =
message.get<TunerFrontendScanMessage::atsc3PlpInfos>();
hidl_vec<FrontendScanAtsc3PlpInfo> plpInfo;
- for (TunerFrontendScanAtsc3PlpInfo info : plp) {
+ int size = plp.size();
+ plpInfo.resize(size);
+ for (int i = 0; i < size; i++) {
+ auto info = message.get<TunerFrontendScanMessage::atsc3PlpInfos>()[i];
FrontendScanAtsc3PlpInfo p{
.plpId = static_cast<uint8_t>(info.plpId),
.bLlsFlag = info.llsFlag,
};
- int size = plpInfo.size();
- plpInfo.resize(size + 1);
- plpInfo[size] = p;
+ plpInfo[i] = p;
}
scanMessage.atsc3PlpInfos(plpInfo);
break;
diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h
index b0107ff..298b397 100644
--- a/media/jni/tuner/FrontendClient.h
+++ b/media/jni/tuner/FrontendClient.h
@@ -43,6 +43,7 @@
using ::aidl::android::media::tv::tuner::TunerFrontendIsdbtSettings;
using ::aidl::android::media::tv::tuner::TunerFrontendScanMessage;
using ::aidl::android::media::tv::tuner::TunerFrontendSettings;
+using ::aidl::android::media::tv::tuner::TunerFrontendStatus;
using ::android::hardware::Return;
using ::android::hardware::Void;
@@ -182,6 +183,9 @@
int getId();
private:
+ vector<FrontendStatus> getHidlStatus(vector<TunerFrontendStatus>& aidlStatus);
+ vector<FrontendStatusExt1_1> getHidlStatusExt(vector<TunerFrontendStatus>& aidlStatus);
+
TunerFrontendSettings getAidlFrontendSettings(
const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1);
TunerFrontendAnalogSettings getAidlAnalogSettings(
diff --git a/media/jni/tuner/LnbClient.h b/media/jni/tuner/LnbClient.h
index e7869e8..465dc23 100644
--- a/media/jni/tuner/LnbClient.h
+++ b/media/jni/tuner/LnbClient.h
@@ -108,7 +108,7 @@
*/
Result close();
- //shared_ptr<ITunerLnb> getAidlLnb() { return mTunerLnb; }
+ shared_ptr<ITunerLnb> getAidlLnb() { return mTunerLnb; }
void setId(LnbId id) { mId = id; }
LnbId getId() { return mId; }
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index a604490d..7f954b5 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -22,7 +22,10 @@
#include "TunerClient.h"
+using ::aidl::android::media::tv::tuner::TunerFrontendCapabilities;
+using ::aidl::android::media::tv::tuner::TunerFrontendDtmbCapabilities;
using ::android::hardware::tv::tuner::V1_0::FrontendId;
+using ::android::hardware::tv::tuner::V1_0::FrontendStatusType;
using ::android::hardware::tv::tuner::V1_0::FrontendType;
namespace android {
@@ -136,12 +139,11 @@
shared_ptr<FrontendInfo> TunerClient::getFrontendInfo(int id) {
if (mTunerService != NULL) {
TunerFrontendInfo aidlFrontendInfo;
- // TODO: handle error code
Status s = mTunerService->getFrontendInfo(id, &aidlFrontendInfo);
if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
return NULL;
}
- return make_shared<FrontendInfo>(FrontendInfoAidlToHidl(aidlFrontendInfo));
+ return make_shared<FrontendInfo>(frontendInfoAidlToHidl(aidlFrontendInfo));
}
if (mTuner != NULL) {
@@ -157,7 +159,22 @@
}
shared_ptr<FrontendDtmbCapabilities> TunerClient::getFrontendDtmbCapabilities(int id) {
- // pending aidl interface
+ if (mTunerService != NULL) {
+ TunerFrontendDtmbCapabilities dtmbCaps;
+ Status s = mTunerService->getFrontendDtmbCapabilities(id, &dtmbCaps);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return NULL;
+ }
+ FrontendDtmbCapabilities hidlCaps{
+ .transmissionModeCap = static_cast<uint32_t>(dtmbCaps.transmissionModeCap),
+ .bandwidthCap = static_cast<uint32_t>(dtmbCaps.bandwidthCap),
+ .modulationCap = static_cast<uint32_t>(dtmbCaps.modulationCap),
+ .codeRateCap = static_cast<uint32_t>(dtmbCaps.codeRateCap),
+ .guardIntervalCap = static_cast<uint32_t>(dtmbCaps.guardIntervalCap),
+ .interleaveModeCap = static_cast<uint32_t>(dtmbCaps.interleaveModeCap),
+ };
+ return make_shared<FrontendDtmbCapabilities>(hidlCaps);
+ }
if (mTuner_1_1 != NULL) {
Result result;
@@ -224,17 +241,18 @@
return NULL;
}
-sp<DescramblerClient> TunerClient::openDescrambler(int /*descramblerHandle*/) {
+sp<DescramblerClient> TunerClient::openDescrambler(int descramblerHandle) {
if (mTunerService != NULL) {
- // TODO: handle error code
- /*shared_ptr<ITunerDescrambler> tunerDescrambler;
- mTunerService->openDescrambler(demuxHandle, &tunerDescrambler);
- return new DescramblerClient(tunerDescrambler);*/
+ shared_ptr<ITunerDescrambler> tunerDescrambler;
+ Status s = mTunerService->openDescrambler(descramblerHandle, &tunerDescrambler);
+ if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+ return NULL;
+ }
+ return new DescramblerClient(tunerDescrambler);
}
if (mTuner != NULL) {
- // TODO: pending aidl interface
- sp<DescramblerClient> descramblerClient = new DescramblerClient();
+ sp<DescramblerClient> descramblerClient = new DescramblerClient(NULL);
sp<IDescrambler> hidlDescrambler = openHidlDescrambler();
if (hidlDescrambler != NULL) {
descramblerClient->setHidlDescrambler(hidlDescrambler);
@@ -486,7 +504,7 @@
return caps;
}
-FrontendInfo TunerClient::FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo) {
+FrontendInfo TunerClient::frontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo) {
FrontendInfo hidlFrontendInfo {
.type = static_cast<FrontendType>(aidlFrontendInfo.type),
.minFrequency = static_cast<uint32_t>(aidlFrontendInfo.minFrequency),
@@ -496,8 +514,102 @@
.acquireRange = static_cast<uint32_t>(aidlFrontendInfo.acquireRange),
.exclusiveGroupId = static_cast<uint32_t>(aidlFrontendInfo.exclusiveGroupId),
};
- // TODO: handle Frontend caps
+ int size = aidlFrontendInfo.statusCaps.size();
+ hidlFrontendInfo.statusCaps.resize(size);
+ for (int i = 0; i < size; i++) {
+ hidlFrontendInfo.statusCaps[i] =
+ static_cast<FrontendStatusType>(aidlFrontendInfo.statusCaps[i]);
+ }
+
+ switch (aidlFrontendInfo.caps.getTag()) {
+ case TunerFrontendCapabilities::analogCaps: {
+ auto analog = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::analogCaps>();
+ hidlFrontendInfo.frontendCaps.analogCaps({
+ .typeCap = static_cast<uint32_t>(analog.typeCap),
+ .sifStandardCap = static_cast<uint32_t>(analog.sifStandardCap),
+ });
+ break;
+ }
+ case TunerFrontendCapabilities::atscCaps: {
+ auto atsc = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::atscCaps>();
+ hidlFrontendInfo.frontendCaps.atscCaps({
+ .modulationCap = static_cast<uint32_t>(atsc.modulationCap),
+ });
+ break;
+ }
+ case TunerFrontendCapabilities::atsc3Caps: {
+ auto atsc3 = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::atsc3Caps>();
+ hidlFrontendInfo.frontendCaps.atsc3Caps({
+ .bandwidthCap = static_cast<uint32_t>(atsc3.bandwidthCap),
+ .modulationCap = static_cast<uint32_t>(atsc3.modulationCap),
+ .timeInterleaveModeCap = static_cast<uint32_t>(atsc3.timeInterleaveModeCap),
+ .codeRateCap = static_cast<uint32_t>(atsc3.codeRateCap),
+ .fecCap = static_cast<uint32_t>(atsc3.fecCap),
+ .demodOutputFormatCap = static_cast<uint8_t>(atsc3.demodOutputFormatCap),
+ });
+ break;
+ }
+ case TunerFrontendCapabilities::cableCaps: {
+ auto cable = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::cableCaps>();
+ hidlFrontendInfo.frontendCaps.dvbcCaps({
+ .modulationCap = static_cast<uint32_t>(cable.modulationCap),
+ .fecCap = static_cast<uint64_t>(cable.codeRateCap),
+ .annexCap = static_cast<uint8_t>(cable.annexCap),
+ });
+ break;
+ }
+ case TunerFrontendCapabilities::dvbsCaps: {
+ auto dvbs = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::dvbsCaps>();
+ hidlFrontendInfo.frontendCaps.dvbsCaps({
+ .modulationCap = static_cast<int32_t>(dvbs.modulationCap),
+ .innerfecCap = static_cast<uint64_t>(dvbs.codeRateCap),
+ .standard = static_cast<uint8_t>(dvbs.standard),
+ });
+ break;
+ }
+ case TunerFrontendCapabilities::dvbtCaps: {
+ auto dvbt = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::dvbtCaps>();
+ hidlFrontendInfo.frontendCaps.dvbtCaps({
+ .transmissionModeCap = static_cast<uint32_t>(dvbt.transmissionModeCap),
+ .bandwidthCap = static_cast<uint32_t>(dvbt.bandwidthCap),
+ .constellationCap = static_cast<uint32_t>(dvbt.constellationCap),
+ .coderateCap = static_cast<uint32_t>(dvbt.codeRateCap),
+ .hierarchyCap = static_cast<uint32_t>(dvbt.hierarchyCap),
+ .guardIntervalCap = static_cast<uint32_t>(dvbt.guardIntervalCap),
+ .isT2Supported = dvbt.isT2Supported,
+ .isMisoSupported = dvbt.isMisoSupported,
+ });
+ break;
+ }
+ case TunerFrontendCapabilities::isdbsCaps: {
+ auto isdbs = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::isdbsCaps>();
+ hidlFrontendInfo.frontendCaps.isdbsCaps({
+ .modulationCap = static_cast<uint32_t>(isdbs.modulationCap),
+ .coderateCap = static_cast<uint32_t>(isdbs.codeRateCap),
+ });
+ break;
+ }
+ case TunerFrontendCapabilities::isdbs3Caps: {
+ auto isdbs3 = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::isdbs3Caps>();
+ hidlFrontendInfo.frontendCaps.isdbs3Caps({
+ .modulationCap = static_cast<uint32_t>(isdbs3.modulationCap),
+ .coderateCap = static_cast<uint32_t>(isdbs3.codeRateCap),
+ });
+ break;
+ }
+ case TunerFrontendCapabilities::isdbtCaps: {
+ auto isdbt = aidlFrontendInfo.caps.get<TunerFrontendCapabilities::isdbtCaps>();
+ hidlFrontendInfo.frontendCaps.isdbtCaps({
+ .modeCap = static_cast<uint32_t>(isdbt.modeCap),
+ .bandwidthCap = static_cast<uint32_t>(isdbt.bandwidthCap),
+ .modulationCap = static_cast<uint32_t>(isdbt.modulationCap),
+ .coderateCap = static_cast<uint32_t>(isdbt.codeRateCap),
+ .guardIntervalCap = static_cast<uint32_t>(isdbt.guardIntervalCap),
+ });
+ break;
+ }
+ }
return hidlFrontendInfo;
}
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index acd018e..744bf20 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -147,7 +147,7 @@
sp<IDescrambler> openHidlDescrambler();
vector<int> getLnbHandles();
DemuxCapabilities getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps);
- FrontendInfo FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo);
+ FrontendInfo frontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo);
void updateTunerResources();
void updateFrontendResources();
void updateLnbResources();
diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp
index 45f42f1b5..48d7380 100644
--- a/native/android/system_fonts.cpp
+++ b/native/android/system_fonts.cpp
@@ -264,7 +264,7 @@
static_cast<minikin::FamilyVariant>(matcher->mFamilyVariant),
1 /* maxRun */);
- const minikin::Font* font = runs[0].fakedFont.font;
+ const std::shared_ptr<minikin::Font>& font = runs[0].fakedFont.font;
std::unique_ptr<AFont> result = std::make_unique<AFont>();
const android::MinikinFontSkia* minikinFontSkia =
reinterpret_cast<android::MinikinFontSkia*>(font->typeface().get());
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index fd71670..606cd57 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -322,7 +322,8 @@
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 fa3213d..fbe15bb 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.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;
@@ -1368,7 +1369,7 @@
public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
try {
return mService.getDefaultNetworkCapabilitiesForUser(
- userId, mContext.getOpPackageName());
+ userId, mContext.getOpPackageName(), getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1450,7 +1451,8 @@
@Nullable
public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
try {
- return mService.getNetworkCapabilities(network, mContext.getOpPackageName());
+ return mService.getNetworkCapabilities(
+ network, mContext.getOpPackageName(), getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3720,7 +3722,8 @@
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 {
@@ -3735,7 +3738,8 @@
Binder binder = new Binder();
if (reqType == LISTEN) {
request = mService.listenForNetwork(
- need, messenger, binder, callingPackageName);
+ need, messenger, binder, callingPackageName,
+ getAttributionTag());
} else {
request = mService.requestNetwork(
need, reqType.ordinal(), messenger, timeoutMs, binder, legacyType,
@@ -4180,7 +4184,8 @@
checkPendingIntentNotNull(operation);
try {
mService.pendingListenForNetwork(
- request.networkCapabilities, operation, mContext.getOpPackageName());
+ request.networkCapabilities, operation, mContext.getOpPackageName(),
+ getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {
@@ -4189,8 +4194,9 @@
}
/**
- * 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
@@ -4203,7 +4209,7 @@
* {@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.
*/
@@ -4213,10 +4219,46 @@
}
/**
+ * 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
+ * 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
+ * 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) {
+ 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
@@ -4230,20 +4272,19 @@
* 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
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
- public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback,
+ @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) {
- // 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);
+ TRACK_SYSTEM_DEFAULT, TYPE_NONE, cbHandler);
}
/**
@@ -4845,9 +4886,13 @@
}
}
- private void setOemNetworkPreference(@NonNull OemNetworkPreferences preference) {
- Log.d(TAG, "setOemNetworkPreference called with preference: "
- + preference.toString());
+ private void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) {
+ try {
+ mService.setOemNetworkPreference(preference);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setOemNetworkPreference() failed for preference: " + preference.toString());
+ throw e.rethrowFromSystemServer();
+ }
}
@NonNull
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index db8b7ed..f909d13 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -29,6 +29,7 @@
import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.net.NetworkState;
+import android.net.OemNetworkPreferences;
import android.net.ProxyInfo;
import android.net.UidRange;
import android.net.QosSocketInfo;
@@ -65,7 +66,7 @@
Network getNetworkForType(int networkType);
Network[] getAllNetworks();
NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
- int userId, String callingPackageName);
+ int userId, String callingPackageName, String callingAttributionTag);
boolean isNetworkSupported(int networkType);
@@ -74,7 +75,8 @@
LinkProperties getLinkPropertiesForType(int networkType);
LinkProperties getLinkProperties(in Network network);
- NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName);
+ NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName,
+ String callingAttributionTag);
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
NetworkState[] getAllNetworkState();
@@ -175,10 +177,12 @@
void releasePendingNetworkRequest(in PendingIntent operation);
NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
- in Messenger messenger, in IBinder binder, String callingPackageName);
+ in Messenger messenger, in IBinder binder, String callingPackageName,
+ String callingAttributionTag);
void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
- in PendingIntent operation, String callingPackageName);
+ in PendingIntent operation, String callingPackageName,
+ String callingAttributionTag);
void releaseNetworkRequest(in NetworkRequest networkRequest);
@@ -240,4 +244,6 @@
void registerQosSocketCallback(in QosSocketInfo socketInfo, in IQosCallback callback);
void unregisterQosCallback(in IQosCallback callback);
+
+ void setOemNetworkPreference(in OemNetworkPreferences preference);
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 55b2c3c..9d67f0b 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -762,12 +762,14 @@
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 c4d1b09..b4a651c 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -16,6 +16,22 @@
package android.net;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -30,6 +46,8 @@
import android.text.TextUtils;
import android.util.proto.ProtoOutputStream;
+import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -86,17 +104,14 @@
* 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
@@ -119,6 +134,7 @@
TRACK_DEFAULT,
REQUEST,
BACKGROUND_REQUEST,
+ TRACK_SYSTEM_DEFAULT,
};
/**
@@ -156,8 +172,30 @@
* needed in terms of {@link NetworkCapabilities} features
*/
public static class Builder {
+ /**
+ * Capabilities that are currently compatible with VCN networks.
+ */
+ private static final List<Integer> VCN_SUPPORTED_CAPABILITIES = Arrays.asList(
+ NET_CAPABILITY_CAPTIVE_PORTAL,
+ NET_CAPABILITY_DUN,
+ NET_CAPABILITY_FOREGROUND,
+ NET_CAPABILITY_INTERNET,
+ NET_CAPABILITY_NOT_CONGESTED,
+ NET_CAPABILITY_NOT_METERED,
+ NET_CAPABILITY_NOT_RESTRICTED,
+ NET_CAPABILITY_NOT_ROAMING,
+ NET_CAPABILITY_NOT_SUSPENDED,
+ NET_CAPABILITY_NOT_VPN,
+ NET_CAPABILITY_PARTIAL_CONNECTIVITY,
+ NET_CAPABILITY_TEMPORARILY_NOT_METERED,
+ NET_CAPABILITY_TRUSTED,
+ NET_CAPABILITY_VALIDATED);
+
private final NetworkCapabilities mNetworkCapabilities;
+ // A boolean that represents the user modified NOT_VCN_MANAGED capability.
+ private boolean mModifiedNotVcnManaged = false;
+
/**
* Default constructor for Builder.
*/
@@ -179,6 +217,7 @@
// maybeMarkCapabilitiesRestricted() doesn't add back.
final NetworkCapabilities nc = new NetworkCapabilities(mNetworkCapabilities);
nc.maybeMarkCapabilitiesRestricted();
+ deduceNotVcnManagedCapability(nc);
return new NetworkRequest(nc, ConnectivityManager.TYPE_NONE,
ConnectivityManager.REQUEST_ID_UNSET, Type.NONE);
}
@@ -195,6 +234,9 @@
*/
public Builder addCapability(@NetworkCapabilities.NetCapability int capability) {
mNetworkCapabilities.addCapability(capability);
+ if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) {
+ mModifiedNotVcnManaged = true;
+ }
return this;
}
@@ -206,6 +248,9 @@
*/
public Builder removeCapability(@NetworkCapabilities.NetCapability int capability) {
mNetworkCapabilities.removeCapability(capability);
+ if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) {
+ mModifiedNotVcnManaged = true;
+ }
return this;
}
@@ -263,6 +308,9 @@
@NonNull
public Builder clearCapabilities() {
mNetworkCapabilities.clearAll();
+ // If the caller explicitly clear all capabilities, the NOT_VCN_MANAGED capabilities
+ // should not be add back later.
+ mModifiedNotVcnManaged = true;
return this;
}
@@ -382,6 +430,25 @@
mNetworkCapabilities.setSignalStrength(signalStrength);
return this;
}
+
+ /**
+ * Deduce the NET_CAPABILITY_NOT_VCN_MANAGED capability from other capabilities
+ * and user intention, which includes:
+ * 1. For the requests that don't have anything besides
+ * {@link #VCN_SUPPORTED_CAPABILITIES}, add the NET_CAPABILITY_NOT_VCN_MANAGED to
+ * allow the callers automatically utilize VCN networks if available.
+ * 2. For the requests that explicitly add or remove NET_CAPABILITY_NOT_VCN_MANAGED,
+ * do not alter them to allow user fire request that suits their need.
+ *
+ * @hide
+ */
+ private void deduceNotVcnManagedCapability(final NetworkCapabilities nc) {
+ if (mModifiedNotVcnManaged) return;
+ for (final int cap : nc.getCapabilities()) {
+ if (!VCN_SUPPORTED_CAPABILITIES.contains(cap)) return;
+ }
+ nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
+ }
}
// implement the Parcelable interface
@@ -435,25 +502,7 @@
* @hide
*/
public boolean isRequest() {
- return isForegroundRequest() || isBackgroundRequest();
- }
-
- /**
- * Returns true iff. the contained NetworkRequest is one that:
- *
- * - should be associated with at most one satisfying network
- * at a time;
- *
- * - should cause a network to be kept up and in the foreground if
- * it is the best network which can satisfy the NetworkRequest.
- *
- * For full detail of how isRequest() is used for pairing Networks with
- * NetworkRequests read rematchNetworkAndRequests().
- *
- * @hide
- */
- public boolean isForegroundRequest() {
- return type == Type.TRACK_DEFAULT || type == Type.REQUEST;
+ return type == Type.REQUEST || type == Type.BACKGROUND_REQUEST;
}
/**
@@ -550,6 +599,8 @@
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/Proxy.java b/packages/Connectivity/framework/src/android/net/Proxy.java
index 03cfbbb..77c8a4f 100644
--- a/packages/Connectivity/framework/src/android/net/Proxy.java
+++ b/packages/Connectivity/framework/src/android/net/Proxy.java
@@ -32,8 +32,6 @@
import java.net.ProxySelector;
import java.net.URI;
import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* A convenience class for accessing the user and default proxy
@@ -66,40 +64,9 @@
@Deprecated
public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
- /** @hide */
- public static final int PROXY_VALID = 0;
- /** @hide */
- public static final int PROXY_HOSTNAME_EMPTY = 1;
- /** @hide */
- public static final int PROXY_HOSTNAME_INVALID = 2;
- /** @hide */
- public static final int PROXY_PORT_EMPTY = 3;
- /** @hide */
- public static final int PROXY_PORT_INVALID = 4;
- /** @hide */
- public static final int PROXY_EXCLLIST_INVALID = 5;
-
private static ConnectivityManager sConnectivityManager = null;
- // Hostname / IP REGEX validation
- // Matches blank input, ips, and domain names
- private static final String NAME_IP_REGEX =
- "[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*(\\.[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*)*";
-
- private static final String HOSTNAME_REGEXP = "^$|^" + NAME_IP_REGEX + "$";
-
- private static final Pattern HOSTNAME_PATTERN;
-
- private static final String EXCL_REGEX =
- "[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*(\\.[a-zA-Z0-9*]+(\\-[a-zA-Z0-9*]+)*)*";
-
- private static final String EXCLLIST_REGEXP = "^$|^" + EXCL_REGEX + "(," + EXCL_REGEX + ")*$";
-
- private static final Pattern EXCLLIST_PATTERN;
-
static {
- HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP);
- EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP);
sDefaultProxySelector = ProxySelector.getDefault();
}
@@ -218,33 +185,6 @@
return false;
}
- /**
- * Validate syntax of hostname, port and exclusion list entries
- * {@hide}
- */
- public static int validate(String hostname, String port, String exclList) {
- Matcher match = HOSTNAME_PATTERN.matcher(hostname);
- Matcher listMatch = EXCLLIST_PATTERN.matcher(exclList);
-
- if (!match.matches()) return PROXY_HOSTNAME_INVALID;
-
- if (!listMatch.matches()) return PROXY_EXCLLIST_INVALID;
-
- if (hostname.length() > 0 && port.length() == 0) return PROXY_PORT_EMPTY;
-
- if (port.length() > 0) {
- if (hostname.length() == 0) return PROXY_HOSTNAME_EMPTY;
- int portVal = -1;
- try {
- portVal = Integer.parseInt(port);
- } catch (NumberFormatException ex) {
- return PROXY_PORT_INVALID;
- }
- if (portVal <= 0 || portVal > 0xFFFF) return PROXY_PORT_INVALID;
- }
- return PROXY_VALID;
- }
-
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@Deprecated
diff --git a/packages/Connectivity/framework/src/android/net/ProxyInfo.java b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
index 9c9fed1..229db0d 100644
--- a/packages/Connectivity/framework/src/android/net/ProxyInfo.java
+++ b/packages/Connectivity/framework/src/android/net/ProxyInfo.java
@@ -23,6 +23,8 @@
import android.os.Parcelable;
import android.text.TextUtils;
+import com.android.net.module.util.ProxyUtils;
+
import java.net.InetSocketAddress;
import java.net.URLConnection;
import java.util.List;
@@ -233,7 +235,7 @@
*/
public boolean isValid() {
if (!Uri.EMPTY.equals(mPacFileUrl)) return true;
- return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost,
+ return ProxyUtils.PROXY_VALID == ProxyUtils.validate(mHost == null ? "" : mHost,
mPort == 0 ? "" : Integer.toString(mPort),
mExclusionList == null ? "" : mExclusionList);
}
diff --git a/packages/Connectivity/framework/src/android/net/VpnManager.java b/packages/Connectivity/framework/src/android/net/VpnManager.java
index c87b827..1e30283 100644
--- a/packages/Connectivity/framework/src/android/net/VpnManager.java
+++ b/packages/Connectivity/framework/src/android/net/VpnManager.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
@@ -28,6 +29,8 @@
import android.content.res.Resources;
import android.os.RemoteException;
+import com.android.internal.net.LegacyVpnInfo;
+import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import java.io.IOException;
@@ -52,13 +55,29 @@
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 {}
@@ -161,4 +180,104 @@
throw e.rethrowFromSystemServer();
}
}
-}
+
+ /**
+ * Return the VPN configuration for the given user ID.
+ * @hide
+ */
+ @Nullable
+ public VpnConfig getVpnConfig(@UserIdInt int userId) {
+ try {
+ return mService.getVpnConfig(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Prepare for a VPN application.
+ * VPN permissions are checked in the {@link Vpn} class. If the caller is not {@code userId},
+ * {@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+ *
+ * @param oldPackage Package name of the application which currently controls VPN, which will
+ * be replaced. If there is no such application, this should should either be
+ * {@code null} or {@link VpnConfig.LEGACY_VPN}.
+ * @param newPackage Package name of the application which should gain control of VPN, or
+ * {@code null} to disable.
+ * @param userId User for whom to prepare the new VPN.
+ *
+ * @hide
+ */
+ public boolean prepareVpn(@Nullable String oldPackage, @Nullable String newPackage,
+ int userId) {
+ try {
+ return mService.prepareVpn(oldPackage, newPackage, userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether the VPN package has the ability to launch VPNs without user intervention. This
+ * method is used by system-privileged apps. VPN permissions are checked in the {@link Vpn}
+ * class. If the caller is not {@code userId}, {@link
+ * android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission is required.
+ *
+ * @param packageName The package for which authorization state should change.
+ * @param userId User for whom {@code packageName} is installed.
+ * @param vpnType The {@link VpnManager.VpnType} constant representing what class of VPN
+ * permissions should be granted. When unauthorizing an app, {@link
+ * VpnManager.TYPE_VPN_NONE} should be used.
+ * @hide
+ */
+ public void setVpnPackageAuthorization(
+ String packageName, int userId, @VpnManager.VpnType int vpnType) {
+ try {
+ mService.setVpnPackageAuthorization(packageName, userId, vpnType);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return the legacy VPN information for the specified user ID.
+ * @hide
+ */
+ public LegacyVpnInfo getLegacyVpnInfo(@UserIdInt int userId) {
+ try {
+ return mService.getLegacyVpnInfo(userId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Starts a legacy VPN.
+ * @hide
+ */
+ public void startLegacyVpn(VpnProfile profile) {
+ try {
+ mService.startLegacyVpn(profile);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Informs the service that legacy lockdown VPN state should be updated (e.g., if its keystore
+ * entry has been updated). If the LockdownVpn mechanism is enabled, updates the vpn
+ * with a reload of its profile.
+ *
+ * <p>This method can only be called by the system UID
+ * @return a boolean indicating success
+ *
+ * @hide
+ */
+ public boolean updateLockdownVpn() {
+ try {
+ return mService.updateLockdownVpn();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 3837743..889980a 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Sal nie outomaties koppel nie"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang nie"</string>
<string name="saved_network" msgid="7143698034077223645">"Gestoor deur <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Gekoppel aan beperkte netwerk"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Outomaties deur %1$s gekoppel"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Outomaties deur netwerkgraderingverskaffer gekoppel"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Gekoppel via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 4ce01d6..c41e4d5 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"በራስ-ሰር አይገናኝም"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ምንም የበይነመረብ መዳረሻ ያለም"</string>
<string name="saved_network" msgid="7143698034077223645">"የተቀመጠው በ<xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ከሚለካ አውታረ መረብ ጋር ተገናኝቷል"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"በ%1$s በኩል በራስ-ሰር ተገናኝቷል"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"በአውታረ መረብ ደረጃ ሰጪ አቅራቢ በኩል በራስ-ሰር ተገናኝቷል"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"በ%1$s በኩል መገናኘት"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 2580d0f..f8d1d57 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"لن يتم الاتصال تلقائيًا"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"لا يتوفّر اتصال بالإنترنت"</string>
<string name="saved_network" msgid="7143698034077223645">"تم الحفظ بواسطة <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"تم الاتصال بشبكة تفرض تكلفة استخدام."</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"تم الاتصال تلقائيًا عبر %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"تم الاتصال تلقائيًا عبر مقدم خدمة تقييم الشبكة"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"تم الاتصال عبر %1$s"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index b61ff50..c6078f8 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"স্বয়ংক্ৰিয়ভাৱে সংযোগ নহ’ব"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ইণ্টাৰনেট সংযোগ নাই"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>এ ছেভ কৰিছে"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"নিৰিখ-নিৰ্দিষ্ট নেটৱৰ্কৰ সৈতে সংযোগ কৰা হৈছে"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s মাধ্যমেদি স্বয়ংক্ৰিয়ভাৱে সংযোগ কৰা হৈছে"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"নেটৱৰ্ক ৰেটিং প্ৰদানকাৰীৰ জৰিয়তে স্বয়ং সংয়োগ কৰা হ’ল"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-ৰ মাধ্যমেদি সংযোগ কৰা হৈছে"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index d063776..d3e0a25 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Avtomatik qoşulmayacaq"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"İnternet girişi yoxdur"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tərəfindən saxlandı"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Ölçülən şəbəkəyə qoşulub"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s üzərindən avtomatik qoşuldu"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Avtomatik olaraq şəbəkə reytinq provayderi ilə qoşuludur"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s vasitəsilə qoşuludur"</string>
@@ -313,7 +312,7 @@
<string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Adsız Bluetooth cihazları (yalnız MAC ünvanları) göstəriləcək"</string>
<string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Uzaqdan idarə olunan cihazlarda dözülməz yüksək səs həcmi və ya nəzarət çatışmazlığı kimi səs problemləri olduqda Bluetooth mütləq səs həcmi xüsusiyyətini deaktiv edir."</string>
<string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"Bluetooth Gabeldorsche funksiyasını aktiv edir."</string>
- <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Təkmilləşdirilmiş Bağlantı funksiyasını aktiv edir."</string>
+ <string name="enhanced_connectivity_summary" msgid="1576414159820676330">"Qabaqcıl məlumat mübadiləsini aktiv edir."</string>
<string name="enable_terminal_title" msgid="3834790541986303654">"Yerli terminal"</string>
<string name="enable_terminal_summary" msgid="2481074834856064500">"Yerli örtük girişini təklif edən terminal tətbiqi aktiv edin"</string>
<string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP yoxlanılır"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 2976bb5..8541222 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Automatsko povezivanje nije uspelo"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string>
<string name="saved_network" msgid="7143698034077223645">"Sačuvao/la je <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezani ste na mrežu sa ograničenjem"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezano preko %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezano preko dobavljača ocene mreže"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Veza je uspostavljena preko pristupne tačke %1$s"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 7af9e93..aab600f 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не будзе аўтаматычна падключацца"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Няма доступу да інтэрнэту"</string>
<string name="saved_network" msgid="7143698034077223645">"Захавана праз: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Падключана да сеткі з падлікам трафіка"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Аўтаматычна падключана праз %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Аўтаматычна падключана праз пастаўшчыка паслугі ацэнкі сеткі"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Падключана праз %1$s"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 77b6493..92f2935 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Няма да се свърже автоматично"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Няма достъп до интернет"</string>
<string name="saved_network" msgid="7143698034077223645">"Запазено от <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Установена е връзка с мрежа с отчитане"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматично е установена връзка чрез %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматично е установена връзка чрез доставчик на услуги за оценяване на мрежите"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Установена е връзка през „%1$s“"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 819625b..b40e24e 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"অটোমেটিক কানেক্ট করবে না"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ইন্টারনেট অ্যাক্সেস নেই"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> দ্বারা সেভ করা"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"মিটার্ড নেটওয়ার্কের সঙ্গে কানেক্ট করা"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"স্বয়ংক্রিয়ভাবে %1$s এর মাধ্যমে কানেক্ট হয়েছে"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"নেটওয়ার্কের রেটিং প্রদানকারীর মাধ্যমে অটোমেটিক কানেক্ট"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s মাধ্যমে কানেক্ট হয়েছে"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 193ac60..cec2e45 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Neće se automatski povezati"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string>
<string name="saved_network" msgid="7143698034077223645">"Sačuvano: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezani ste s mrežom s naplatom"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezano koristeći %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezano putem ocjenjivača mreže"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Povezani preko %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index fef9bfb..6134da8 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No es connectarà automàticament"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"No hi ha accés a Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Desada per <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connectat a una xarxa d\'ús mesurat"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Connectada automàticament a través de: %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connectada automàticament a través d\'un proveïdor de valoració de xarxes"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Connectada mitjançant %1$s"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 281a788..f29a3dd 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Připojení nebude automaticky navázáno"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nebyl zjištěn žádný přístup k internetu"</string>
<string name="saved_network" msgid="7143698034077223645">"Uloženo uživatelem <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Připojeno k měřené síti"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaticky připojeno přes poskytovatele %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automaticky připojeno přes poskytovatele hodnocení sítí"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Připojeno prostřednictvím %1$s"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 69cc8d8..d92d41d 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Der oprettes ikke automatisk forbindelse"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internetadgang"</string>
<string name="saved_network" msgid="7143698034077223645">"Gemt af <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Der er oprettet forbindelse til det forbrugsbaserede netværk"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisk tilsluttet via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisk forbundet via udbyder af netværksvurdering"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Tilsluttet via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 737ea16..ac05b620 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Kein automatischer Verbindungsaufbau"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Kein Internetzugriff"</string>
<string name="saved_network" msgid="7143698034077223645">"Gespeichert von <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Mit kostenpflichtigem Netzwerk verbunden"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch über %1$s verbunden"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch über Anbieter von Netzwerkbewertungen verbunden"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Über %1$s verbunden"</string>
@@ -287,7 +286,7 @@
<string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"Verringert den Akkuverbrauch und verbessert die Netzwerkleistung"</string>
<string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"Wenn dieser Modus aktiviert ist, kann sich die MAC-Adresse dieses Geräts bei jeder Verbindung mit einem Netzwerk ändern, bei dem die MAC-Adressen randomisiert werden."</string>
<string name="wifi_metered_label" msgid="8737187690304098638">"Kostenpflichtig"</string>
- <string name="wifi_unmetered_label" msgid="6174142840934095093">"Kostenlos"</string>
+ <string name="wifi_unmetered_label" msgid="6174142840934095093">"Ohne Datenlimit"</string>
<string name="select_logd_size_title" msgid="1604578195914595173">"Logger-Puffergrößen"</string>
<string name="select_logd_size_dialog_title" msgid="2105401994681013578">"Größe pro Protokollpuffer wählen"</string>
<string name="dev_logpersist_clear_warning_title" msgid="8631859265777337991">"Speicher der dauerhaften Protokollierung löschen?"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 8e1d5e3..0b11d69 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Δεν θα συνδεθεί αυτόματα"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Δεν υπάρχει πρόσβαση στο διαδίκτυο"</string>
<string name="saved_network" msgid="7143698034077223645">"Αποθηκεύτηκε από <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Σύνδεση σε δίκτυο με ογκοχρέωση"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Συνδέθηκε αυτόματα μέσω %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Συνδέθηκε αυτόματα μέσω παρόχου αξιολόγησης δικτύου"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Συνδέθηκε μέσω %1$s"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index f79072f..1da8e37 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No se conectará automáticamente"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"No hay acceso a Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Conectado a la red de uso medido"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conexión automática mediante %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automáticamente mediante proveedor de calificación de red"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Conexión a través de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index f99a3f1..54226ce 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No se establecerá conexión automáticamente"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"No se ha detectado ningún acceso a Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Conectado a una red de uso medido"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectada automáticamente a través de %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automáticamente a través de un proveedor de valoración de redes"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index fa2aa46..4c747e3 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Automaatselt ei ühendata"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Juurdepääs Internetile puudub"</string>
<string name="saved_network" msgid="7143698034077223645">"Salvestas: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Ühendatud mahupõhise võrguga"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Ühendus loodi automaatselt teenusega %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ühendus loodi automaatselt võrgukvaliteedi hinnangute pakkuja kaudu"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Ühendatud üksuse %1$s kaudu"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 2c4a8ee..6016cd2 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Ez da konektatuko automatikoki"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ezin da konektatu Internetera"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> aplikazioak gorde du"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Sare neurtu batera konektatuta"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s bidez automatikoki konektatuta"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatikoki konektatuta sare-balorazioen hornitzailearen bidez"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s bidez konektatuta"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 1c6ca76..e855570 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -36,10 +36,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"اتصال بهصورت خودکار انجام نمیشود"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"دسترسی به اینترنت ندارد"</string>
<string name="saved_network" msgid="7143698034077223645">"ذخیرهشده توسط <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"اتصال به شبکه محدود برقرار شد"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"اتصال خودکار ازطریق %1$s"</string>
- <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"اتصال خودکار ازطریق ارائهدهنده رتبهبندی شبکه"</string>
+ <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"اتصال خودکار ازطریق ارائهدهنده ردهبندی شبکه"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"متصل از طریق %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"متصل شده ازطریق <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"در دسترس از طریق %1$s"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 04c9130..724e52a 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Yhteyttä ei muodosteta automaattisesti"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ei internetyhteyttä"</string>
<string name="saved_network" msgid="7143698034077223645">"Tallentaja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Yhdistetty maksulliseen verkkoon"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaattinen yhteys muodostettu palvelun %1$s kautta"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Yhdistetty automaattisesti verkon arviointipalvelun kautta"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Yhdistetty seuraavan kautta: %1$s"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index aa0cd2a..74c3005 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Reconnexion automatique impossible"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Aucun accès à Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Enregistrés par <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Appareil connecté à un réseau mesuré"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiquement connecté par %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connecté automatiquement par le fournisseur d\'avis sur le réseau"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Connecté par %1$s"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index dbdc160..c9651ce 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Reconnexion automatique impossible"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Aucun accès à Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Enregistré lors de : <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connecté au réseau facturé à l\'usage"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Connecté automatiquement via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connecté automatiquement via un fournisseur d\'évaluation de l\'état du réseau"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Connecté via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 90c1303..f499f58 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Non se conectará automaticamente"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Sen acceso a Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Gardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Estableceuse conexión coa rede de pago por consumo"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectouse automaticamente a través de %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectada automaticamente a través dun provedor de valoración de redes"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 4caeda2..23ee1de 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ઑટોમૅટિક રીતે કનેક્ટ કરશે નહીં"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"કોઈ ઇન્ટરનેટ ઍક્સેસ નથી"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા સચવાયું"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"મીટર્ડ (ડેટા નિયંત્રણ) નેટવર્ક સાથે કનેક્ટેડ છે"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s દ્વારા સ્વત: કનેક્ટ થયેલ"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"નેટવર્ક રેટિંગ પ્રદાતા દ્વારા ઑટોમૅટિક રીતે કનેક્ટ થયું"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s દ્વારા કનેક્ટ થયેલ"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 468808b..a2fc43c 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"अपने आप कनेक्ट नहीं होगा"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"इंटरनेट नहीं है"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> के द्वारा सहेजा गया"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"सीमित डेटा वाले नेटवर्क से कनेक्ट किया गया"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s के ज़रिए ऑटोमैटिक रूप से कनेक्ट है"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्क रेटिंग कंपनी के ज़रिए अपने आप कनेक्ट है"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s के द्वारा उपलब्ध"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 932c256..396051d 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Neće se povezati automatski"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string>
<string name="saved_network" msgid="7143698034077223645">"Spremila aplik. <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezano s mrežom s ograničenim prometom"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezan putem %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezan putem ocjenjivača mreže"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Povezano putem %1$s"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index fbaffac..0c9bc54 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nem csatlakozik automatikusan"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nincs internet-hozzáférés"</string>
<string name="saved_network" msgid="7143698034077223645">"Mentette: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Forgalomkorlátos hálózathoz csatlakozva"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatikusan csatlakozott a következőn keresztül: %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatikusan csatlakozott a hálózatértékelés szolgáltatóján keresztül"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Csatlakozva a következőn keresztül: %1$s"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 224d641..56c93b6 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Չի միանա ավտոմատ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ինտերնետ կապ չկա"</string>
<string name="saved_network" msgid="7143698034077223645">"Ով է պահել՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Միացած է վճարովի թրաֆիկով ցանցի"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Ավտոմատ կերպով կապակցվել է %1$s-ի միջոցով"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ավտոմատ միացել է ցանցերի վարկանիշի մատակարարի միջոցով"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Միացված է %1$s-ի միջոցով"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 8cf13cd..0440f47 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Tidak akan tersambung otomatis"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Tidak ada akses internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Disimpan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Terhubung ke jaringan berbayar"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Tersambung otomatis melalui %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Otomatis tersambung melalui penyedia rating jaringan"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Terhubung melalui %1$s"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index aa8893e..0bb294f 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Mun ekki tengjast sjálfkrafa"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Enginn netaðgangur"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> vistaði"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Tengdist neti með mældri notkun"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Sjálfkrafa tengt um %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Sjálfkrafa tengt um netgæðaveitu"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Tengt í gegnum %1$s"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 0199f54..54049d7 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Non verrà eseguita la connessione automatica"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nessun accesso a Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Salvata da <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connessione a rete a consumo effettuata"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Collegato automaticamente tramite %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Collegato automaticamente tramite fornitore di servizi di valutazione rete"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Collegato tramite %1$s"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index a50a22d..6cc4347 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"לא יתבצע חיבור באופן אוטומטי"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"אין גישה לאינטרנט"</string>
<string name="saved_network" msgid="7143698034077223645">"נשמר על ידי <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"יש חיבור לרשת המבוססת על חיוב לפי שימוש בנתונים"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"מחובר אוטומטית דרך %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"מחובר אוטומטית דרך ספק של דירוג רשת"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"מחובר דרך %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index a1d1b70..ec674ad 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"自動的に接続されません"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"インターネット接続なし"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>で保存"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"従量制ネットワークに接続しました"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s 経由で自動的に接続しています"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ネットワーク評価プロバイダ経由で自動的に接続しています"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s経由で接続"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 77fd4b1..30ab3b4 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ავტომატურად დაკავშირება ვერ მოხერხდება"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ინტერნეტ-კავშირი არ არის"</string>
<string name="saved_network" msgid="7143698034077223645">"შენახული <xliff:g id="NAME">%1$s</xliff:g>-ის მიერ"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"დაკავშირებულია ფასიან ქსელთან"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"ავტომატურად დაკავშირდა %1$s-ის მეშვეობით"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ავტომატურად დაკავშირდა ქსელის ხარისხის შეფასების პროვაიდერის მეშვეობით"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-ით დაკავშირებული"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index eb5cd54..ef78527 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматты қосылмайды"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Интернетпен байланыс жоқ"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> сақтаған"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Трафик саналатын желіге қосылды."</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s арқылы автоматты қосылды"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Желі рейтингі провайдері арқылы автоматты түрде қосылған"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s арқылы қосылған"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 38abb80..ed59c74 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"នឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិទេ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"មិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
<string name="saved_network" msgid="7143698034077223645">"បានរក្សាទុកដោយ <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"បានភ្ជាប់ទៅបណ្ដាញដែលផ្អែកតាមទិន្នន័យដែលប្រើ"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"បានភ្ជាប់ដោយស្វ័យប្រវត្តិតាមរយៈ %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"បានភ្ជាប់ដោយស្វ័យប្រវត្តិតាមរយៈក្រុមហ៊ុនផ្តល់ការវាយតម្លៃលើបណ្តាញ"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"បានភ្ជាប់តាមរយៈ %1$s"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 560fba15..995dc86 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವಿಲ್ಲ"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ನಿಂದ ಉಳಿಸಲಾಗಿದೆ"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ಮಾಪನ ಮಾಡಲಾದ ನೆಟ್ವರ್ಕ್ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ಮೂಲಕ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ನೆಟ್ವರ್ಕ್ ರೇಟಿಂಗ್ ಒದಗಿಸುವವರ ಮೂಲಕ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ಮೂಲಕ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index f43ce16..7863d48 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"자동으로 연결되지 않습니다."</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"인터넷에 연결되어 있지 않음"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>(으)로 저장됨"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"데이터 전송량 제한이 있는 네트워크에 연결됨"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s을(를) 통해 자동으로 연결됨"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"네트워크 평가 제공업체를 통해 자동으로 연결됨"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s을(를) 통해 연결됨"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 35b1ecac..45d05c6 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматтык түрдө туташпайт"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Интернетке туташпай турат"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> тарабынан сакталды"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Чектелген трафикке туташтырылды"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s аркылуу автоматтык түрдө туташты"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Тармактар рейтингинин булагы аркылуу автоматтык түрдө туташты"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s аркылуу жеткиликтүү"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index f60fe7f..ca6e06c 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ຈະບໍ່ເຊື່ອມຕໍ່ອັດຕະໂນມັດ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
<string name="saved_network" msgid="7143698034077223645">"ບັນທຶກໂດຍ <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ເຊື່ອມຕໍ່ຫາເຄືອຂ່າຍທີ່ມີການວັດແທກແລ້ວ"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"ເຊື່ອມຕໍ່ຜ່ານທາງ %1$s ໂດຍອັດຕະໂນມັດ"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ເຊື່ອມຕໍ່ກັບອັດຕະໂນມັດແລ້ວຜ່ານຜູ້ໃຫ້ບໍລິການຄະແນນເຄືອຂ່າຍ"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"ເຊື່ອມຕໍ່ຜ່ານ %1$s ແລ້ວ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index e66e3c5..173b57e 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nebus automatiškai prisijungiama"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nėra interneto ryšio"</string>
<string name="saved_network" msgid="7143698034077223645">"Išsaugojo <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Prisijungta prie matuojamo tinklo"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiškai prisijungta naudojant „%1$s“"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatiškai prisijungta naudojant tinklo įvertinimo paslaugos teikėjo paslaugomis"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Prisijungta naudojant „%1$s“"</string>
@@ -208,7 +207,7 @@
<string name="enable_adb" msgid="8072776357237289039">"USB perkrova"</string>
<string name="enable_adb_summary" msgid="3711526030096574316">"Derinimo režimas, kai prijungtas USB"</string>
<string name="clear_adb_keys" msgid="3010148733140369917">"Panaikinti USB derinimo prieigos teises"</string>
- <string name="enable_adb_wireless" msgid="6973226350963971018">"Belaidžio ryšio derinimas"</string>
+ <string name="enable_adb_wireless" msgid="6973226350963971018">"Belaidžio ryšio derin."</string>
<string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Derinimo režimas, kai prisijungta prie „Wi‑Fi“"</string>
<string name="adb_wireless_error" msgid="721958772149779856">"Klaida"</string>
<string name="adb_wireless_settings" msgid="2295017847215680229">"Belaidžio ryšio derinimas"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 90bcc04..0ef3f0e 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не може да се поврзе автоматски"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Нема пристап до интернет"</string>
<string name="saved_network" msgid="7143698034077223645">"Зачувано од <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Поврзано на мрежа со ограничен интернет"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматски поврзано преку %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматски поврзано преку оценувач на мрежа"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Поврзано преку %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index ba1987b..e6289ae 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"സ്വയമേവ കണക്റ്റുചെയ്യില്ല"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> സംരക്ഷിച്ചത്"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"മീറ്റർ ചെയ്ത നെറ്റ്വർക്കിലേക്ക് കണക്റ്റ് ചെയ്തു"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s വഴി സ്വയമേവ ബന്ധിപ്പിച്ചു"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"നെറ്റ്വർക്ക് റേറ്റിംഗ് ദാതാവുമായി സ്വയം കണക്റ്റുചെയ്തു"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s വഴി ബന്ധിപ്പിച്ചു"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 45a831d..ead712c 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматаар холбогдохгүй"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Интернет хандалт алга"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> хадгалсан"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Хязгаартай сүлжээнд холбогдсон"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s-р автоматаар холбогдсон"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Сүлжээний үнэлгээ үзүүлэгчээр автоматаар холбогдох"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-р холбогдсон"</string>
@@ -398,8 +397,8 @@
<item msgid="1282170165150762976">"Дижитал агуулгад зориулан тааруулсан өнгө"</item>
</string-array>
<string name="inactive_apps_title" msgid="5372523625297212320">"Зогсолтын горимын апп"</string>
- <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Идэвхгүй байна. Унтраах/асаахын тулд дарна уу."</string>
- <string name="inactive_app_active_summary" msgid="8047630990208722344">"Идэвхтэй байна. Унтраах/асаахын тулд дарна уу."</string>
+ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Идэвхгүй байна. Асаах/унтраахын тулд дарна уу."</string>
+ <string name="inactive_app_active_summary" msgid="8047630990208722344">"Идэвхтэй байна. Асаах/унтраахын тулд дарна уу."</string>
<string name="standby_bucket_summary" msgid="5128193447550429600">"Апп зогсолтын горимын төлөв:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
<string name="transcode_settings_title" msgid="2581975870429850549">"Медиа хөрвүүлгийн тохиргоо"</string>
<string name="transcode_user_control" msgid="6176368544817731314">"Хөрвүүлгийн өгөгдмөлийг дарах"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 1e69b28..edcc71b 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"स्वयंचलितपणे कनेक्ट करणार नाही"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"इंटरनेट अॅक्सेस नाही"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> द्वारे सेव्ह केले"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"मर्यादित नेटवर्कशी कनेक्ट केले आहे"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s द्वारे स्वयंचलितपणे कनेक्ट केले"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्क रेटिंग प्रदात्याद्वारे स्वयंचलितपणे कनेक्ट केले"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s द्वारे कनेक्ट केले"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 71c9eea..8a14437 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Tidak akan menyambung secara automatik"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Tiada akses Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Diselamatkan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Disambungkan kepada rangkaian bermeter"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Disambungkan secara automatik melalui %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Disambungkan secara automatik melalui pembekal penilaian rangkaian"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Disambungkan melalui %1$s"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 3b3983e..377c8b2 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"အလိုအလျောက်ချိတ်ဆက်မည်မဟုတ်ပါ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"အင်တာနက် ချိတ်ဆက်မှု မရှိပါ"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> က သိမ်းဆည်းခဲ့သည်"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"အခမဲ့မဟုတ်သော ကွန်ရက်သို့ ချိတ်ဆက်ထားသည်"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s မှတစ်ဆင့် အလိုအလျောက် ချိတ်ဆက်ထားပါသည်"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ကွန်ရက်အဆင့်သတ်မှတ်ပေးသူ မှတစ်ဆင့် အလိုအလျောက် ချိတ်ဆက်ထားပါသည်"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index a8c01b3..f0e773b 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Kobler ikke til automatisk"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internettilgang"</string>
<string name="saved_network" msgid="7143698034077223645">"Lagret av <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Koble til et nettverk med datamåling"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisk tilkoblet via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisk tilkoblet via leverandør av nettverksvurdering"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Tilkoblet via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index c285637..0bde70f 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"स्वतः जडान हुने छैन"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"इन्टरनेटमाथिको पहुँच छैन"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> द्वारा सुरक्षित गरियो"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"प्रयोगसम्बन्धी सीमा तोकिएको नेटवर्कमा कनेक्ट गरियो"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s मार्फत् स्वतः जडान गरिएको"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्कको दर्जा प्रदायक मार्फत स्वत: जडान गरिएको"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s मार्फत जडित"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 9f4ea68..2bc1e8c 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Er wordt niet automatisch verbinding gemaakt"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang"</string>
<string name="saved_network" msgid="7143698034077223645">"Opgeslagen door \'<xliff:g id="NAME">%1$s</xliff:g>\'"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Verbonden met netwerk met datalimiet"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch verbonden via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch verbonden via provider van netwerkbeoordelingen"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Verbonden via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 31bd7af..787c871 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ସ୍ୱଚାଳିତ ଭାବେ ସଂଯୁକ୍ତ ହେବନାହିଁ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ଇଣ୍ଟରନେଟ୍ର କୌଣସି ଆକ୍ସେସ୍ ନାହିଁ"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ଦ୍ୱାରା ସେଭ କରାଯାଇଛି"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ମିଟରଯୁକ୍ତ ନେଟୱାର୍କ ସହ ସଂଯୋଗ କରାଯାଇଛି"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ମାଧ୍ୟମରେ ଅଟୋମେଟିକାଲୀ ସଂଯୁକ୍ତ"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ନେଟୱର୍କ ମୂଲ୍ୟାୟନ ପ୍ରଦାତାଙ୍କ ମାଧ୍ୟମରେ ଅଟୋମେଟିକାଲ୍ୟ ସଂଯୁକ୍ତ"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ମାଧ୍ୟମରେ ସଂଯୁକ୍ତ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index f21c4ce..9483e06 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ਕੋਈ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ਵੱਲੋਂ ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ਮੀਟਰਬੱਧ ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ਰਾਹੀਂ ਆਪਣੇ-ਆਪ ਕਨੈਕਟ ਹੋਇਆ"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ਨੈੱਟਵਰਕ ਰੇਟਿੰਗ ਪ੍ਰਦਾਨਕ ਰਾਹੀਂ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਕਨੈਕਟ ਹੋਇਆ"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index c9c4a6c..c7f1595 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nie można połączyć automatycznie"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Brak dostępu do internetu"</string>
<string name="saved_network" msgid="7143698034077223645">"Zapisane przez: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Połączono z siecią z pomiarem użycia danych"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatycznie połączono przez: %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatycznie połączono przez dostawcę ocen jakości sieci"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Połączono przez %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 4b71085..63d8b8f 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nu se va conecta automat"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nu există acces la internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Salvată de <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"S-a conectat la o rețea contorizată"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectată automat prin %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectată automat prin furnizor de evaluări ale rețelei"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Conectată prin %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 4aeb985..ac43e49 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Подключение не будет выполняться автоматически"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Без доступа к Интернету"</string>
<string name="saved_network" msgid="7143698034077223645">"Кто сохранил: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Подключено к сети с ограниченным трафиком"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматически подключено к %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматически подключено через автора рейтинга сетей"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Подключено к %1$s"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 525d423..08fd9a0 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ස්වයංක්රිය නැවත සම්බන්ධ නොවනු ඇත"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"අන්තර්ජාල ප්රවේශය නැත"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> විසින් සුරකින ලදී"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"මනුගත ජාලයට සම්බන්ධ කර ඇත"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s හරහා ස්වයංක්රියව සම්බන්ධ විය"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ජාල ශ්රේණිගත සපයන්නා හරහා ස්වයංක්රියව සම්බන්ධ විය"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s හරහා සම්බන්ධ විය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index ee9ae6a..8f05684 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nedôjde k automatickému pripojeniu"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Žiadny prístup k internetu"</string>
<string name="saved_network" msgid="7143698034077223645">"Uložila aplikácia <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Pripojené k meranej sieti"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaticky pripojené prostredníctvom %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automaticky pripojené prostredníctvom poskytovateľa hodnotenia siete"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Pripojené prostredníctvom %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index ff60a32..3dccf3b 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Samodejna vnovična vzpostavitev povezave se ne bo izvedla"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ni dostopa do interneta"</string>
<string name="saved_network" msgid="7143698034077223645">"Shranil(-a): <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezano z omrežjem z omejenim prenosom podatkov"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Samodejno vzpostavljena povezava prek: %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Samodejno vzpostavljena povezava prek ponudnika ocenjevanja omrežij"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Vzpostavljena povezava prek: %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 78e6ed6..c09ad4c 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nuk do të lidhet automatikisht"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nuk ka qasje në internet"</string>
<string name="saved_network" msgid="7143698034077223645">"E ruajtur nga <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Lidhur me një rrjet me matje"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Lidhur automatikisht përmes %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Lidhur automatikisht nëpërmjet ofruesit të vlerësimit të rrjetit"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"E lidhur përmes %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 8bb9277..b5a91bf 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Аутоматско повезивање није успело"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Нема приступа интернету"</string>
<string name="saved_network" msgid="7143698034077223645">"Сачувао/ла је <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Повезани сте на мрежу са ограничењем"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Аутоматски повезано преко %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Аутоматски повезано преко добављача оцене мреже"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Веза је успостављена преко приступне тачке %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 03fb223..16a1be6 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Det går inte att ansluta automatiskt"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internetåtkomst"</string>
<string name="saved_network" msgid="7143698034077223645">"Sparades av <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Ansluten till nätverk med datapriser"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiskt ansluten via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatiskt ansluten via leverantör av nätverksbetyg"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Anslutet via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 631a413..58896c8 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Haiwezi kuunganisha kiotomatiki"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Hakuna muunganisho wa intaneti"</string>
<string name="saved_network" msgid="7143698034077223645">"Ilihifadhiwa na <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Imeunganishwa kwenye mtandao unaopima data"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Imeunganishwa kiotomatiki kupitia %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Imeunganishwa kiotomatiki kupitia mtoa huduma wa ukadiriaji wa mtandao"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Imeunganishwa kupitia %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 5796603..71cf7d4 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"தானாக இணைக்கப்படாது"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"இண்டர்நெட் அணுகல் இல்லை"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> சேமித்தது"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"கட்டண நெட்வொர்க்குடன் இணைக்கப்பட்டுள்ளது"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s மூலம் தானாக இணைக்கப்பட்டது"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"நெட்வொர்க் மதிப்பீடு வழங்குநரால் தானாக இணைக்கப்பட்டது"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s வழியாக இணைக்கப்பட்டது"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 9027ca1..c3a65e7 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"స్వయంచాలకంగా కనెక్ట్ కాదు"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ద్వారా సేవ్ చేయబడింది"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"డేటా నియంత్రణ నెట్వర్క్కు కనెక్ట్ చేయబడింది"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ద్వారా స్వయంచాలకంగా కనెక్ట్ చేయబడింది"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"నెట్వర్క్ రేటింగ్ ప్రదాత ద్వారా స్వయంచాలకంగా కనెక్ట్ చేయబడింది"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ద్వారా కనెక్ట్ చేయబడింది"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 8358dbb..903accb 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"จะไม่เชื่อมต่อโดยอัตโนมัติ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"เข้าถึงอินเทอร์เน็ตไม่ได้"</string>
<string name="saved_network" msgid="7143698034077223645">"บันทึกโดย<xliff:g id="NAME">%1$s</xliff:g> แล้ว"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"เชื่อมต่อกับเครือข่ายแบบจำกัดปริมาณแล้ว"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"เชื่อมต่ออัตโนมัติผ่าน %1$s แล้ว"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"เชื่อมต่ออัตโนมัติผ่านผู้ให้บริการการจัดอันดับเครือข่าย"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"เชื่อมต่อผ่าน %1$s แล้ว"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index d5250a4..b259201 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Hindi awtomatikong kokonekta"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Walang access sa internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Na-save ng <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Nakakonekta sa nakametrong network"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Awtomatikong nakakonekta sa pamamagitan ng %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Awtomatikong nakakonekta sa pamamagitan ng provider ng rating ng network"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Nakakonekta sa pamamagitan ng %1$s"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 8e0b889..a0bc362 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Otomatik olarak bağlanma"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"İnternet erişimi yok"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tarafından kaydedildi"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Sayaçlı ağa bağlı"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s üzerinden otomatik olarak bağlı"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ağ derecelendirme sağlayıcı aracılığıyla otomatik olarak bağlandı"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s üzerinden bağlı"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index c2f71c3..509c8a6 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не під’єднуватиметься автоматично"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Немає доступу до Інтернету"</string>
<string name="saved_network" msgid="7143698034077223645">"Збережено додатком <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Установлено з\'єднання з мережею з тарифікацією трафіку"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматично під’єднано через %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматично під’єднано через постачальника оцінки якості мережі"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Під’єднано через %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index a0c5f94..e097ab2 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"خودکار طور پر منسلک نہیں ہو گا"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"انٹرنیٹ تک کوئی رسائی نہیں"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> کی جانب سے محفوظ کردہ"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"میٹرڈ نیٹ ورک سے منسلک ہے"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s کے ذریعے از خود منسلک کردہ"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"نیٹ ورک درجہ بندی کے فراہم کنندہ کے ذریعے از خود منسلک"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"منسلک بذریعہ %1$s"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 1fb610b..8c2a95d 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Avtomatik ravishda ulanilmaydi"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Internet aloqasi yo‘q"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tomonidan saqlangan"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Trafik hisoblanadigan tarmoqqa ulandi"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s orqali avtomatik ulandi"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Tarmoqlar reytingi muallifi orqali avtomatik ulandi"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s orqali ulangan"</string>
@@ -156,7 +155,7 @@
<string name="running_process_item_user_label" msgid="3988506293099805796">"Foydalanuvchi: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="3631650616557252926">"Ba’zi birlamchi sozlamalar o‘rnatilgan"</string>
<string name="launch_defaults_none" msgid="8049374306261262709">"Birlamchi sozlamalar belgilanmagan"</string>
- <string name="tts_settings" msgid="8130616705989351312">"Nutq sintezi sozlamalari"</string>
+ <string name="tts_settings" msgid="8130616705989351312">"Matnni nutqqa aylantirish sozlamalari"</string>
<string name="tts_settings_title" msgid="7602210956640483039">"Nutq sintezi"</string>
<string name="tts_default_rate_title" msgid="3964187817364304022">"Nutq tezligi"</string>
<string name="tts_default_rate_summary" msgid="3781937042151716987">"Matnni o‘qish tezligi"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 1f3ea48..7a4d89b 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Sẽ không tự động kết nối"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Không có quyền truy cập Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Do <xliff:g id="NAME">%1$s</xliff:g> lưu"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Đã kết nối với mạng có đo lượng dữ liệu"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Tự động được kết nối qua %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Tự động được kết nối qua nhà cung cấp dịch vụ xếp hạng mạng"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Được kết nối qua %1$s"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 828d25f..7cd61fc 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"无法自动连接"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"无法访问互联网"</string>
<string name="saved_network" msgid="7143698034077223645">"由“<xliff:g id="NAME">%1$s</xliff:g>”保存"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"已连接到按流量计费的网络"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"已通过%1$s自动连接"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已自动连接(通过网络评分服务提供方)"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"已通过%1$s连接"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 54e1f41..458071c 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"不會自動連線"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"無法連接互聯網"</string>
<string name="saved_network" msgid="7143698034077223645">"由「<xliff:g id="NAME">%1$s</xliff:g>」儲存"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"已連結至按用量收費的網絡"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"已透過 %1$s 自動連線"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已透過網絡評分供應商自動連線"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"已透過 %1$s 連線"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index b8f1f58..867ed8b 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"無法自動連線"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"沒有可用的網際網路連線"</string>
<string name="saved_network" msgid="7143698034077223645">"由「<xliff:g id="NAME">%1$s</xliff:g>」儲存"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"已連線到計量付費網路"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"已透過 %1$s 自動連線"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已透過網路評分供應商自動連線"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"已透過 %1$s 連線"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index d680b66..e64dbd3 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -36,8 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Ngeke ize ixhumeke ngokuzenzakalela"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Akukho ukufinyelela kwe-inthanethi"</string>
<string name="saved_network" msgid="7143698034077223645">"Kulondolozwe ngu-<xliff:g id="NAME">%1$s</xliff:g>"</string>
- <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
- <skip />
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Kuxhunywe kunethiwekhi eyenziwe imitha"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Ixhumeke ngokuzenzakalela nge-%1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Kuxhunywe ngokuzenzakalelayo ngomhlinzeki wesilinganiso wenethiwekhi"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Kuxhumeke nge-%1$s"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
index b8030f1..4c7b898 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
@@ -259,7 +259,12 @@
.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode).append(',')
.append("dataState=").append(dataState).append(',')
.append("serviceState=").append(serviceState == null ? ""
- : serviceState.toString()).append(',')
+ : "mVoiceRegState=" + serviceState.getState() + "("
+ + ServiceState.rilServiceStateToString(serviceState.getState())
+ + ")" + ", mDataRegState=" + serviceState.getDataRegState() + "("
+ + ServiceState.rilServiceStateToString(
+ serviceState.getDataRegState()) + ")")
+ .append(',')
.append("signalStrength=").append(signalStrength == null ? ""
: signalStrength.toString()).append(',')
.append("telephonyDisplayInfo=").append(telephonyDisplayInfo == null ? ""
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 84dacfd..d10ff40 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/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index ad6a531..66165b6 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -147,10 +147,5 @@
VALIDATORS.put(Global.DEVELOPMENT_SETTINGS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.NOTIFICATION_FEEDBACK_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Global.RESTRICTED_NETWORKING_MODE, BOOLEAN_VALIDATOR);
- VALIDATORS.put(
- Global.ONE_HANDED_KEYGUARD_SIDE,
- new InclusiveIntegerRangeValidator(
- /* first= */Global.ONE_HANDED_KEYGUARD_SIDE_LEFT,
- /* last= */Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT));
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 44864a6..a6e2af9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -42,6 +42,7 @@
import android.provider.settings.validators.SecureSettingsValidators;
import android.provider.settings.validators.SystemSettingsValidators;
import android.provider.settings.validators.Validator;
+import android.telephony.SubscriptionManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.BackupUtils;
@@ -95,10 +96,11 @@
private static final String KEY_NETWORK_POLICIES = "network_policies";
private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
private static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config";
+ private static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings";
// Versioning of the state file. Increment this version
// number any time the set of state items is altered.
- private static final int STATE_VERSION = 8;
+ private static final int STATE_VERSION = 9;
// Versioning of the Network Policies backup payload.
private static final int NETWORK_POLICIES_BACKUP_VERSION = 1;
@@ -106,19 +108,20 @@
// Slots in the checksum array. Never insert new items in the middle
// of this array; new slots must be appended.
- private static final int STATE_SYSTEM = 0;
- private static final int STATE_SECURE = 1;
- private static final int STATE_LOCALE = 2;
- private static final int STATE_WIFI_SUPPLICANT = 3;
- private static final int STATE_WIFI_CONFIG = 4;
- private static final int STATE_GLOBAL = 5;
- private static final int STATE_LOCK_SETTINGS = 6;
- private static final int STATE_SOFTAP_CONFIG = 7;
- private static final int STATE_NETWORK_POLICIES = 8;
- private static final int STATE_WIFI_NEW_CONFIG = 9;
- private static final int STATE_DEVICE_CONFIG = 10;
+ private static final int STATE_SYSTEM = 0;
+ private static final int STATE_SECURE = 1;
+ private static final int STATE_LOCALE = 2;
+ private static final int STATE_WIFI_SUPPLICANT = 3;
+ private static final int STATE_WIFI_CONFIG = 4;
+ private static final int STATE_GLOBAL = 5;
+ private static final int STATE_LOCK_SETTINGS = 6;
+ private static final int STATE_SOFTAP_CONFIG = 7;
+ private static final int STATE_NETWORK_POLICIES = 8;
+ private static final int STATE_WIFI_NEW_CONFIG = 9;
+ private static final int STATE_DEVICE_CONFIG = 10;
+ private static final int STATE_SIM_SPECIFIC_SETTINGS = 11;
- private static final int STATE_SIZE = 11; // The current number of state items
+ private static final int STATE_SIZE = 12; // The current number of state items
// Number of entries in the checksum array at various version numbers
private static final int STATE_SIZES[] = {
@@ -130,7 +133,8 @@
8, // version 5 added STATE_SOFTAP_CONFIG
9, // version 6 added STATE_NETWORK_POLICIES
10, // version 7 added STATE_WIFI_NEW_CONFIG
- STATE_SIZE // version 8 added STATE_DEVICE_CONFIG
+ 11, // version 8 added STATE_DEVICE_CONFIG
+ STATE_SIZE // version 9 added STATE_SIM_SPECIFIC_SETTINGS
};
private static final int FULL_BACKUP_ADDED_GLOBAL = 2; // added the "global" entry
@@ -218,6 +222,7 @@
byte[] netPoliciesData = getNetworkPolicies();
byte[] wifiFullConfigData = getNewWifiConfigData();
byte[] deviceSpecificInformation = getDeviceSpecificConfiguration();
+ byte[] simSpecificSettingsData = getSimSpecificSettingsData();
long[] stateChecksums = readOldChecksums(oldState);
@@ -246,6 +251,9 @@
stateChecksums[STATE_DEVICE_CONFIG] =
writeIfChanged(stateChecksums[STATE_DEVICE_CONFIG], KEY_DEVICE_SPECIFIC_CONFIG,
deviceSpecificInformation, data);
+ stateChecksums[STATE_SIM_SPECIFIC_SETTINGS] =
+ writeIfChanged(stateChecksums[STATE_SIM_SPECIFIC_SETTINGS],
+ KEY_SIM_SPECIFIC_SETTINGS, simSpecificSettingsData, data);
writeNewChecksums(stateChecksums, newState);
}
@@ -386,6 +394,12 @@
preservedSettings);
break;
+ case KEY_SIM_SPECIFIC_SETTINGS:
+ byte[] restoredSimSpecificSettings = new byte[size];
+ data.readEntityData(restoredSimSpecificSettings, 0, size);
+ restoreSimSpecificSettings(restoredSimSpecificSettings);
+ break;
+
default :
data.skipEntityData();
@@ -1189,6 +1203,20 @@
return true;
}
+ private byte[] getSimSpecificSettingsData() {
+ SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
+ byte[] simSpecificData = subManager.getAllSimSpecificSettingsForBackup();
+ Log.i(TAG, "sim specific data of length + " + simSpecificData.length
+ + " successfully retrieved");
+
+ return simSpecificData;
+ }
+
+ private void restoreSimSpecificSettings(byte[] data) {
+ SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
+ subManager.restoreAllSimSpecificSettingsFromBackup(data);
+ }
+
private void updateWindowManagerIfNeeded(Integer previousDensity) {
int newDensity;
try {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index c7790fd..a1fd7ee 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 @@
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 @@
if (UserHandle.getAppId(Binder.getCallingUid()) < Process.FIRST_APPLICATION_UID) {
return;
}
+ checkReadableAnnotation(settingsType, settingName);
ApplicationInfo ai = getCallingApplicationInfoOrThrow();
if (!ai.isInstantApp()) {
return;
@@ -1932,6 +1951,41 @@
}
}
+ /**
+ * 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/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index c11877a..438cec8 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -283,7 +283,6 @@
Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS,
Settings.Global.EUICC_SWITCH_SLOT_TIMEOUT_MILLIS,
Settings.Global.FANCY_IME_ANIMATIONS,
- Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
Settings.Global.FORCED_APP_STANDBY_ENABLED,
Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 9021864..7bfb42b 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -76,8 +76,8 @@
"androidx.dynamicanimation_dynamicanimation",
"androidx-constraintlayout_constraintlayout",
"androidx.exifinterface_exifinterface",
- "kotlinx-coroutines-android",
- "kotlinx-coroutines-core",
+ "kotlinx_coroutines_android",
+ "kotlinx_coroutines",
"iconloader_base",
"SystemUI-tags",
"SystemUI-proto",
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
index b5f55af..7986809 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
@@ -24,7 +24,7 @@
<include
style="@style/BouncerSecurityContainer"
layout="@layout/keyguard_host_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
</FrameLayout>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index 35a2195..7a9e7c7 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-keyguard/layout/keyguard_host_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
index c75ee51..04e645b 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_host_view.xml
@@ -41,14 +41,13 @@
android:layout_gravity="center">
<com.android.keyguard.KeyguardSecurityViewFlipper
android:id="@+id/view_flipper"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingTop="@dimen/keyguard_security_view_top_margin"
android:paddingStart="@dimen/keyguard_security_view_lateral_margin"
android:paddingEnd="@dimen/keyguard_security_view_lateral_margin"
- android:layout_gravity="center"
android:gravity="center">
</com.android.keyguard.KeyguardSecurityViewFlipper>
</com.android.keyguard.KeyguardSecurityContainer>
diff --git a/packages/SystemUI/res-keyguard/values/config.xml b/packages/SystemUI/res-keyguard/values/config.xml
index 6176f7c..8d9d6ee 100644
--- a/packages/SystemUI/res-keyguard/values/config.xml
+++ b/packages/SystemUI/res-keyguard/values/config.xml
@@ -22,5 +22,4 @@
<!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] -->
<bool name="config_disableMenuKeyInLockScreen">false</bool>
- <bool name="can_use_one_handed_bouncer">false</bool>
</resources>
diff --git a/packages/SystemUI/res/drawable/controls_dialog_bg.xml b/packages/SystemUI/res/drawable/controls_dialog_bg.xml
new file mode 100644
index 0000000..cb4686dd
--- /dev/null
+++ b/packages/SystemUI/res/drawable/controls_dialog_bg.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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="?android:attr/colorBackgroundFloating" />
+ <corners android:radius="@dimen/notification_corner_radius" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/udfps_progress_bar.xml b/packages/SystemUI/res/drawable/udfps_progress_bar.xml
new file mode 100644
index 0000000..e5389f3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/udfps_progress_bar.xml
@@ -0,0 +1,44 @@
+<?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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background">
+ <shape
+ android:innerRadiusRatio="2.2"
+ android:shape="ring"
+ android:thickness="@dimen/udfps_enroll_progress_thickness"
+ android:useLevel="false"
+ android:tint="?android:colorControlNormal">
+ <solid android:color="@*android:color/white_disabled_material" />
+ </shape>
+ </item>
+ <item android:id="@android:id/progress">
+ <rotate
+ android:fromDegrees="270"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:toDegrees="270">
+ <shape
+ android:innerRadiusRatio="2.2"
+ android:shape="ring"
+ android:thickness="@dimen/udfps_enroll_progress_thickness"
+ android:tint="?android:attr/colorControlActivated">
+ <solid android:color="@android:color/white" />
+ </shape>
+ </rotate>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_in_dialog.xml b/packages/SystemUI/res/layout/controls_in_dialog.xml
new file mode 100644
index 0000000..983999f
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_in_dialog.xml
@@ -0,0 +1,45 @@
+<?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/control_detail_root"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginVertical="@dimen/controls_activity_view_top_offset"
+ android:layout_marginHorizontal="@dimen/controls_activity_view_side_offset"
+ android:padding="8dp"
+ android:orientation="vertical"
+ android:background="@drawable/controls_dialog_bg">
+
+ <com.android.systemui.globalactions.MinHeightScrollView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:scrollbars="none">
+
+ <LinearLayout
+ android:id="@+id/global_actions_controls"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:orientation="vertical"
+ android:clipToPadding="false" />
+
+ </com.android.systemui.globalactions.MinHeightScrollView>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 04de978..187ae58 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>
@@ -81,6 +84,17 @@
android:contentDescription="@string/accessibility_phone_button"
android:tint="?attr/wallpaperTextColor" />
+ <ImageView
+ android:id="@+id/alt_left_button"
+ android:layout_height="@dimen/keyguard_affordance_height"
+ android:layout_width="@dimen/keyguard_affordance_width"
+ android:layout_gravity="bottom|start"
+ android:scaleType="center"
+ android:tint="?attr/wallpaperTextColor"
+ android:layout_marginStart="24dp"
+ android:layout_marginBottom="48dp"
+ android:visibility="gone" />
+
<FrameLayout
android:id="@+id/overlay_container"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index 8f3345f..e2f3e2a 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -74,7 +74,7 @@
android:id="@+id/preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginBottom="24dp"
+ android:layout_marginBottom="42dp"
android:layout_marginHorizontal="48dp"
android:adjustViewBounds="true"
app:layout_constrainedHeight="true"
@@ -91,19 +91,33 @@
android:id="@+id/crop_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginBottom="24dp"
+ android:layout_marginBottom="42dp"
app:layout_constrainedHeight="true"
app:layout_constrainedWidth="true"
app:layout_constraintTop_toBottomOf="@id/guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
- app:handleThickness="3dp"
+ app:handleThickness="@dimen/screenshot_crop_handle_thickness"
app:handleColor="@*android:color/accent_device_default"
- app:scrimColor="#9444"
+ app:scrimColor="@color/screenshot_crop_scrim"
tools:background="?android:colorBackground"
tools:minHeight="100dp"
tools:minWidth="100dp" />
+ <com.android.systemui.screenshot.MagnifierView
+ android:id="@+id/magnifier"
+ android:visibility="invisible"
+ android:layout_width="200dp"
+ android:layout_height="200dp"
+ app:layout_constraintTop_toBottomOf="@id/guideline"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:handleThickness="@dimen/screenshot_crop_handle_thickness"
+ app:handleColor="@*android:color/accent_device_default"
+ app:scrimColor="@color/screenshot_crop_scrim"
+ app:borderThickness="4dp"
+ app:borderColor="#fff"
+ />
+
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
index c078805..6ae306e 100644
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -20,4 +20,17 @@
android:id="@+id/udfps_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- systemui:sensorTouchAreaCoefficient="0.5"/>
+ systemui:sensorTouchAreaCoefficient="0.5">
+
+ <!-- Enrollment progress bar-->
+ <com.android.systemui.biometrics.UdfpsProgressBar
+ android:id="@+id/progress_bar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:max="100"
+ android:padding="@dimen/udfps_enroll_progress_thickness"
+ android:progress="0"
+ android:layout_gravity="center"
+ android:visibility="gone"/>
+
+</com.android.systemui.biometrics.UdfpsView>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 6c55fb6..8166e35 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -178,6 +178,14 @@
<attr name="scrimColor" format="color" />
</declare-styleable>
+ <declare-styleable name="MagnifierView">
+ <attr name="handleThickness" format="dimension" />
+ <attr name="handleColor" format="color" />
+ <attr name="scrimColor" format="color" />
+ <attr name="borderThickness" format="dimension" />
+ <attr name="borderColor" format="color" />
+ </declare-styleable>
+
<declare-styleable name="RoundedCornerProgressDrawable">
<attr name="android:drawable" />
</declare-styleable>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index a7cf3e9..5fb6de7 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -197,6 +197,9 @@
<color name="global_screenshot_dismiss_foreground">@color/GM2_grey_500</color>
<color name="global_screenshot_background_protection_start">#40000000</color> <!-- 25% black -->
+ <!-- Long screenshot UI -->
+ <color name="screenshot_crop_scrim">#9444</color>
+
<!-- GM2 colors -->
<color name="GM2_grey_50">#F8F9FA</color>
<color name="GM2_grey_100">#F1F3F4</color>
@@ -261,6 +264,7 @@
<color name="control_enabled_cool_foreground">@color/GM2_blue_300</color>
<color name="control_thumbnail_tint">#33000000</color>
<color name="control_thumbnail_shadow_color">@*android:color/black</color>
+ <color name="controls_lockscreen_scrim">#AA000000</color>
<!-- Docked misalignment message -->
<color name="misalignment_text_color">#F28B82</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d92f4ea..12b8ccf 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>
@@ -345,6 +352,7 @@
<dimen name="screenshot_action_chip_padding_end">16dp</dimen>
<dimen name="screenshot_action_chip_text_size">14sp</dimen>
<dimen name="screenshot_dismissal_height_delta">80dp</dimen>
+ <dimen name="screenshot_crop_handle_thickness">3dp</dimen>
<!-- The width of the view containing navigation buttons -->
@@ -684,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. -->
@@ -1116,6 +1129,9 @@
<!-- Y translation for credential contents when animating in -->
<dimen name="biometric_dialog_credential_translation_offset">60dp</dimen>
+ <!-- UDFPS enrollment progress bar thickness -->
+ <dimen name="udfps_enroll_progress_thickness">12dp</dimen>
+
<!-- Wireless Charging Animation values -->
<dimen name="wireless_charging_dots_radius_start">0dp</dimen>
<dimen name="wireless_charging_dots_radius_end">4dp</dimen>
@@ -1153,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>
@@ -1259,6 +1275,7 @@
<!-- Home Controls activity view detail panel-->
<dimen name="controls_activity_view_top_offset">100dp</dimen>
+ <dimen name="controls_activity_view_side_offset">12dp</dimen>
<dimen name="controls_activity_view_text_size">17sp</dimen>
<dimen name="controls_activity_view_corner_radius">@*android:dimen/config_bottomDialogCornerRadius</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 01e54ff..ded8a2e 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -34,6 +34,9 @@
<bool name="flag_brightness_slider">false</bool>
+ <!-- The new animations to/from lockscreen and AOD! -->
+ <bool name="flag_lockscreen_animations">false</bool>
+
<!-- People Tile flag -->
<bool name="flag_conversations">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d932395..abcf4e8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -954,11 +954,8 @@
<string name="quick_settings_dark_mode_secondary_label_on_at">On at <xliff:g id="time" example="10 pm">%s</xliff:g></string>
<!-- QuickSettings: Secondary text for when the Dark theme or some other tile will be on until some user-selected time. [CHAR LIMIT=20] -->
<string name="quick_settings_dark_mode_secondary_label_until">Until <xliff:g id="time" example="7 am">%s</xliff:g></string>
- <!-- TODO(b/170970602): remove translatable=false when RBC has official name and strings -->
- <!-- QuickSettings: Label for the toggle that controls whether Reduce Bright Colors is enabled. [CHAR LIMIT=NONE] -->
- <string name="quick_settings_reduce_bright_colors_label" translatable="false">Reduce Bright Colors</string>
- <!-- QuickSettings: Secondary text for intensity level of Reduce Bright Colors. [CHAR LIMIT=NONE] -->
- <string name="quick_settings_reduce_bright_colors_secondary_label" translatable="false"> <xliff:g id="intensity" example="50">%d</xliff:g>%% reduction</string>
+ <!-- QuickSettings: Label for the toggle that controls whether Reduce Brightness is enabled. [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_reduce_bright_colors_label">Reduce Brightness</string>
<!-- QuickSettings: NFC tile [CHAR LIMIT=NONE] -->
<string name="quick_settings_nfc_label">NFC</string>
@@ -2256,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/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 4b04eeb..7c72548 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -662,6 +662,19 @@
<item name="android:windowExitAnimation">@anim/bottomsheet_out</item>
</style>
+ <style name="Theme.SystemUI.Dialog.Control.LockScreen" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
+ <item name="android:windowAnimationStyle">@style/Animation.Fade</item>
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:windowIsFloating">false</item>
+ <item name="android:windowBackground">@color/controls_lockscreen_scrim</item>
+ <item name="android:backgroundDimEnabled">true</item>
+ </style>
+
+ <style name="Animation.Fade">
+ <item name="android:windowEnterAnimation">@android:anim/fade_in</item>
+ <item name="android:windowExitAnimation">@android:anim/fade_out</item>
+ </style>
+
<style name="Control" />
<style name="Control.MenuItem">
@@ -752,4 +765,12 @@
<item name="android:textSize">14sp</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
</style>
+
+ <style name="UdfpsProgressBarStyle"
+ parent="android:style/Widget.Material.ProgressBar.Horizontal">
+ <item name="android:indeterminate">false</item>
+ <item name="android:max">10000</item>
+ <item name="android:mirrorForRtl">false</item>
+ <item name="android:progressDrawable">@drawable/udfps_progress_bar</item>
+ </style>
</resources>
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 e38cf23..5c943f6 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 @@
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/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
index 19e7d7e..bec9220 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
@@ -17,7 +17,6 @@
package com.android.systemui.shared.system;
import android.annotation.IntDef;
-import android.annotation.NonNull;
import android.view.View;
import com.android.internal.jank.InteractionJankMonitor;
@@ -37,6 +36,10 @@
InteractionJankMonitor.CUJ_LAUNCHER_APP_CLOSE_TO_PIP;
public static final int CUJ_QUICK_SWITCH =
InteractionJankMonitor.CUJ_LAUNCHER_QUICK_SWITCH;
+ public static final int CUJ_OPEN_ALL_APPS =
+ InteractionJankMonitor.CUJ_LAUNCHER_OPEN_ALL_APPS;
+ public static final int CUJ_ALL_APPS_SCROLL =
+ InteractionJankMonitor.CUJ_LAUNCHER_ALL_APPS_SCROLL;
@IntDef({
CUJ_APP_LAUNCH_FROM_RECENTS,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index a56c6a1..e6477f1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -18,9 +18,11 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.view.WindowManager.TransitionOldType;
import android.os.RemoteException;
import android.util.Log;
@@ -65,13 +67,17 @@
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
return new IRemoteAnimationRunner.Stub() {
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
final IRemoteAnimationFinishedCallback finishedCallback) {
final RemoteAnimationTargetCompat[] appsCompat =
RemoteAnimationTargetCompat.wrap(apps);
final RemoteAnimationTargetCompat[] wallpapersCompat =
RemoteAnimationTargetCompat.wrap(wallpapers);
+ final RemoteAnimationTargetCompat[] nonAppsCompat =
+ RemoteAnimationTargetCompat.wrap(nonApps);
final Runnable animationFinishedCallback = new Runnable() {
@Override
public void run() {
@@ -83,8 +89,8 @@
}
}
};
- remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat,
- animationFinishedCallback);
+ remoteAnimationAdapter.onAnimationStart(transit, appsCompat, wallpapersCompat,
+ nonAppsCompat, animationFinishedCallback);
}
@Override
@@ -104,6 +110,9 @@
RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */);
final RemoteAnimationTargetCompat[] wallpapersCompat =
RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */);
+ // TODO(bc-unlock): Build wrapped object for non-apps target.
+ final RemoteAnimationTargetCompat[] nonAppsCompat =
+ new RemoteAnimationTargetCompat[0];
final Runnable animationFinishedCallback = new Runnable() {
@Override
public void run() {
@@ -147,7 +156,10 @@
}
}
t.apply();
- remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat,
+ // TODO(bc-unlcok): Pass correct transit type.
+ remoteAnimationAdapter.onAnimationStart(
+ TRANSIT_OLD_NONE,
+ appsCompat, wallpapersCompat, nonAppsCompat,
animationFinishedCallback);
}
};
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
index 33372f6..0076292 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
@@ -16,8 +16,11 @@
package com.android.systemui.shared.system;
+import android.view.WindowManager;
+
public interface RemoteAnimationRunnerCompat {
- void onAnimationStart(RemoteAnimationTargetCompat[] apps,
- RemoteAnimationTargetCompat[] wallpapers, Runnable finishedCallback);
+ void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTargetCompat[] apps, RemoteAnimationTargetCompat[] wallpapers,
+ RemoteAnimationTargetCompat[] nonApps, Runnable finishedCallback);
void onAnimationCancelled();
}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
index 70021b6..fbabaa4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -114,10 +114,13 @@
for (int i = params.length - 1; i >= 0; i--) {
SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams =
params[i];
- t.deferTransactionUntil(surfaceParams.surface, mBarrierSurfaceControl, frame);
surfaceParams.applyTo(t);
}
- t.apply();
+ if (mTargetViewRootImpl != null) {
+ mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
+ } else {
+ t.apply();
+ }
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
.sendToTarget();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
index 4a28d56..89c60f1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
@@ -56,4 +56,12 @@
});
}
}
+
+ public void mergeWithNextTransaction(SurfaceControl.Transaction t, long frame) {
+ if (mViewRoot != null) {
+ mViewRoot.mergeWithNextTransaction(t, frame);
+ } else {
+ t.apply();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 59e81cf..0a117c1 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 @@
@Override
protected void onViewDetached() {
+ mBroadcastDispatcher.unregisterReceiver(mLocaleBroadcastReceiver);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
}
@@ -84,11 +120,23 @@
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 ca99563..64b3d73 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
@@ -44,12 +44,13 @@
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 @@
() -> {
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 @@
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 @@
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 e0de180..e375877 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -28,6 +28,7 @@
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 @@
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 @@
SysuiColorExtractor colorExtractor, ClockManager clockManager,
KeyguardSliceViewController keyguardSliceViewController,
NotificationIconAreaController notificationIconAreaController,
- ContentResolver contentResolver) {
+ ContentResolver contentResolver,
+ BroadcastDispatcher broadcastDispatcher) {
super(keyguardClockSwitch);
mResources = resources;
mStatusBarStateController = statusBarStateController;
@@ -109,6 +112,7 @@
mClockManager = clockManager;
mKeyguardSliceViewController = keyguardSliceViewController;
mNotificationIconAreaController = notificationIconAreaController;
+ mBroadcastDispatcher = broadcastDispatcher;
mTimeFormat = Settings.System.getString(contentResolver, Settings.System.TIME_12_24);
}
@@ -231,12 +235,14 @@
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/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 85e9ca0..276036c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -74,16 +74,7 @@
@Override
public void onDisplayChanged(int displayId) {
- if (displayId == DEFAULT_DISPLAY) return;
- final Presentation presentation = mPresentations.get(displayId);
- if (presentation != null && mShowing) {
- hidePresentation(displayId);
- // update DisplayInfo.
- final Display display = mDisplayService.getDisplay(displayId);
- if (display != null) {
- showPresentation(display);
- }
- }
+
}
@Override
@@ -305,15 +296,16 @@
}
@Override
+ public void onDisplayChanged() {
+ updateBounds();
+ getWindow().getDecorView().requestLayout();
+ }
+
+ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
- .getBounds();
- mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
- mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
- mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
- mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
+ updateBounds();
setContentView(LayoutInflater.from(mContext)
.inflate(R.layout.keyguard_presentation, null));
@@ -338,5 +330,14 @@
mKeyguardClockSwitchController.init();
}
+
+ private void updateBounds() {
+ final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
+ .getBounds();
+ mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
+ mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
+ mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
+ mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index c182fd1..5f6fd30 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -29,17 +29,12 @@
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
-import android.provider.Settings;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.util.TypedValue;
-import android.view.Gravity;
import android.view.MotionEvent;
-import android.view.OrientationEventListener;
import android.view.VelocityTracker;
-import android.view.View;
import android.view.ViewConfiguration;
-import android.view.ViewPropertyAnimator;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimationControlListener;
@@ -60,7 +55,6 @@
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import java.util.List;
@@ -105,12 +99,6 @@
private boolean mDisappearAnimRunning;
private SwipeListener mSwipeListener;
- private boolean mIsSecurityViewLeftAligned = true;
- private boolean mOneHandedMode = false;
- private SecurityMode mSecurityMode = SecurityMode.Invalid;
- private ViewPropertyAnimator mRunningOneHandedAnimator;
- private final OrientationEventListener mOrientationEventListener;
-
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
@@ -169,20 +157,16 @@
// Used to notify the container when something interesting happens.
public interface SecurityCallback {
boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen);
-
void userActivity();
-
void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
/**
- * @param strongAuth wheher the user has authenticated with strong authentication like
- * pattern, password or PIN but not by trust agents or fingerprint
+ * @param strongAuth wheher the user has authenticated with strong authentication like
+ * pattern, password or PIN but not by trust agents or fingerprint
* @param targetUserId a user that needs to be the foreground user at the finish completion.
*/
void finish(boolean strongAuth, int targetUserId);
-
void reset();
-
void onCancelClicked();
}
@@ -240,136 +224,12 @@
super(context, attrs, defStyle);
mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y);
mViewConfiguration = ViewConfiguration.get(context);
-
- mOrientationEventListener = new OrientationEventListener(context) {
- @Override
- public void onOrientationChanged(int orientation) {
- updateLayoutForSecurityMode(mSecurityMode);
- }
- };
}
void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
- mSecurityMode = securityMode;
mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
updateBiometricRetry(securityMode, faceAuthEnabled);
- updateLayoutForSecurityMode(securityMode);
- mOrientationEventListener.enable();
- }
-
- void updateLayoutForSecurityMode(SecurityMode securityMode) {
- mSecurityMode = securityMode;
- mOneHandedMode = canUseOneHandedBouncer();
-
- if (mOneHandedMode) {
- mIsSecurityViewLeftAligned = isOneHandedKeyguardLeftAligned(mContext);
- }
-
- updateSecurityViewGravity();
- updateSecurityViewLocation(false);
- }
-
- /** Return whether the one-handed keyguard should be enabled. */
- private boolean canUseOneHandedBouncer() {
- // Is it enabled?
- if (!getResources().getBoolean(
- com.android.internal.R.bool.config_enableOneHandedKeyguard)) {
- return false;
- }
-
- if (!KeyguardSecurityModel.isSecurityViewOneHanded(mSecurityMode)) {
- return false;
- }
-
- return getResources().getBoolean(R.bool.can_use_one_handed_bouncer);
- }
-
- /** Read whether the one-handed keyguard should be on the left/right from settings. */
- private boolean isOneHandedKeyguardLeftAligned(Context context) {
- try {
- return Settings.Global.getInt(context.getContentResolver(),
- Settings.Global.ONE_HANDED_KEYGUARD_SIDE)
- == Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
- } catch (Settings.SettingNotFoundException ex) {
- return true;
- }
- }
-
- private void updateSecurityViewGravity() {
- View securityView = findKeyguardSecurityView();
-
- if (securityView == null) {
- return;
- }
-
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) securityView.getLayoutParams();
-
- if (mOneHandedMode) {
- lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
- } else {
- lp.gravity = Gravity.CENTER;
- }
-
- securityView.setLayoutParams(lp);
- }
-
- /**
- * Moves the inner security view to the correct location (in one handed mode) with animation.
- * This is triggered when the user taps on the side of the screen that is not currently occupied
- * by the security view .
- */
- private void updateSecurityViewLocation(boolean animate) {
- View securityView = findKeyguardSecurityView();
-
- if (securityView == null) {
- return;
- }
-
- if (!mOneHandedMode) {
- securityView.setTranslationX(0);
- return;
- }
-
- if (mRunningOneHandedAnimator != null) {
- mRunningOneHandedAnimator.cancel();
- mRunningOneHandedAnimator = null;
- }
-
- int targetTranslation = mIsSecurityViewLeftAligned ? 0 : (int) (getMeasuredWidth() / 2f);
-
- if (animate) {
- mRunningOneHandedAnimator = securityView.animate().translationX(targetTranslation);
- mRunningOneHandedAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- mRunningOneHandedAnimator.setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mRunningOneHandedAnimator = null;
- }
- });
-
- mRunningOneHandedAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- mRunningOneHandedAnimator.start();
- } else {
- securityView.setTranslationX(targetTranslation);
- }
- }
-
- @Nullable
- private KeyguardSecurityViewFlipper findKeyguardSecurityView() {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
-
- if (isKeyguardSecurityView(child)) {
- return (KeyguardSecurityViewFlipper) child;
- }
- }
-
- return null;
- }
-
- private boolean isKeyguardSecurityView(View view) {
- return view instanceof KeyguardSecurityViewFlipper;
}
public void onPause() {
@@ -378,7 +238,6 @@
mAlertDialog = null;
}
mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
- mOrientationEventListener.disable();
}
@Override
@@ -460,44 +319,19 @@
if (mSwipeListener != null) {
mSwipeListener.onSwipeUp();
}
- } else {
- if (!mIsDragging) {
- handleTap(event);
- }
}
}
return true;
}
- private void handleTap(MotionEvent event) {
- // If we're using a fullscreen security mode, skip
- if (!mOneHandedMode) {
- return;
- }
-
- // Did the tap hit the "other" side of the bouncer?
- if ((mIsSecurityViewLeftAligned && (event.getX() > getWidth() / 2f))
- || (!mIsSecurityViewLeftAligned && (event.getX() < getWidth() / 2f))) {
- mIsSecurityViewLeftAligned = !mIsSecurityViewLeftAligned;
-
- Settings.Global.putInt(
- mContext.getContentResolver(),
- Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
- mIsSecurityViewLeftAligned ? Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT
- : Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT);
-
- updateSecurityViewLocation(true);
- }
- }
-
void setSwipeListener(SwipeListener swipeListener) {
mSwipeListener = swipeListener;
}
private void startSpringAnimation(float startVelocity) {
mSpringAnimation
- .setStartVelocity(startVelocity)
- .animateToFinalPosition(0);
+ .setStartVelocity(startVelocity)
+ .animateToFinalPosition(0);
}
public void startDisappearAnimation(SecurityMode securitySelection) {
@@ -607,17 +441,18 @@
return insets.inset(0, 0, 0, inset);
}
+
private void showDialog(String title, String message) {
if (mAlertDialog != null) {
mAlertDialog.dismiss();
}
mAlertDialog = new AlertDialog.Builder(mContext)
- .setTitle(title)
- .setMessage(message)
- .setCancelable(false)
- .setNeutralButton(R.string.ok, null)
- .create();
+ .setTitle(title)
+ .setMessage(message)
+ .setCancelable(false)
+ .setNeutralButton(R.string.ok, null)
+ .create();
if (!(mContext instanceof Activity)) {
mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
}
@@ -655,44 +490,6 @@
}
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int maxHeight = 0;
- int maxWidth = 0;
- int childState = 0;
-
- int halfWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
- MeasureSpec.getSize(widthMeasureSpec) / 2,
- MeasureSpec.getMode(widthMeasureSpec));
-
- for (int i = 0; i < getChildCount(); i++) {
- final View view = getChildAt(i);
- if (view.getVisibility() != GONE) {
- if (mOneHandedMode && isKeyguardSecurityView(view)) {
- measureChildWithMargins(view, halfWidthMeasureSpec, 0,
- heightMeasureSpec, 0);
- } else {
- measureChildWithMargins(view, widthMeasureSpec, 0,
- heightMeasureSpec, 0);
- }
- final LayoutParams lp = (LayoutParams) view.getLayoutParams();
- maxWidth = Math.max(maxWidth,
- view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
- maxHeight = Math.max(maxHeight,
- view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
- childState = combineMeasuredStates(childState, view.getMeasuredState());
- }
- }
-
- // Check against our minimum height and width
- maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
- maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
-
- setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
- resolveSizeAndState(maxHeight, heightMeasureSpec,
- childState << MEASURED_HEIGHT_STATE_SHIFT));
- }
-
void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
String message = null;
switch (userType) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index fdab8db..1a8d420 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -404,7 +404,6 @@
if (newView != null) {
newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
mSecurityViewFlipperController.show(newView);
- mView.updateLayoutForSecurityMode(securityMode);
}
mSecurityCallback.onSecurityModeChanged(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
index 631c248..c77c867 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -92,13 +92,4 @@
throw new IllegalStateException("Unknown security quality:" + security);
}
}
-
- /**
- * Returns whether the given security view should be used in a "one handed" way. This can be
- * used to change how the security view is drawn (e.g. take up less of the screen, and align to
- * one side).
- */
- public static boolean isSecurityViewOneHanded(SecurityMode securityMode) {
- return securityMode == SecurityMode.Pattern || securityMode == SecurityMode.PIN;
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 97aa26f..fea152a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -56,8 +56,10 @@
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 @@
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 @@
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 @@
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 973b493..a5f364d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -134,7 +134,7 @@
}
/**
- * 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 @@
// 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 f2d36d1..5735a4f 100644
--- a/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt
+++ b/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt
@@ -54,11 +54,10 @@
*/
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 @@
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 @@
}
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 0d41a2f..5d5797c 100644
--- a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
+++ b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
@@ -30,8 +30,7 @@
* Provide text style linear interpolation for plain text.
*/
class TextInterpolator(
- layout: Layout,
- lines: Int = 1
+ layout: Layout
) {
/**
@@ -40,11 +39,9 @@
* 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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
}
// 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 @@
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/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 055270d..7d06dd6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -23,7 +23,6 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
-import android.app.IActivityTaskManager;
import android.app.TaskStackListener;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -137,7 +136,8 @@
mActivityTaskManager.getTasks(1);
if (!runningTasks.isEmpty()) {
final String topPackage = runningTasks.get(0).topActivity.getPackageName();
- if (!topPackage.contentEquals(clientPackage)) {
+ if (!topPackage.contentEquals(clientPackage)
+ && !Utils.isSystem(mContext, clientPackage)) {
Log.w(TAG, "Evicting client due to: " + topPackage);
mCurrentDialog.dismissWithoutCallback(true /* animate */);
mCurrentDialog = null;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
index e07c8403..5290986 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
@@ -38,6 +38,7 @@
private static final String TAG = "UdfpsAnimationEnroll";
private static final float SHADOW_RADIUS = 5.f;
+ private static final float PROGRESS_BAR_RADIUS = 140.f;
@Nullable private RectF mSensorRect;
@NonNull private final Paint mSensorPaint;
@@ -81,12 +82,12 @@
@Override
public int getPaddingX() {
- return (int) Math.ceil(SHADOW_RADIUS);
+ return (int) Math.ceil(PROGRESS_BAR_RADIUS);
}
@Override
public int getPaddingY() {
- return (int) Math.ceil(SHADOW_RADIUS);
+ return (int) Math.ceil(PROGRESS_BAR_RADIUS);
}
@Override
@@ -104,12 +105,4 @@
public int getOpacity() {
return 0;
}
-
- public void onEnrollmentProgress(int remaining) {
- Log.d(TAG, "Remaining: " + remaining);
- }
-
- public void onEnrollmentHelp() {
- Log.d(TAG, "onEnrollmentHelp");
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index 4e3419e..41ea4d6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -103,18 +103,6 @@
postInvalidate();
}
- void onEnrollmentProgress(int remaining) {
- if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) {
- ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentProgress(remaining);
- }
- }
-
- void onEnrollmentHelp() {
- if (mUdfpsAnimation instanceof UdfpsAnimationEnroll) {
- ((UdfpsAnimationEnroll) mUdfpsAnimation).onEnrollmentHelp();
- }
- }
-
public int getPaddingX() {
if (mUdfpsAnimation == null) {
return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index baa5973..e7b08e7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -84,6 +84,7 @@
private boolean mIsOverlayRequested;
// Reason the overlay has been requested. See IUdfpsOverlayController for definitions.
private int mRequestReason;
+ @Nullable UdfpsEnrollHelper mEnrollHelper;
// The fingerprint AOD trigger doesn't provide an ACTION_UP/ACTION_CANCEL event to tell us when
// to turn off high brightness mode. To get around this limitation, the state of the AOD
@@ -95,6 +96,12 @@
public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
@Override
public void showUdfpsOverlay(int sensorId, int reason) {
+ if (reason == IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR
+ || reason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING) {
+ mEnrollHelper = new UdfpsEnrollHelper(reason);
+ } else {
+ mEnrollHelper = null;
+ }
UdfpsController.this.showOverlay(reason);
}
@@ -260,15 +267,17 @@
// Transform dimensions if the device is in landscape mode.
switch (mContext.getDisplay().getRotation()) {
case Surface.ROTATION_90:
- mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius;
- mCoreLayoutParams.y =
- p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius;
+ mCoreLayoutParams.x = mSensorProps.sensorLocationY - mSensorProps.sensorRadius
+ - paddingX;
+ mCoreLayoutParams.y = p.y - mSensorProps.sensorLocationX - mSensorProps.sensorRadius
+ - paddingY;
break;
case Surface.ROTATION_270:
- mCoreLayoutParams.x =
- p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius;
- mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius;
+ mCoreLayoutParams.x = p.x - mSensorProps.sensorLocationY - mSensorProps.sensorRadius
+ - paddingX;
+ mCoreLayoutParams.y = mSensorProps.sensorLocationX - mSensorProps.sensorRadius
+ - paddingY;
break;
default:
@@ -294,7 +303,7 @@
try {
Log.v(TAG, "showUdfpsOverlay | adding window");
final UdfpsAnimation animation = getUdfpsAnimationForReason(reason);
- mView.setUdfpsAnimation(animation);
+ mView.setExtras(animation, mEnrollHelper);
mWindowManager.addView(mView, computeLayoutParams(animation));
mView.setOnTouchListener(mOnTouchListener);
mIsOverlayShowing = true;
@@ -311,7 +320,8 @@
private UdfpsAnimation getUdfpsAnimationForReason(int reason) {
Log.d(TAG, "getUdfpsAnimationForReason: " + reason);
switch (reason) {
- case IUdfpsOverlayController.REASON_ENROLL:
+ case IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR:
+ case IUdfpsOverlayController.REASON_ENROLL_ENROLLING:
return new UdfpsAnimationEnroll(mContext);
case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD:
return new UdfpsAnimationKeyguard(mView, mContext, mStatusBarStateController);
@@ -327,7 +337,7 @@
mFgExecutor.execute(() -> {
if (mIsOverlayShowing) {
Log.v(TAG, "hideUdfpsOverlay | removing window");
- mView.setUdfpsAnimation(null);
+ mView.setExtras(null, null);
mView.setOnTouchListener(null);
// Reset the controller back to its starting state.
onFingerUp();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
new file mode 100644
index 0000000..2442633
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -0,0 +1,60 @@
+/*
+ * 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.biometrics;
+
+import android.hardware.fingerprint.IUdfpsOverlayController;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Helps keep track of enrollment state and animates the progress bar accordingly.
+ */
+public class UdfpsEnrollHelper {
+ private static final String TAG = "UdfpsEnrollHelper";
+
+ // IUdfpsOverlayController reason
+ private final int mEnrollReason;
+
+ private int mTotalSteps = -1;
+ private int mCurrentProgress = 0;
+
+ public UdfpsEnrollHelper(int reason) {
+ mEnrollReason = reason;
+ }
+
+ boolean shouldShowProgressBar() {
+ return mEnrollReason == IUdfpsOverlayController.REASON_ENROLL_ENROLLING;
+ }
+
+ void onEnrollmentProgress(int remaining, @NonNull UdfpsProgressBar progressBar) {
+ if (mTotalSteps == -1) {
+ mTotalSteps = remaining;
+ }
+
+ mCurrentProgress = progressBar.getMax() * Math.max(0, mTotalSteps + 1 - remaining)
+ / (mTotalSteps + 1);
+ progressBar.setProgress(mCurrentProgress, true /* animate */);
+ }
+
+ void updateProgress(@NonNull UdfpsProgressBar progressBar) {
+ progressBar.setProgress(mCurrentProgress);
+ }
+
+ void onEnrollmentHelp() {
+
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java
new file mode 100644
index 0000000..84e2fab
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsProgressBar.java
@@ -0,0 +1,59 @@
+/*
+ * 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.biometrics;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ProgressBar;
+
+import com.android.systemui.R;
+
+/**
+ * A (determinate) progress bar in the form of a ring. The progress bar goes clockwise starting
+ * from the 12 o'clock position. This view maintain equal width and height using a strategy similar
+ * to "centerInside" for ImageView.
+ */
+public class UdfpsProgressBar extends ProgressBar {
+
+ public UdfpsProgressBar(Context context) {
+ this(context, null);
+ }
+
+ public UdfpsProgressBar(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public UdfpsProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, R.style.UdfpsProgressBarStyle);
+ }
+
+ public UdfpsProgressBar(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ final int measuredHeight = getMeasuredHeight();
+ final int measuredWidth = getMeasuredWidth();
+
+ final int length = Math.min(measuredHeight, measuredWidth);
+ setMeasuredDimension(length, length);
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index 7e378d3..00cb28b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -56,6 +56,8 @@
@NonNull private final RectF mSensorRect;
@NonNull private final Paint mDebugTextPaint;
+ @Nullable private UdfpsProgressBar mProgressBar;
+
// Used to obtain the sensor location.
@NonNull private FingerprintSensorPropertiesInternal mSensorProps;
@@ -64,6 +66,7 @@
private boolean mIlluminationRequested;
private int mStatusBarState;
private boolean mNotificationShadeExpanded;
+ @Nullable private UdfpsEnrollHelper mEnrollHelper;
public UdfpsView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -108,8 +111,17 @@
mSensorProps = properties;
}
- void setUdfpsAnimation(@Nullable UdfpsAnimation animation) {
+ void setExtras(@Nullable UdfpsAnimation animation, @Nullable UdfpsEnrollHelper enrollHelper) {
mAnimationView.setAnimation(animation);
+ mEnrollHelper = enrollHelper;
+
+ if (enrollHelper != null) {
+ mEnrollHelper.updateProgress(mProgressBar);
+ mProgressBar.setVisibility(enrollHelper.shouldShowProgressBar()
+ ? View.VISIBLE : View.GONE);
+ } else {
+ mProgressBar.setVisibility(View.GONE);
+ }
}
@Override
@@ -138,6 +150,11 @@
}
@Override
+ protected void onFinishInflate() {
+ mProgressBar = findViewById(R.id.progress_bar);
+ }
+
+ @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mSensorRect.set(0 + mAnimationView.getPaddingX(),
@@ -233,10 +250,10 @@
}
void onEnrollmentProgress(int remaining) {
- mAnimationView.onEnrollmentProgress(remaining);
+ mEnrollHelper.onEnrollmentProgress(remaining, mProgressBar);
}
void onEnrollmentHelp() {
- mAnimationView.onEnrollmentHelp();
+
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
index fd5e85a..076c7cb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java
@@ -16,13 +16,16 @@
package com.android.systemui.biometrics;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.hardware.biometrics.BiometricManager.Authenticators;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.biometrics.PromptInfo;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.os.UserManager;
@@ -116,4 +119,10 @@
return false;
}
+
+ static boolean isSystem(@NonNull Context context, @Nullable String clientPackage) {
+ final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
+ == PackageManager.PERMISSION_GRANTED;
+ return hasPermission && "android".equals(clientPackage);
+ }
}
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 429f67f..d06568a 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 @@
*/
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 7cd7e18..247f25e 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.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.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 @@
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 @@
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 @@
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
new file mode 100644
index 0000000..f533cfb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialog.kt
@@ -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.systemui.controls.ui
+
+import android.app.Dialog
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
+
+import com.android.systemui.Interpolators
+import com.android.systemui.R
+import com.android.systemui.broadcast.BroadcastDispatcher
+
+/**
+ * Show the controls space inside a dialog, as from the lock screen.
+ */
+class ControlsDialog(
+ thisContext: Context,
+ val broadcastDispatcher: BroadcastDispatcher
+) : Dialog(thisContext, R.style.Theme_SystemUI_Dialog_Control_LockScreen) {
+
+ private val receiver = object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val action = intent.getAction()
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
+ dismiss()
+ }
+ }
+ }
+
+ init {
+ window.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
+ window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+ or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
+
+ setContentView(R.layout.controls_in_dialog)
+
+ requireViewById<ViewGroup>(R.id.control_detail_root).apply {
+ setOnClickListener { dismiss() }
+ (getParent() as View).setOnClickListener { dismiss() }
+ }
+ }
+
+ fun show(
+ controller: ControlsUiController
+ ): ControlsDialog {
+ super.show()
+
+ val vg = requireViewById<ViewGroup>(com.android.systemui.R.id.global_actions_controls)
+ vg.alpha = 0f
+ controller.show(vg, { /* do nothing */ }, false /* startedFromGlobalActions */)
+
+ vg.animate()
+ .alpha(1f)
+ .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
+ .setDuration(300)
+
+ val filter = IntentFilter()
+ filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
+ broadcastDispatcher.registerReceiver(receiver, filter)
+
+ return this
+ }
+
+ override fun dismiss() {
+ broadcastDispatcher.unregisterReceiver(receiver)
+
+ if (!isShowing()) return
+
+ super.dismiss()
+ }
+}
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 4e4c82c..9448877 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 @@
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 2b529f9..762362c 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 @@
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 @@
}
}
- 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 @@
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 @@
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 ec4a91c..2b362b9 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 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 @@
@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/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 062410a..ffb8446 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -33,7 +33,7 @@
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.RemoteTransitions;
import java.util.Optional;
@@ -86,7 +86,7 @@
Builder setShellCommandHandler(Optional<ShellCommandHandler> shellDump);
@BindsInstance
- Builder setTransitions(Transitions t);
+ Builder setTransitions(RemoteTransitions t);
SysUIComponent build();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 60b665f..84dd259 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -27,7 +27,7 @@
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.RemoteTransitions;
import java.util.Optional;
@@ -55,16 +55,12 @@
getShellInit().init();
}
- // Gets the Shell init instance
@WMSingleton
ShellInit getShellInit();
- // Gets the Shell dump instance
@WMSingleton
Optional<ShellCommandHandler> getShellCommandHandler();
- // TODO(b/162923491): We currently pass the instances through to SysUI, but that may change
- // depending on the threading mechanism we go with
@WMSingleton
Optional<OneHanded> getOneHanded();
@@ -89,7 +85,6 @@
@WMSingleton
Optional<TaskViewFactory> getTaskViewFactory();
- /** Gets transitions */
@WMSingleton
- Transitions getTransitions();
+ RemoteTransitions getTransitions();
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index ad4c447..8af45a5 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 @@
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 @@
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 @@
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 0000000..3a06f7a
--- /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 0000000..8c04143
--- /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/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 1b033e9..17f7ccf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -17,7 +17,13 @@
package com.android.systemui.keyguard;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
+import android.app.ActivityTaskManager;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
@@ -26,8 +32,17 @@
import android.os.IBinder;
import android.os.PowerManager;
import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.Trace;
import android.util.Log;
+import android.util.Slog;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
+import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -43,6 +58,21 @@
static final String TAG = "KeyguardService";
static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD;
+ /**
+ * Run Keyguard animation as remote animation in System UI instead of local animation in
+ * the server process.
+ *
+ * Note: Must be consistent with WindowManagerService.
+ */
+ private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
+ "persist.wm.enable_remote_keyguard_animation";
+
+ /**
+ * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
+ */
+ private static boolean sEnableRemoteKeyguardAnimation =
+ SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
+
private final KeyguardViewMediator mKeyguardViewMediator;
private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
@@ -52,6 +82,21 @@
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
+
+ if (sEnableRemoteKeyguardAnimation) {
+ RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
+ final RemoteAnimationAdapter exitAnimationAdapter =
+ new RemoteAnimationAdapter(mExitAnimationRunner, 0, 0);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, exitAnimationAdapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+ exitAnimationAdapter);
+ final RemoteAnimationAdapter occludeAnimationAdapter =
+ new RemoteAnimationAdapter(mOccludeAnimationRunner, 0, 0);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE, occludeAnimationAdapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_UNOCCLUDE, occludeAnimationAdapter);
+ ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay(
+ DEFAULT_DISPLAY, definition);
+ }
}
@Override
@@ -76,6 +121,48 @@
}
}
+ private final IRemoteAnimationRunner.Stub mExitAnimationRunner =
+ new IRemoteAnimationRunner.Stub() {
+ @Override // Binder interface
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ Trace.beginSection("KeyguardService.mBinder#startKeyguardExitAnimation");
+ checkPermission();
+ mKeyguardViewMediator.startKeyguardExitAnimation(transit, apps, wallpapers,
+ null /* nonApps */, finishedCallback);
+ Trace.endSection();
+ }
+
+ @Override // Binder interface
+ public void onAnimationCancelled() {
+ }
+ };
+
+ private final IRemoteAnimationRunner.Stub mOccludeAnimationRunner =
+ new IRemoteAnimationRunner.Stub() {
+ @Override // Binder interface
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ // TODO(bc-unlock): Calls KeyguardViewMediator#setOccluded to update the state and
+ // run animation.
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException");
+ }
+ }
+
+ @Override // Binder interface
+ public void onAnimationCancelled() {
+ }
+ };
+
private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
@Override // Binder interface
@@ -225,6 +312,11 @@
mKeyguardViewMediator.onBootCompleted();
}
+ /**
+ * @deprecated When remote animation is enabled, this won't be called anymore. Use
+ * {@code IRemoteAnimationRunner#onAnimationStart} instead.
+ */
+ @Deprecated
@Override
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
Trace.beginSection("KeyguardService.mBinder#startKeyguardExitAnimation");
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e732669..c55fdf4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -27,6 +27,9 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.AlarmManager;
@@ -64,9 +67,15 @@
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;
+import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -85,6 +94,7 @@
import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.Dumpable;
+import com.android.systemui.Interpolators;
import com.android.systemui.SystemUI;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
@@ -297,6 +307,13 @@
*/
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;
@@ -468,10 +485,10 @@
}
}
- 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);
}
@@ -495,17 +512,19 @@
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 "
@@ -532,9 +551,10 @@
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();
}
}
@@ -1318,6 +1338,7 @@
if (mHiding && isOccluded) {
// We're in the process of going away but WindowManager wants to show a
// SHOW_WHEN_LOCKED activity instead.
+ // TODO(bc-unlock): Migrate to remote animation.
startKeyguardExitAnimation(0, 0);
}
@@ -1703,7 +1724,9 @@
Trace.beginSection(
"KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM");
StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj;
- handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration);
+ handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration,
+ params.mApps, params.mWallpapers, params.mNonApps,
+ params.mFinishedCallback);
mFalsingCollector.onSuccessfulUnlock();
Trace.endSection();
break;
@@ -1990,15 +2013,19 @@
if (mShowing && !mOccluded) {
mKeyguardGoingAwayRunnable.run();
} else {
+ // TODO(bc-unlock): Fill parameters
handleStartKeyguardExitAnimation(
SystemClock.uptimeMillis() + mHideAnimation.getStartOffset(),
- mHideAnimation.getDuration());
+ mHideAnimation.getDuration(), null /* apps */, null /* wallpapers */,
+ null /* nonApps */, null /* finishedCallback */);
}
}
Trace.endSection();
}
- private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+ private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration,
+ RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) {
Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation");
if (DEBUG) Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime
+ " fadeoutDuration=" + fadeoutDuration);
@@ -2031,6 +2058,49 @@
mWakeAndUnlocking = false;
mDismissCallbackRegistry.notifyDismissSucceeded();
mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);
+
+ // TODO(bc-animation): When remote animation is enabled for keyguard exit animation,
+ // apps, wallpapers and finishedCallback are set to non-null. nonApps is not yet
+ // supported, so it's always null.
+ mContext.getMainExecutor().execute(() -> {
+ if (finishedCallback == null) {
+ return;
+ }
+
+ // TODO(bc-unlock): Sample animation, just to apply alpha animation on the app.
+ final SyncRtSurfaceTransactionApplier applier = new SyncRtSurfaceTransactionApplier(
+ mKeyguardViewControllerLazy.get().getViewRootImpl().getView());
+ final RemoteAnimationTarget primary = apps[0];
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+ anim.setDuration(400 /* duration */);
+ anim.setInterpolator(Interpolators.LINEAR);
+ anim.addUpdateListener((ValueAnimator animation) -> {
+ SurfaceParams params = new SurfaceParams.Builder(primary.leash)
+ .withAlpha(animation.getAnimatedFraction())
+ .build();
+ applier.scheduleApply(params);
+ });
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException");
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "RemoteException");
+ }
+ }
+ });
+ anim.start();
+ });
resetKeyguardDonePendingLocked();
mHideAnimationRun = false;
adjustStatusBarLocked();
@@ -2223,10 +2293,55 @@
return mKeyguardViewControllerLazy.get();
}
+ /**
+ * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+ * the wallpaper and keyguard flag, and WindowManager has started running keyguard exit
+ * animation.
+ *
+ * @param startTime the start time of the animation in uptime milliseconds. Deprecated.
+ * @param fadeoutDuration the duration of the exit animation, in milliseconds Deprecated.
+ * @deprecated Will be migrate to remote animation soon.
+ */
+ @Deprecated
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+ startKeyguardExitAnimation(0, startTime, fadeoutDuration, null, null, null, null);
+ }
+
+ /**
+ * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+ * the wallpaper and keyguard flag, and System UI should start running keyguard exit animation.
+ *
+ * @param apps The list of apps to animate.
+ * @param wallpapers The list of wallpapers to animate.
+ * @param nonApps The list of non-app windows such as Bubbles to animate.
+ * @param finishedCallback The callback to invoke when the animation is finished.
+ */
+ public void startKeyguardExitAnimation(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ startKeyguardExitAnimation(transit, 0, 0, apps, wallpapers, nonApps, finishedCallback);
+ }
+
+ /**
+ * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+ * the wallpaper and keyguard flag, and start running keyguard exit animation.
+ *
+ * @param startTime the start time of the animation in uptime milliseconds. Deprecated.
+ * @param fadeoutDuration the duration of the exit animation, in milliseconds Deprecated.
+ * @param apps The list of apps to animate.
+ * @param wallpapers The list of wallpapers to animate.
+ * @param nonApps The list of non-app windows such as Bubbles to animate.
+ * @param finishedCallback The callback to invoke when the animation is finished.
+ */
+ private void startKeyguardExitAnimation(@WindowManager.TransitionOldType int transit,
+ long startTime, long fadeoutDuration,
+ RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) {
Trace.beginSection("KeyguardViewMediator#startKeyguardExitAnimation");
Message msg = mHandler.obtainMessage(START_KEYGUARD_EXIT_ANIM,
- new StartKeyguardExitAnimParams(startTime, fadeoutDuration));
+ new StartKeyguardExitAnimParams(transit, startTime, fadeoutDuration, apps,
+ wallpapers, nonApps, finishedCallback));
mHandler.sendMessage(msg);
Trace.endSection();
}
@@ -2300,12 +2415,26 @@
private static class StartKeyguardExitAnimParams {
+ @WindowManager.TransitionOldType int mTransit;
long startTime;
long fadeoutDuration;
+ RemoteAnimationTarget[] mApps;
+ RemoteAnimationTarget[] mWallpapers;
+ RemoteAnimationTarget[] mNonApps;
+ IRemoteAnimationFinishedCallback mFinishedCallback;
- private StartKeyguardExitAnimParams(long startTime, long fadeoutDuration) {
+ private StartKeyguardExitAnimParams(@WindowManager.TransitionOldType int transit,
+ long startTime, long fadeoutDuration,
+ RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ this.mTransit = transit;
this.startTime = startTime;
this.fadeoutDuration = fadeoutDuration;
+ this.mApps = apps;
+ this.mWallpapers = wallpapers;
+ this.mNonApps = nonApps;
+ this.mFinishedCallback = finishedCallback;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java
index 453e85a..517f8e7 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ContextualButton.java
@@ -51,7 +51,7 @@
* Reload the drawable from resource id, should reapply the previous dark intensity.
*/
public void updateIcon(int lightIconColor, int darkIconColor) {
- if (getCurrentView() == null || !getCurrentView().isAttachedToWindow() || mIconResId == 0) {
+ if (mIconResId == 0) {
return;
}
final KeyButtonDrawable currentDrawable = getImageDrawable();
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index a4e9189..c67aef6 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -24,10 +24,8 @@
import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.os.Bundle;
@@ -36,15 +34,15 @@
import android.util.Log;
import android.view.ViewGroup;
-import androidx.preference.PreferenceManager;
-
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
-import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
+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).
*/
@@ -59,10 +57,17 @@
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);
@@ -96,8 +101,8 @@
*/
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());
@@ -128,26 +133,17 @@
/** Stores the user selected configuration for {@code mAppWidgetId}. */
private void storeWidgetConfiguration(PeopleSpaceTile tile) {
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
- SharedPreferences.Editor editor = sp.edit();
if (PeopleSpaceUtils.DEBUG) {
Log.d(TAG, "Put " + tile.getUserName() + "'s shortcut ID: "
+ tile.getId() + " for widget ID: "
+ mAppWidgetId);
}
- // Ensure updates to app widget can be retrieved from both appWidget Id and tile ID.
- editor.putString(String.valueOf(mAppWidgetId), tile.getId());
- editor.putInt(tile.getId(), mAppWidgetId);
- editor.apply();
- AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
- Bundle options = new Bundle();
- options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, tile);
- appWidgetManager.updateAppWidgetOptions(mAppWidgetId, options);
- int[] widgetIds = appWidgetManager.getAppWidgetIds(
- new ComponentName(mContext, PeopleSpaceWidgetProvider.class));
+
+ PeopleSpaceUtils.setStorageForTile(mContext, tile, mAppWidgetId);
+ int[] widgetIds = new int[mAppWidgetId];
// TODO: Populate new widget with existing conversation notification, if there is any.
PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, mAppWidgetManager,
- mNotificationManager);
+ mPeopleManager);
finishActivity();
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 91e7968..dd05484 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -18,6 +18,7 @@
import static android.app.Notification.EXTRA_MESSAGES;
+import android.annotation.Nullable;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.PendingIntent;
@@ -59,9 +60,12 @@
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;
@@ -70,11 +74,13 @@
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -89,6 +95,13 @@
private static final int MIN_HOUR = 1;
private static final int ONE_DAY = 1;
public static final String OPTIONS_PEOPLE_SPACE_TILE = "options_people_space_tile";
+ public static final String PACKAGE_NAME = "package_name";
+ public static final String USER_ID = "user_id";
+ public static final String SHORTCUT_ID = "shortcut_id";
+
+ public static final String EMPTY_STRING = "";
+ public static final int INVALID_WIDGET_ID = -1;
+ public static final int INVALID_USER_ID = -1;
private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
@@ -127,7 +140,7 @@
/** 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;
@@ -163,6 +176,8 @@
getSortedTiles(peopleManager, launcherApps, mergedStream);
tiles.addAll(recentTiles);
}
+
+ tiles = augmentTilesFromVisibleNotifications(tiles, notificationEntryManager);
return tiles;
}
@@ -171,87 +186,217 @@
* notification being posted or removed.
*/
public static void updateSingleConversationWidgets(Context context, int[] appWidgetIds,
- AppWidgetManager appWidgetManager, INotificationManager notificationManager) {
- IPeopleManager peopleManager = IPeopleManager.Stub.asInterface(
- ServiceManager.getService(Context.PEOPLE_SERVICE));
- LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
Map<Integer, PeopleSpaceTile> widgetIdToTile = new HashMap<>();
- try {
- List<PeopleSpaceTile> tiles =
- PeopleSpaceUtils.getTiles(context, notificationManager,
- peopleManager, launcherApps);
- for (int appWidgetId : appWidgetIds) {
- String shortcutId = sp.getString(String.valueOf(appWidgetId), null);
- if (DEBUG) {
- Log.d(TAG, "Widget ID: " + appWidgetId + " Shortcut ID: " + shortcutId);
- }
-
- Optional<PeopleSpaceTile> entry = tiles.stream().filter(
- e -> e.getId().equals(shortcutId)).findFirst();
-
- if (!entry.isPresent() || shortcutId == null) {
- if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
- //TODO: Delete app widget id when crash is fixed (b/175486868)
- continue;
- }
- // Augment current tile based on stored fields.
- PeopleSpaceTile tile = augmentTileFromStorage(entry.get(), appWidgetManager,
- appWidgetId);
-
- RemoteViews views = createRemoteViews(context, tile, appWidgetId);
-
- // Tell the AppWidgetManager to perform an update on the current app widget.
- appWidgetManager.updateAppWidget(appWidgetId, views);
-
- widgetIdToTile.put(appWidgetId, tile);
+ for (int appWidgetId : appWidgetIds) {
+ PeopleSpaceTile tile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
+ appWidgetId);
+ if (tile == null) {
+ if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
+ //TODO: Delete app widget id when crash is fixed (b/172932636)
+ continue;
}
- } catch (Exception e) {
- Log.e(TAG, "Failed to retrieve conversations to set tiles: " + e);
+
+ if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName());
+ RemoteViews views = createRemoteViews(context, tile, appWidgetId);
+
+ // Tell the AppWidgetManager to perform an update on the current app widget.
+ appWidgetManager.updateAppWidget(appWidgetId, views);
+
+ widgetIdToTile.put(appWidgetId, tile);
}
getBirthdaysOnBackgroundThread(context, appWidgetManager, widgetIdToTile, appWidgetIds);
}
- /** Augment {@link PeopleSpaceTile} with fields from stored tile. */
- @VisibleForTesting
- static PeopleSpaceTile augmentTileFromStorage(PeopleSpaceTile tile,
- AppWidgetManager appWidgetManager, int appWidgetId) {
- Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
- PeopleSpaceTile storedTile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
- if (storedTile == null) {
- return tile;
+ @Nullable
+ private static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager,
+ AppWidgetManager appWidgetManager,
+ Context context, int appWidgetId) {
+ try {
+ // Migrate storage for existing users.
+ SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
+ Context.MODE_PRIVATE);
+ String pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
+ int userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
+ String shortcutId = widgetSp.getString(SHORTCUT_ID, EMPTY_STRING);
+ if (pkg.isEmpty() || shortcutId.isEmpty() || userId == INVALID_WIDGET_ID) {
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ shortcutId = sp.getString(String.valueOf(appWidgetId), null);
+ if (shortcutId == null) {
+ Log.e(TAG, "Cannot restore widget");
+ return null;
+ }
+ migrateExistingUsersToNewStorage(context, shortcutId, appWidgetId);
+ pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
+ userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
+ }
+
+ // Check if tile is cached.
+ Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
+ PeopleSpaceTile tile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ if (tile != null) {
+ return tile;
+ }
+
+ // If tile is null, we need to retrieve from persisted storage.
+ if (DEBUG) Log.d(TAG, "Retrieving from storage after reboots");
+ LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
+ ConversationChannel channel = peopleManager.getConversation(pkg, userId, shortcutId);
+ if (channel == null) {
+ Log.d(TAG, "Could not retrieve conversation from storage");
+ return null;
+ }
+ return new PeopleSpaceTile.Builder(channel, launcherApps).build();
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to retrieve conversation for tile: " + e);
+ return null;
}
- return tile.toBuilder()
- .setBirthdayText(storedTile.getBirthdayText())
- .setNotificationKey(storedTile.getNotificationKey())
- .setNotificationContent(storedTile.getNotificationContent())
- .setNotificationDataUri(storedTile.getNotificationDataUri())
- .build();
}
- /** If incoming notification changed tile, store the changes in the tile options. */
- public static void storeNotificationChange(StatusBarNotification sbn,
+ /** Best-effort attempts to migrate existing users to the new storage format. */
+ // TODO: Remove after sufficient time. Temporary migration storage for existing users.
+ private static void migrateExistingUsersToNewStorage(Context context, String shortcutId,
+ int appWidgetId) {
+ try {
+ List<PeopleSpaceTile> tiles =
+ PeopleSpaceUtils.getTiles(context, INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE)),
+ IPeopleManager.Stub.asInterface(
+ ServiceManager.getService(Context.PEOPLE_SERVICE)),
+ context.getSystemService(LauncherApps.class),
+ Dependency.get(NotificationEntryManager.class));
+ Optional<PeopleSpaceTile> entry = tiles.stream().filter(
+ e -> e.getId().equals(shortcutId)).findFirst();
+ if (entry.isPresent()) {
+ if (DEBUG) Log.d(TAG, "Migrate storage for " + entry.get().getUserName());
+ setStorageForTile(context, entry.get(), appWidgetId);
+ } else {
+ Log.e(TAG, "Could not migrate user");
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Could not query conversations");
+ }
+ }
+
+ /** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */
+ public static void setStorageForTile(Context context, PeopleSpaceTile tile, int appWidgetId) {
+ // Write relevant persisted storage.
+ SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
+ Context.MODE_PRIVATE);
+ SharedPreferences.Editor widgetEditor = widgetSp.edit();
+ widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, tile.getPackageName());
+ widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, tile.getId());
+ int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier();
+ widgetEditor.putInt(PeopleSpaceUtils.USER_ID, userId);
+ widgetEditor.apply();
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putString(String.valueOf(appWidgetId), tile.getId());
+ String key = PeopleSpaceUtils.getKey(tile.getId(), tile.getPackageName(), userId);
+ // Don't overwrite existing widgets with the same key.
+ Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+ storedWidgetIds.add(String.valueOf(appWidgetId));
+ editor.putStringSet(key, storedWidgetIds);
+ editor.apply();
+
+ // Write cached storage.
+ updateAppWidgetOptionsAndView(AppWidgetManager.getInstance(context), context, appWidgetId,
+ tile);
+ }
+
+ /** Removes stored data when tile is deleted. */
+ public static void removeStorageForTile(Context context, int widgetId) {
+ SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId),
+ Context.MODE_PRIVATE);
+ String packageName = widgetSp.getString(PACKAGE_NAME, null);
+ String shortcutId = widgetSp.getString(SHORTCUT_ID, null);
+ int userId = widgetSp.getInt(USER_ID, -1);
+
+ // Delete widgetId mapping to key.
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+ SharedPreferences.Editor editor = sp.edit();
+ String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
+ Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+ storedWidgetIds.remove(String.valueOf(widgetId));
+ editor.putStringSet(key, storedWidgetIds);
+ editor.remove(String.valueOf(widgetId));
+ editor.apply();
+
+ // Delete all data specifically mapped to widgetId.
+ SharedPreferences.Editor widgetEditor = widgetSp.edit();
+ widgetEditor.remove(PACKAGE_NAME);
+ widgetEditor.remove(USER_ID);
+ widgetEditor.remove(SHORTCUT_ID);
+ widgetEditor.apply();
+ }
+
+ /**
+ * Returns whether the data mapped to app widget specified by {@code appWidgetId} matches the
+ * requested update data.
+ */
+ public static boolean isCorrectAppWidget(Context context, int appWidgetId, String shortcutId,
+ String packageName, int userId) {
+ SharedPreferences sp = context.getSharedPreferences(String.valueOf(appWidgetId),
+ Context.MODE_PRIVATE);
+ String storedPackage = sp.getString(PACKAGE_NAME, EMPTY_STRING);
+ int storedUserId = sp.getInt(USER_ID, INVALID_USER_ID);
+ String storedShortcutId = sp.getString(SHORTCUT_ID, EMPTY_STRING);
+ return storedPackage.equals(packageName) && storedShortcutId.equals(shortcutId)
+ && 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.
+ */
+ public static void updateWidgetWithNotificationChanged(IPeopleManager peopleManager,
+ Context context,
+ StatusBarNotification sbn,
NotificationAction notificationAction, AppWidgetManager appWidgetManager,
int appWidgetId) {
- Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
- PeopleSpaceTile storedTile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ PeopleSpaceTile storedTile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
+ appWidgetId);
if (storedTile == null) {
if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to");
return;
}
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);
@@ -263,7 +408,22 @@
.setNotificationDataUri(null)
.build();
}
- updateAppWidgetOptions(appWidgetManager, appWidgetId, storedTile);
+ 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,
@@ -677,4 +837,33 @@
}
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.
+ *
+ * <p>{@code userId} will always be a number, so we put user ID as the
+ * delimiter between the app-provided strings of shortcut ID and package name.
+ *
+ * <p>There aren't restrictions on shortcut ID characters, but there are restrictions requiring
+ * a {@code packageName} to always start with a letter. This restriction means we are
+ * guaranteed to avoid cases like "a/b/0/0/package.name" having two potential keys, as the first
+ * case is impossible given the package name restrictions:
+ * <ul>
+ * <li>"a/b" + "/" + 0 + "/" + "0/packageName"</li>
+ * <li>"a/b/0" + "/" + 0 + "/" + "packageName"</li>
+ * </ul>
+ */
+ public static String getKey(String shortcutId, String packageName, int userId) {
+ return shortcutId + "/" + userId + "/" + packageName;
+ }
}
\ No newline at end of file
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 ad6046b..bee9889 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -16,8 +16,8 @@
package com.android.systemui.people.widget;
-import android.app.INotificationManager;
import android.app.NotificationChannel;
+import android.app.people.IPeopleManager;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
@@ -29,6 +29,7 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
+import android.widget.RemoteViews;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.appwidget.IAppWidgetService;
@@ -37,7 +38,8 @@
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
-import java.util.Objects;
+import java.util.HashSet;
+import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -51,7 +53,7 @@
private final Context mContext;
private IAppWidgetService mAppWidgetService;
private AppWidgetManager mAppWidgetManager;
- private INotificationManager mNotificationManager;
+ private IPeopleManager mPeopleManager;
@Inject
public PeopleSpaceWidgetManager(Context context, IAppWidgetService appWidgetService) {
@@ -59,11 +61,13 @@
mContext = context;
mAppWidgetService = appWidgetService;
mAppWidgetManager = AppWidgetManager.getInstance(context);
- mNotificationManager = INotificationManager.Stub.asInterface(
- ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ mPeopleManager = IPeopleManager.Stub.asInterface(
+ ServiceManager.getService(Context.PEOPLE_SERVICE));
}
- /** Constructor used for testing. */
+ /**
+ * Constructor used for testing.
+ */
@VisibleForTesting
protected PeopleSpaceWidgetManager(Context context) {
if (DEBUG) Log.d(TAG, "constructor");
@@ -72,16 +76,20 @@
ServiceManager.getService(Context.APPWIDGET_SERVICE));
}
- /** AppWidgetManager setter used for testing. */
+ /**
+ * AppWidgetManager setter used for testing.
+ */
@VisibleForTesting
protected void setAppWidgetManager(IAppWidgetService appWidgetService,
- AppWidgetManager appWidgetManager, INotificationManager notificationManager) {
+ AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
mAppWidgetService = appWidgetService;
mAppWidgetManager = appWidgetManager;
- mNotificationManager = notificationManager;
+ mPeopleManager = peopleManager;
}
- /** Updates People Space widgets. */
+ /**
+ * Updates People Space widgets.
+ */
public void updateWidgets() {
try {
if (DEBUG) Log.d(TAG, "updateWidgets called");
@@ -99,7 +107,7 @@
if (showSingleConversation) {
PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds,
- mAppWidgetManager, mNotificationManager);
+ mAppWidgetManager, mPeopleManager);
} else {
mAppWidgetService
.notifyAppWidgetViewDataChanged(mContext.getOpPackageName(), widgetIds,
@@ -114,30 +122,41 @@
* Check if any existing People tiles match the incoming notification change, and store the
* change in the tile if so.
*/
- public void storeNotificationChange(StatusBarNotification sbn,
+ public void updateWidgetWithNotificationChanged(StatusBarNotification sbn,
PeopleSpaceUtils.NotificationAction notificationAction) {
- if (DEBUG) Log.d(TAG, "storeNotificationChange called");
+ RemoteViews views = new RemoteViews(
+ mContext.getPackageName(), R.layout.people_space_small_avatar_tile);
+ if (DEBUG) Log.d(TAG, "updateWidgetWithNotificationChanged called");
boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
if (!showSingleConversation) {
return;
}
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);
- for (int widgetId : widgetIds) {
- String shortcutId = sp.getString(String.valueOf(widgetId), null);
- if (!Objects.equals(sbn.getShortcutId(), shortcutId)) {
- continue;
- }
+ int userId = UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier();
+ 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) {
+ int widgetId = Integer.parseInt(widgetIdString);
if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
- PeopleSpaceUtils.storeNotificationChange(
+ PeopleSpaceUtils.updateWidgetWithNotificationChanged(mPeopleManager, mContext,
sbn, notificationAction, mAppWidgetManager, widgetId);
}
} catch (Exception e) {
@@ -159,8 +178,7 @@
public void onNotificationPosted(
StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap) {
if (DEBUG) Log.d(TAG, "onNotificationPosted");
- storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
- updateWidgets();
+ updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
}
@Override
@@ -169,8 +187,7 @@
NotificationListenerService.RankingMap rankingMap
) {
if (DEBUG) Log.d(TAG, "onNotificationRemoved");
- storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
- updateWidgets();
+ updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
}
@Override
@@ -179,8 +196,7 @@
NotificationListenerService.RankingMap rankingMap,
int reason) {
if (DEBUG) Log.d(TAG, "onNotificationRemoved with reason " + reason);
- storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
- updateWidgets();
+ updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
}
@Override
@@ -207,4 +223,4 @@
}
};
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
index 7f204cc..f5577d3 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
@@ -16,8 +16,8 @@
package com.android.systemui.people.widget;
-import android.app.INotificationManager;
import android.app.PendingIntent;
+import android.app.people.IPeopleManager;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
@@ -53,8 +53,8 @@
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
if (showSingleConversation) {
PeopleSpaceUtils.updateSingleConversationWidgets(context, appWidgetIds,
- appWidgetManager, INotificationManager.Stub.asInterface(
- ServiceManager.getService(Context.NOTIFICATION_SERVICE)));
+ appWidgetManager, IPeopleManager.Stub.asInterface(
+ ServiceManager.getService(Context.PEOPLE_SERVICE)));
return;
}
// Perform this loop procedure for each App Widget that belongs to this provider
@@ -91,6 +91,7 @@
for (int widgetId : appWidgetIds) {
if (DEBUG) Log.d(TAG, "Widget removed");
mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED);
+ PeopleSpaceUtils.removeStorageForTile(context, widgetId);
}
}
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 fb33aff..80794cb 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.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 @@
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 @@
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 @@
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/PrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
index 3f79be8..680a617 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 @@
* @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 @@
} ?: 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 @@
}
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 @@
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 8b6c04f..03c1843 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 @@
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 @@
}
@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 @@
}
PrivacyDialog.PrivacyElement(
t,
+ it.packageName,
+ UserHandle.getUserId(it.uid),
appName,
it.attribution,
it.lastAccess,
@@ -257,7 +260,7 @@
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 8059475..ebf6ae9 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 @@
})
}
- 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 d248ab5..c8edaec 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 @@
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/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 507048c..21464fd 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 @@
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 @@
}
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/qs/dagger/QSFlagsModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
index 9ab2d73..35a8257 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFlagsModule.java
@@ -16,6 +16,9 @@
package com.android.systemui.qs.dagger;
+import android.content.Context;
+import android.hardware.display.ColorDisplayManager;
+
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.FeatureFlags;
@@ -27,6 +30,7 @@
@Module
public interface QSFlagsModule {
String QS_LABELS_FLAG = "qs_labels_flag";
+ String RBC_AVAILABLE = "rbc_available";
@Provides
@SysUISingleton
@@ -34,4 +38,12 @@
static boolean provideQSFlag(FeatureFlags featureFlags) {
return featureFlags.isQSLabelsEnabled();
}
+
+ /** */
+ @Provides
+ @SysUISingleton
+ @Named(RBC_AVAILABLE)
+ static boolean isReduceBrightColorsAvailable(Context context) {
+ return ColorDisplayManager.isReduceBrightColorsAvailable(context);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 6e28cd8..56d06eb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -315,7 +315,8 @@
}
try {
if (DEBUG) Log.d(TAG, "Adding token");
- mWindowManager.addWindowToken(mToken, TYPE_QS_DIALOG, DEFAULT_DISPLAY);
+ mWindowManager.addWindowToken(mToken, TYPE_QS_DIALOG, DEFAULT_DISPLAY,
+ null /* options */);
mIsTokenGranted = true;
} catch (RemoteException e) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
index 84c7611..f94cabc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
@@ -16,12 +16,13 @@
package com.android.systemui.qs.tiles;
+import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE;
+
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
-import android.text.TextUtils;
import android.widget.Switch;
import com.android.internal.logging.MetricsLogger;
@@ -39,6 +40,7 @@
import com.android.systemui.util.settings.SecureSettings;
import javax.inject.Inject;
+import javax.inject.Named;
/** Quick settings tile: Reduce Bright Colors **/
public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState> {
@@ -46,9 +48,11 @@
//TODO(b/170973645): get icon drawable
private final Icon mIcon = null;
private final SecureSetting mActivatedSetting;
+ private final boolean mIsAvailable;
@Inject
public ReduceBrightColorsTile(
+ @Named(RBC_AVAILABLE) boolean isAvailable,
QSHost host,
@Background Looper backgroundLooper,
@Main Handler mainHandler,
@@ -69,11 +73,12 @@
refreshState();
}
};
+ mIsAvailable = isAvailable;
+
}
@Override
public boolean isAvailable() {
- // TODO(b/170970675): Call into ColorDisplayService to get availability/config status
- return true;
+ return mIsAvailable;
}
@Override
@@ -121,15 +126,6 @@
state.label = mContext.getString(R.string.quick_settings_reduce_bright_colors_label);
state.expandedAccessibilityClassName = Switch.class.getName();
state.contentDescription = state.label;
-
- final int intensity = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0, mActivatedSetting.getCurrentUser());
- state.secondaryLabel = state.value ? mContext.getString(
- R.string.quick_settings_reduce_bright_colors_secondary_label, intensity) : "";
-
- state.contentDescription = TextUtils.isEmpty(state.secondaryLabel)
- ? state.label
- : TextUtils.concat(state.label, ", ", state.secondaryLabel);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index a9f76f6..5b2a7e7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -101,7 +101,7 @@
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.RemoteTransitions;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -149,7 +149,7 @@
private final ScreenshotHelper mScreenshotHelper;
private final Optional<OneHanded> mOneHandedOptional;
private final CommandQueue mCommandQueue;
- private final Transitions mShellTransitions;
+ private final RemoteTransitions mShellTransitions;
private Region mActiveNavBarRegion;
@@ -620,6 +620,19 @@
}
@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;
@@ -799,7 +812,7 @@
Optional<Lazy<StatusBar>> statusBarOptionalLazy,
Optional<OneHanded> oneHandedOptional,
BroadcastDispatcher broadcastDispatcher,
- Transitions shellTransitions) {
+ RemoteTransitions shellTransitions) {
super(broadcastDispatcher);
mContext = context;
mPipOptional = pipOptional;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index 9037192..5438743 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -187,7 +187,7 @@
* @param refreshRate Desired refresh rate
* @return array with supported width, height, and refresh rate
*/
- private int[] getSupportedSize(int screenWidth, int screenHeight, int refreshRate) {
+ private int[] getSupportedSize(final int screenWidth, final int screenHeight, int refreshRate) {
double maxScale = 0;
MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
@@ -207,25 +207,33 @@
int width = vc.getSupportedWidths().getUpper();
int height = vc.getSupportedHeights().getUpper();
- if (width >= screenWidth && height >= screenHeight
- && vc.isSizeSupported(screenWidth, screenHeight)) {
+ int screenWidthAligned = screenWidth;
+ if (screenWidthAligned % vc.getWidthAlignment() != 0) {
+ screenWidthAligned -= (screenWidthAligned % vc.getWidthAlignment());
+ }
+ int screenHeightAligned = screenHeight;
+ if (screenHeightAligned % vc.getHeightAlignment() != 0) {
+ screenHeightAligned -= (screenHeightAligned % vc.getHeightAlignment());
+ }
+ if (width >= screenWidthAligned && height >= screenHeightAligned
+ && vc.isSizeSupported(screenWidthAligned, screenHeightAligned)) {
// Desired size is supported, now get the rate
- int maxRate = vc.getSupportedFrameRatesFor(screenWidth, screenHeight)
- .getUpper().intValue();
+ int maxRate = vc.getSupportedFrameRatesFor(screenWidthAligned,
+ screenHeightAligned).getUpper().intValue();
if (maxRate < refreshRate) {
refreshRate = maxRate;
}
Log.d(TAG, "Screen size supported at rate " + refreshRate);
- return new int[]{screenWidth, screenHeight, refreshRate};
+ return new int[]{screenWidthAligned, screenHeightAligned, refreshRate};
}
// Otherwise, continue searching
double scale = Math.min(((double) width / screenWidth),
((double) height / screenHeight));
if (scale > maxScale) {
- maxScale = scale;
+ maxScale = Math.min(1, scale);
maxInfo = vc;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
index 8e182b4..c8afd0b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -35,7 +35,7 @@
* cropped out.
*/
public class CropView extends View {
- private enum CropBoundary {
+ public enum CropBoundary {
NONE, TOP, BOTTOM
}
@@ -48,8 +48,14 @@
private float mTopCrop = 0f;
private float mBottomCrop = 1f;
+ // When the user is dragging a handle, these variables store the distance between the top/bottom
+ // crop values and
+ private float mTopDelta = 0f;
+ private float mBottomDelta = 0f;
+
private CropBoundary mCurrentDraggingBoundary = CropBoundary.NONE;
- private float mLastY;
+ private float mStartingY; // y coordinate of ACTION_DOWN
+ private CropInteractionListener mCropInteractionListener;
public CropView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
@@ -73,54 +79,84 @@
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
- drawShade(canvas, 0, mTopCrop);
- drawShade(canvas, mBottomCrop, 1f);
- drawHandle(canvas, mTopCrop);
- drawHandle(canvas, mBottomCrop);
+ float top = mTopCrop + mTopDelta;
+ float bottom = mBottomCrop + mBottomDelta;
+ drawShade(canvas, 0, top);
+ drawShade(canvas, bottom, 1f);
+ drawHandle(canvas, top);
+ drawHandle(canvas, bottom);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int topPx = fractionToPixels(mTopCrop);
int bottomPx = fractionToPixels(mBottomCrop);
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- mCurrentDraggingBoundary = nearestBoundary(event, topPx, bottomPx);
- if (mCurrentDraggingBoundary != CropBoundary.NONE) {
- mLastY = event.getY();
- }
- return true;
- }
- if (event.getAction() == MotionEvent.ACTION_MOVE
- && mCurrentDraggingBoundary != CropBoundary.NONE) {
- float delta = event.getY() - mLastY;
- if (mCurrentDraggingBoundary == CropBoundary.TOP) {
- mTopCrop = pixelsToFraction((int) MathUtils.constrain(topPx + delta, 0,
- bottomPx - 2 * mCropTouchMargin));
- } else { // Bottom
- mBottomCrop = pixelsToFraction((int) MathUtils.constrain(bottomPx + delta,
- topPx + 2 * mCropTouchMargin, getMeasuredHeight()));
- }
- mLastY = event.getY();
- invalidate();
- return true;
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mCurrentDraggingBoundary = nearestBoundary(event, topPx, bottomPx);
+ if (mCurrentDraggingBoundary != CropBoundary.NONE) {
+ mStartingY = event.getY();
+ updateListener(event);
+ }
+ return true;
+ case MotionEvent.ACTION_MOVE:
+ if (mCurrentDraggingBoundary != CropBoundary.NONE) {
+ float delta = event.getY() - mStartingY;
+ if (mCurrentDraggingBoundary == CropBoundary.TOP) {
+ mTopDelta = pixelsToFraction((int) MathUtils.constrain(delta, -topPx,
+ bottomPx - 2 * mCropTouchMargin - topPx));
+ } else { // Bottom
+ mBottomDelta = pixelsToFraction((int) MathUtils.constrain(delta,
+ topPx + 2 * mCropTouchMargin - bottomPx,
+ getMeasuredHeight() - bottomPx));
+ }
+ updateListener(event);
+ invalidate();
+ return true;
+ }
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ if (mCurrentDraggingBoundary != CropBoundary.NONE) {
+ // Commit the delta to the stored crop values.
+ mTopCrop += mTopDelta;
+ mBottomCrop += mBottomDelta;
+ mTopDelta = 0;
+ mBottomDelta = 0;
+ updateListener(event);
+ }
}
return super.onTouchEvent(event);
}
/**
- * @return value [0,1] representing the position of the top crop boundary.
+ * @return value [0,1] representing the position of the top crop boundary. Does not reflect
+ * changes from any in-progress touch input.
*/
public float getTopBoundary() {
return mTopCrop;
}
/**
- * @return value [0,1] representing the position of the bottom crop boundary.
+ * @return value [0,1] representing the position of the bottom crop boundary. Does not reflect
+ * changes from any in-progress touch input.
*/
public float getBottomBoundary() {
return mBottomCrop;
}
+ public void setCropInteractionListener(CropInteractionListener listener) {
+ mCropInteractionListener = listener;
+ }
+
+ private void updateListener(MotionEvent event) {
+ if (mCropInteractionListener != null) {
+ float boundaryPosition = (mCurrentDraggingBoundary == CropBoundary.TOP)
+ ? mTopCrop + mTopDelta : mBottomCrop + mBottomDelta;
+ mCropInteractionListener.onCropMotionEvent(event, mCurrentDraggingBoundary,
+ boundaryPosition, fractionToPixels(boundaryPosition));
+ }
+ }
+
private void drawShade(Canvas canvas, float fracStart, float fracEnd) {
canvas.drawRect(0, fractionToPixels(fracStart), getMeasuredWidth(),
fractionToPixels(fracEnd), mShadePaint);
@@ -148,4 +184,17 @@
}
return CropBoundary.NONE;
}
+
+ /**
+ * Listen for crop motion events and state.
+ */
+ public interface CropInteractionListener {
+ /**
+ * Called whenever CropView has a MotionEvent that can impact the position of the crop
+ * boundaries.
+ */
+ void onCropMotionEvent(MotionEvent event, CropBoundary boundary, float boundaryPosition,
+ int boundaryPositionPx);
+
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
new file mode 100644
index 0000000..f887151
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
@@ -0,0 +1,184 @@
+/*
+ * 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.screenshot;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+
+/**
+ * MagnifierView shows a full-res cropped circular display of a given ImageTileSet, contents and
+ * positioning dereived from events from a CropView to which it listens.
+ *
+ * Not meant to be a general-purpose magnifier!
+ */
+public class MagnifierView extends View implements CropView.CropInteractionListener {
+ private Drawable mDrawable;
+
+ private final Paint mShadePaint;
+ private final Paint mHandlePaint;
+
+ private Path mOuterCircle;
+ private Path mInnerCircle;
+
+ private Path mCheckerboard;
+ private Paint mCheckerboardPaint;
+ private final float mBorderPx;
+ private final int mBorderColor;
+ private float mCheckerboardBoxSize = 40;
+
+ private float mLastCropPosition;
+ private CropView.CropBoundary mCropBoundary;
+
+ public MagnifierView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public MagnifierView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ TypedArray t = context.getTheme().obtainStyledAttributes(
+ attrs, R.styleable.MagnifierView, 0, 0);
+ mShadePaint = new Paint();
+ mShadePaint.setColor(t.getColor(R.styleable.MagnifierView_scrimColor, Color.TRANSPARENT));
+ mHandlePaint = new Paint();
+ mHandlePaint.setColor(t.getColor(R.styleable.MagnifierView_handleColor, Color.BLACK));
+ mHandlePaint.setStrokeWidth(
+ t.getDimensionPixelSize(R.styleable.MagnifierView_handleThickness, 20));
+ mBorderPx = t.getDimensionPixelSize(R.styleable.MagnifierView_borderThickness, 0);
+ mBorderColor = t.getColor(R.styleable.MagnifierView_borderColor, Color.WHITE);
+ t.recycle();
+ mCheckerboardPaint = new Paint();
+ mCheckerboardPaint.setColor(Color.GRAY);
+ }
+
+ public void setImageTileset(ImageTileSet tiles) {
+ if (tiles != null) {
+ mDrawable = tiles.getDrawable();
+ mDrawable.setBounds(0, 0, tiles.getWidth(), tiles.getHeight());
+ } else {
+ mDrawable = null;
+ }
+ invalidate();
+ }
+
+ @Override
+ public void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ int radius = getWidth() / 2;
+ mOuterCircle = new Path();
+ mOuterCircle.addCircle(radius, radius, radius, Path.Direction.CW);
+ mInnerCircle = new Path();
+ mInnerCircle.addCircle(radius, radius, radius - mBorderPx, Path.Direction.CW);
+ mCheckerboard = generateCheckerboard();
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ // TODO: just draw a circle at the end instead of clipping like this?
+ canvas.clipPath(mOuterCircle);
+ canvas.drawColor(mBorderColor);
+ canvas.clipPath(mInnerCircle);
+
+ // Draw a checkerboard pattern for out of bounds.
+ canvas.drawPath(mCheckerboard, mCheckerboardPaint);
+
+ if (mDrawable != null) {
+ canvas.save();
+ // Translate such that the center of this view represents the center of the crop
+ // boundary.
+ canvas.translate(-mDrawable.getBounds().width() / 2 + getWidth() / 2,
+ -mDrawable.getBounds().height() * mLastCropPosition + getHeight() / 2);
+ mDrawable.draw(canvas);
+ canvas.restore();
+ }
+
+ Rect scrimRect = new Rect(0, 0, getWidth(), getHeight() / 2);
+ if (mCropBoundary == CropView.CropBoundary.BOTTOM) {
+ scrimRect.offset(0, getHeight() / 2);
+ }
+ canvas.drawRect(scrimRect, mShadePaint);
+
+ canvas.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2, mHandlePaint);
+ }
+
+ @Override
+ public void onCropMotionEvent(MotionEvent event, CropView.CropBoundary boundary,
+ float cropPosition, int cropPositionPx) {
+ mCropBoundary = boundary;
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mLastCropPosition = cropPosition;
+ setTranslationY(cropPositionPx - getHeight() / 2);
+ setPivotX(getWidth() / 2);
+ setPivotY(getHeight() / 2);
+ setScaleX(0.2f);
+ setScaleY(0.2f);
+ setAlpha(0f);
+ setTranslationX((getParentWidth() - getWidth()) / 2);
+ setVisibility(View.VISIBLE);
+ animate().alpha(1f).translationX(0).scaleX(1f).scaleY(1f).start();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ mLastCropPosition = cropPosition;
+ setTranslationY(cropPositionPx - getHeight() / 2);
+ invalidate();
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ animate().alpha(0).translationX((getParentWidth() - getWidth()) / 2).scaleX(0.2f)
+ .scaleY(0.2f).withEndAction(() -> setVisibility(View.INVISIBLE)).start();
+ break;
+ }
+ }
+
+ private Path generateCheckerboard() {
+ Path path = new Path();
+ int checkerWidth = (int) Math.ceil(getWidth() / mCheckerboardBoxSize);
+ int checkerHeight = (int) Math.ceil(getHeight() / mCheckerboardBoxSize);
+
+ for (int row = 0; row < checkerHeight; row++) {
+ // Alternate starting on the first and second column;
+ int colStart = (row % 2 == 0) ? 0 : 1;
+ for (int col = colStart; col < checkerWidth; col += 2) {
+ path.addRect(col * mCheckerboardBoxSize,
+ row * mCheckerboardBoxSize,
+ (col + 1) * mCheckerboardBoxSize,
+ (row + 1) * mCheckerboardBoxSize,
+ Path.Direction.CW);
+ }
+ }
+ return path;
+ }
+
+ private int getParentWidth() {
+ return ((View) getParent()).getWidth();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index 18c379a..25438a6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -81,6 +81,7 @@
private View mEdit;
private View mShare;
private CropView mCropView;
+ private MagnifierView mMagnifierView;
public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor,
Executor bgExecutor, ImageExporter exporter, UiEventLogger uiEventLogger) {
@@ -120,13 +121,14 @@
mEdit = findViewById(R.id.edit);
mShare = findViewById(R.id.share);
mCropView = findViewById(R.id.crop_view);
+ mMagnifierView = findViewById(R.id.magnifier);
+ mCropView.setCropInteractionListener(mMagnifierView);
mSave.setOnClickListener(this::onClicked);
mCancel.setOnClickListener(this::onClicked);
mEdit.setOnClickListener(this::onClicked);
mShare.setOnClickListener(this::onClicked);
- //mPreview.setImageDrawable(mImageTileSet.getDrawable());
mConnection.start(this::startCapture);
}
@@ -164,6 +166,7 @@
private void doFinish() {
mPreview.setImageDrawable(null);
+ mMagnifierView.setImageTileset(null);
mImageTileSet.clear();
mCallback.onFinish();
mWindow.getDecorView().getViewTreeObserver()
@@ -273,6 +276,7 @@
session.end(mCallback::onFinish);
} else {
mPreview.setImageDrawable(mImageTileSet.getDrawable());
+ mMagnifierView.setImageTileset(mImageTileSet);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index a3b5f27..43bb343 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -79,6 +79,7 @@
private final float mMaximumBacklightForVr;
private final float mDefaultBacklightForVr;
+ private final int mDisplayId;
private final Context mContext;
private final ToggleSlider mControl;
private final boolean mAutomaticAvailable;
@@ -311,6 +312,7 @@
};
mBrightnessObserver = new BrightnessObserver(mHandler);
+ mDisplayId = mContext.getDisplayId();
PowerManager pm = context.getSystemService(PowerManager.class);
mMinimumBacklight = pm.getBrightnessConstraint(
PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
@@ -420,7 +422,7 @@
}
private void setBrightness(float brightness) {
- mDisplayManager.setTemporaryBrightness(brightness);
+ mDisplayManager.setTemporaryBrightness(mDisplayId, brightness);
}
private void updateVrMode(boolean isEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index e7b60c3..2dd85e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -67,6 +67,10 @@
return mFlagReader.isEnabled(R.bool.flag_brightness_slider);
}
+ public boolean useNewLockscreenAnimations() {
+ return mFlagReader.isEnabled(R.bool.flag_lockscreen_animations);
+ }
+
public boolean isPeopleTileEnabled() {
return mFlagReader.isEnabled(R.bool.flag_conversations);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index c816784..c70a93b 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 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.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 @@
* 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 @@
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 @@
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 @@
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 @@
BroadcastDispatcher broadcastDispatcher,
DevicePolicyManager devicePolicyManager,
IBatteryStats iBatteryStats,
- UserManager userManager) {
+ UserManager userManager,
+ @Main DelayableExecutor executor) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mDevicePolicyManager = devicePolicyManager;
@@ -165,23 +186,29 @@
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 @@
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);
- }
- mDisclosure.setVisibility(View.VISIBLE);
+ 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 {
- mDisclosure.setVisibility(View.GONE);
+ 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";
+ }
+
+ mRotateTextViewController.updateIndication(
+ INDICATION_TYPE_BATTERY,
+ new KeyguardIndication.Builder()
+ .setMessage(powerIndication)
+ .setTextColor(mInitialTextColorState)
+ .build(),
+ animate);
+ } else {
+ // 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 @@
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 @@
mTransientIndication = null;
mHideTransientMessageOnScreenOff = false;
mHandler.removeMessages(MSG_HIDE_TRANSIENT);
+ mRotateTextViewController.hideTransient();
updateIndication(false);
}
}
@@ -396,98 +602,109 @@
}
// 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;
}
- int userId = KeyguardUpdateMonitor.getCurrentUser();
- String trustGrantedIndication = getTrustGrantedIndication();
- String trustManagedIndication = getTrustManagedIndication();
-
- String powerIndication = null;
- if (mPowerPluggedIn || mEnableBatteryDefender) {
- powerIndication = computePowerIndication();
- }
-
+ // LOCK SCREEN
// 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);
- }
- 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);
- hideIndication = !mBatteryPresent;
- } 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";
- }
- if (animate) {
- animateText(mTextView, powerIndication);
- } else {
- mTextView.switchIndication(powerIndication);
- }
- hideIndication = !mBatteryPresent;
- } else if (!TextUtils.isEmpty(trustManagedIndication)
- && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId)
- && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
- mTextView.switchIndication(trustManagedIndication);
+ int userId = KeyguardUpdateMonitor.getCurrentUser();
+
+ if (mLockScreenMode == LOCK_SCREEN_MODE_LAYOUT_1) {
+ mTopIndicationView.setVisibility(GONE);
+ updateIndications(animate, userId);
} else {
- mTextView.switchIndication(mRestingIndication);
- }
- mTextView.setTextColor(isError ? Utils.getColorError(mContext)
- : mInitialTextColorState);
- if (hideIndication) {
- mIndicationArea.setVisibility(View.GONE);
+ boolean hideIndication = false;
+ boolean isError = false;
+ String trustGrantedIndication = getTrustGrantedIndication();
+ String trustManagedIndication = getTrustManagedIndication();
+ String powerIndication = null;
+
+ if (mPowerPluggedIn || mEnableBatteryDefender) {
+ powerIndication = computePowerIndication();
+ }
+ 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 {
+ mTopIndicationView.switchIndication(mRestingIndication, null);
+ }
+
+ mTopIndicationView.setTextColor(
+ isError ? Utils.getColorError(mContext) : mInitialTextColorState);
+
+ if (hideIndication) {
+ mIndicationArea.setVisibility(GONE);
+ }
}
}
@@ -510,7 +727,7 @@
@Override
public void onAnimationStart(Animator animation) {
- textView.switchIndication(indication);
+ textView.switchIndication(indication, null);
}
@Override
@@ -634,18 +851,6 @@
}
}
- 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 @@
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 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 @@
@Override
public void onUserSwitchComplete(int userId) {
if (mVisible) {
+ updateOwnerInfo();
updateIndication(false);
}
}
@@ -857,11 +1055,39 @@
}
@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/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 2f0f90d..c1feaca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -11,14 +11,10 @@
import android.graphics.PorterDuffXfermode
import android.graphics.RadialGradient
import android.graphics.Shader
-import android.os.SystemProperties
import android.util.AttributeSet
import android.view.View
import com.android.systemui.Interpolators
-val enableLightReveal =
- SystemProperties.getBoolean("persist.sysui.show_new_screen_on_transitions", false)
-
/**
* Provides methods to modify the various properties of a [LightRevealScrim] to reveal between 0% to
* 100% of the view(s) underneath the scrim.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index a816ecc..cfadcd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -388,7 +388,28 @@
*/
public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input,
PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo) {
+ return activateRemoteInput(view, inputs, input, pendingIntent, editedSuggestionInfo,
+ null /* userMessageContent */, null /* authBypassCheck */);
+ }
+ /**
+ * Activates a given {@link RemoteInput}
+ *
+ * @param view The view of the action button or suggestion chip that was tapped.
+ * @param inputs The remote inputs that need to be sent to the app.
+ * @param input The remote input that needs to be activated.
+ * @param pendingIntent The pending intent to be sent to the app.
+ * @param editedSuggestionInfo The smart reply that should be inserted in the remote input, or
+ * {@code null} if the user is not editing a smart reply.
+ * @param userMessageContent User-entered text with which to initialize the remote input view.
+ * @param authBypassCheck Optional auth bypass check associated with this remote input
+ * activation. If {@code null}, we never bypass.
+ * @return Whether the {@link RemoteInput} was activated.
+ */
+ public boolean activateRemoteInput(View view, RemoteInput[] inputs, RemoteInput input,
+ PendingIntent pendingIntent, @Nullable EditedSuggestionInfo editedSuggestionInfo,
+ @Nullable String userMessageContent,
+ @Nullable AuthBypassPredicate authBypassCheck) {
ViewParent p = view.getParent();
RemoteInputView riv = null;
ExpandableNotificationRow row = null;
@@ -410,40 +431,9 @@
row.setUserExpanded(true);
- if (!mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) {
- final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
-
- final boolean isLockedManagedProfile =
- mUserManager.getUserInfo(userId).isManagedProfile()
- && mKeyguardManager.isDeviceLocked(userId);
-
- final boolean isParentUserLocked;
- if (isLockedManagedProfile) {
- final UserInfo profileParent = mUserManager.getProfileParent(userId);
- isParentUserLocked = (profileParent != null)
- && mKeyguardManager.isDeviceLocked(profileParent.id);
- } else {
- isParentUserLocked = false;
- }
-
- if (mLockscreenUserManager.isLockscreenPublicMode(userId)
- || mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
- // If the parent user is no longer locked, and the user to which the remote input
- // is destined is a locked, managed profile, then onLockedWorkRemoteInput should be
- // called to unlock it.
- if (isLockedManagedProfile && !isParentUserLocked) {
- mCallback.onLockedWorkRemoteInput(userId, row, view);
- } else {
- // Even if we don't have security we should go through this flow, otherwise
- // we won't go to the shade.
- mCallback.onLockedRemoteInput(row, view);
- }
- return true;
- }
- if (isLockedManagedProfile) {
- mCallback.onLockedWorkRemoteInput(userId, row, view);
- return true;
- }
+ final boolean deferBouncer = authBypassCheck != null;
+ if (!deferBouncer && showBouncerForRemoteInput(view, pendingIntent, row)) {
+ return true;
}
if (riv != null && !riv.isAttachedToWindow()) {
@@ -461,7 +451,10 @@
&& !row.getPrivateLayout().getExpandedChild().isShown()) {
// The expanded layout is selected, but it's not shown yet, let's wait on it to
// show before we do the animation.
- mCallback.onMakeExpandedVisibleForRemoteInput(row, view);
+ mCallback.onMakeExpandedVisibleForRemoteInput(row, view, deferBouncer, () -> {
+ activateRemoteInput(view, inputs, input, pendingIntent, editedSuggestionInfo,
+ userMessageContent, authBypassCheck);
+ });
return true;
}
@@ -491,10 +484,62 @@
riv.setPendingIntent(pendingIntent);
riv.setRemoteInput(inputs, input, editedSuggestionInfo);
riv.focusAnimated();
+ if (userMessageContent != null) {
+ riv.setEditTextContent(userMessageContent);
+ }
+ if (deferBouncer) {
+ final ExpandableNotificationRow finalRow = row;
+ riv.setBouncerChecker(() -> !authBypassCheck.canSendRemoteInputWithoutBouncer()
+ && showBouncerForRemoteInput(view, pendingIntent, finalRow));
+ }
return true;
}
+ private boolean showBouncerForRemoteInput(View view, PendingIntent pendingIntent,
+ ExpandableNotificationRow row) {
+ if (mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) {
+ return false;
+ }
+
+ final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
+
+ final boolean isLockedManagedProfile =
+ mUserManager.getUserInfo(userId).isManagedProfile()
+ && mKeyguardManager.isDeviceLocked(userId);
+
+ final boolean isParentUserLocked;
+ if (isLockedManagedProfile) {
+ final UserInfo profileParent = mUserManager.getProfileParent(userId);
+ isParentUserLocked = (profileParent != null)
+ && mKeyguardManager.isDeviceLocked(profileParent.id);
+ } else {
+ isParentUserLocked = false;
+ }
+
+ if ((mLockscreenUserManager.isLockscreenPublicMode(userId)
+ || mStatusBarStateController.getState() == StatusBarState.KEYGUARD)) {
+ // If the parent user is no longer locked, and the user to which the remote
+ // input
+ // is destined is a locked, managed profile, then onLockedWorkRemoteInput
+ // should be
+ // called to unlock it.
+ if (isLockedManagedProfile && !isParentUserLocked) {
+ mCallback.onLockedWorkRemoteInput(userId, row, view);
+ } else {
+ // Even if we don't have security we should go through this flow, otherwise
+ // we won't go to the shade.
+ mCallback.onLockedRemoteInput(row, view);
+ }
+ return true;
+ }
+ if (isLockedManagedProfile) {
+ mCallback.onLockedWorkRemoteInput(userId, row, view);
+ return true;
+ }
+ return false;
+ }
+
private RemoteInputView findRemoteInputView(View v) {
if (v == null) {
return null;
@@ -807,8 +852,11 @@
*
* @param row
* @param clickedView
+ * @param deferBouncer
+ * @param runnable
*/
- void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, View clickedView);
+ void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row, View clickedView,
+ boolean deferBouncer, Runnable runnable);
/**
* Return whether or not remote input should be handled for this view.
@@ -845,4 +893,28 @@
*/
boolean handleClick();
}
+
+ /**
+ * Predicate that is associated with a specific {@link #activateRemoteInput(View, RemoteInput[],
+ * RemoteInput, PendingIntent, EditedSuggestionInfo, String, AuthBypassPredicate)}
+ * invocation that determines whether or not the bouncer can be bypassed when sending the
+ * RemoteInput.
+ */
+ public interface AuthBypassPredicate {
+ /**
+ * Determines if the RemoteInput can be sent without the bouncer. Should be checked the
+ * same frame that the RemoteInput is to be sent.
+ */
+ boolean canSendRemoteInputWithoutBouncer();
+ }
+
+ /** Shows the bouncer if necessary */
+ public interface BouncerChecker {
+ /**
+ * Shows the bouncer if necessary in order to send a RemoteInput.
+ *
+ * @return {@code true} if the bouncer was shown, {@code false} otherwise
+ */
+ boolean showBouncerIfNecessary();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 88b9c6c..d08f973 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -33,6 +33,7 @@
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.View;
+import android.view.WindowManager;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.policy.ScreenDecorationsUtils;
@@ -159,8 +160,10 @@
}
@Override
- public void onAnimationStart(RemoteAnimationTarget[] remoteAnimationTargets,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] remoteAnimationTargets,
RemoteAnimationTarget[] remoteAnimationWallpaperTargets,
+ RemoteAnimationTarget[] remoteAnimationNonAppTargets,
IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
throws RemoteException {
mMainExecutor.execute(() -> {
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 a03fc13..845d321 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.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 @@
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 @@
}
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 @@
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 @@
// 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 @@
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 0000000..4541ebf
--- /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 c49f6cb..905bccf 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 @@
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 @@
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 @@
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 @@
addViewsTransformingToSimilar(
conversationIconView,
conversationBadgeBg,
- expandButton,
+ expandBtn,
importanceRing,
facePileTop,
facePileBottom,
@@ -134,11 +132,9 @@
)
}
- 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 @@
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 @@
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 @@
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/NotificationDecoratedCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
index 7964845..301c372 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationDecoratedCustomViewWrapper.java
@@ -29,6 +29,30 @@
private View mWrappedView = null;
+ /**
+ * Determines if the standard template contains a custom view, injected by Notification.Builder
+ */
+ public static boolean hasCustomView(View v) {
+ return getWrappedCustomView(v) != null;
+ }
+
+ private static View getWrappedCustomView(View view) {
+ if (view == null) {
+ return null;
+ }
+ ViewGroup container = view.findViewById(
+ com.android.internal.R.id.notification_main_column);
+ if (container == null) {
+ return null;
+ }
+ Integer childIndex = (Integer) container.getTag(
+ com.android.internal.R.id.notification_custom_view_index_tag);
+ if (childIndex == null || childIndex == -1) {
+ return null;
+ }
+ return container.getChildAt(childIndex);
+ }
+
protected NotificationDecoratedCustomViewWrapper(Context ctx, View view,
ExpandableNotificationRow row) {
super(ctx, view, row);
@@ -36,13 +60,7 @@
@Override
public void onContentUpdated(ExpandableNotificationRow row) {
- ViewGroup container = mView.findViewById(
- com.android.internal.R.id.notification_main_column);
- Integer childIndex = (Integer) container.getTag(
- com.android.internal.R.id.notification_custom_view_index_tag);
- if (childIndex != null && childIndex != -1) {
- mWrappedView = container.getChildAt(childIndex);
- }
+ mWrappedView = getWrappedCustomView(mView);
// Custom views will most likely use just white or black as their text color.
// We need to scan through and replace these colors by Material NEXT colors.
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 97201f5..34bc537 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 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 @@
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 @@
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 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 @@
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 @@
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 @@
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 7b5c5f6..5fff8c8 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,12 +74,17 @@
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();
if (Notification.DecoratedCustomViewStyle.class.equals(style)) {
return new NotificationDecoratedCustomViewWrapper(ctx, v, row);
}
+ if (NotificationDecoratedCustomViewWrapper.hasCustomView(v)) {
+ return new NotificationDecoratedCustomViewWrapper(ctx, v, row);
+ }
return new NotificationTemplateViewWrapper(ctx, v, row);
} else if (v instanceof NotificationHeaderView) {
return new NotificationHeaderViewWrapper(ctx, v, row);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index e6efba7..5d2203b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -286,8 +286,7 @@
float currentYPosition = -algorithmState.scrollY;
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
- currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition,
- false /* reverse */);
+ currentYPosition = updateChild(i, algorithmState, ambientState, currentYPosition);
}
}
@@ -301,10 +300,6 @@
* @param currentYPosition The Y position of the current pass of the algorithm. For a forward
* pass, this should be the top of the child; for a reverse pass, the
* bottom of the child.
- * @param reverse Whether we're laying out children in the reverse direction (Y
- * positions
- * decreasing) instead of the forward direction (Y positions
- * increasing).
* @return The Y position after laying out the child. This will be the {@code currentYPosition}
* for the next call to this method, after adjusting for any gaps between children.
*/
@@ -312,8 +307,7 @@
int i,
StackScrollAlgorithmState algorithmState,
AmbientState ambientState,
- float currentYPosition,
- boolean reverse) {
+ float currentYPosition) {
ExpandableView child = algorithmState.visibleChildren.get(i);
ExpandableView previousChild = i > 0 ? algorithmState.visibleChildren.get(i - 1) : null;
final boolean applyGapHeight =
@@ -323,20 +317,12 @@
ExpandableViewState childViewState = child.getViewState();
childViewState.location = ExpandableViewState.LOCATION_UNKNOWN;
- if (applyGapHeight && !reverse) {
+ if (applyGapHeight) {
currentYPosition += mGapHeight;
}
-
int childHeight = getMaxAllowedChildHeight(child);
- if (reverse) {
- childViewState.yTranslation = currentYPosition
- - (childHeight + mPaddingBetweenElements);
- if (currentYPosition <= 0) {
- childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
- }
- } else {
- childViewState.yTranslation = currentYPosition;
- }
+ childViewState.yTranslation = currentYPosition;
+
boolean isFooterView = child instanceof FooterView;
boolean isEmptyShadeView = child instanceof EmptyShadeView;
@@ -362,16 +348,9 @@
clampPositionToShelf(child, childViewState, ambientState);
}
- if (reverse) {
- currentYPosition = childViewState.yTranslation;
- if (applyGapHeight) {
- currentYPosition -= mGapHeight;
- }
- } else {
- currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
- if (currentYPosition <= 0) {
- childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
- }
+ currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
+ if (currentYPosition <= 0) {
+ childViewState.location = ExpandableViewState.LOCATION_HIDDEN_TOP;
}
if (childViewState.location == ExpandableViewState.LOCATION_UNKNOWN) {
Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 8c2fa33..85d8df8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -29,6 +29,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.tuner.TunerService;
@@ -54,6 +55,7 @@
private final AlwaysOnDisplayPolicy mAlwaysOnPolicy;
private final Resources mResources;
private final BatteryController mBatteryController;
+ private final FeatureFlags mFeatureFlags;
private boolean mDozeAlwaysOn;
private boolean mControlScreenOffAnimation;
@@ -65,7 +67,8 @@
AlwaysOnDisplayPolicy alwaysOnDisplayPolicy,
PowerManager powerManager,
BatteryController batteryController,
- TunerService tunerService) {
+ TunerService tunerService,
+ FeatureFlags featureFlags) {
mResources = resources;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mAlwaysOnPolicy = alwaysOnDisplayPolicy;
@@ -74,6 +77,7 @@
mControlScreenOffAnimation = !getDisplayNeedsBlanking();
mPowerManager = powerManager;
mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation);
+ mFeatureFlags = featureFlags;
tunerService.addTunable(
this,
@@ -200,8 +204,7 @@
* then abruptly showing AOD.
*/
public boolean shouldControlUnlockedScreenOff() {
- return getAlwaysOn() && SystemProperties.getBoolean(
- "persist.sysui.show_new_screen_on_transitions", false);
+ return getAlwaysOn() && mFeatureFlags.useNewLockscreenAnimations();
}
private boolean getBoolean(String propName, int resId) {
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 0a366c9..2ce0a87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -48,6 +48,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.MediaStore;
+import android.provider.Settings;
import android.service.media.CameraPrewarmService;
import android.telecom.TelecomManager;
import android.text.TextUtils;
@@ -60,6 +61,7 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
@@ -71,6 +73,10 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.controls.dagger.ControlsComponent;
+import com.android.systemui.controls.ui.ControlsDialog;
+import com.android.systemui.controls.ui.ControlsUiController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.IntentButtonProvider;
import com.android.systemui.plugins.IntentButtonProvider.IntentButton;
@@ -117,14 +123,16 @@
private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
+ // TODO(b/179494051): May no longer be needed
private final boolean mShowLeftAffordance;
private final boolean mShowCameraAffordance;
private KeyguardAffordanceView mRightAffordanceView;
private KeyguardAffordanceView mLeftAffordanceView;
+ private ImageView mAltLeftButton;
private ViewGroup mIndicationArea;
- private TextView mEnterpriseDisclosure;
private TextView mIndicationText;
+ private TextView mIndicationTextBottom;
private ViewGroup mPreviewContainer;
private ViewGroup mOverlayContainer;
@@ -171,6 +179,11 @@
private int mBurnInYOffset;
private ActivityIntentHelper mActivityIntentHelper;
+ private ControlsDialog mControlsDialog;
+ private ControlsComponent mControlsComponent;
+ private int mLockScreenMode;
+ private BroadcastDispatcher mBroadcastDispatcher;
+
public KeyguardBottomAreaView(Context context) {
this(context, null);
}
@@ -236,10 +249,10 @@
mOverlayContainer = findViewById(R.id.overlay_container);
mRightAffordanceView = findViewById(R.id.camera_button);
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(
@@ -316,7 +329,7 @@
}
// 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,
@@ -334,6 +347,11 @@
lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height);
mLeftAffordanceView.setLayoutParams(lp);
updateLeftAffordanceIcon();
+
+ lp = mAltLeftButton.getLayoutParams();
+ lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width);
+ lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height);
+ mAltLeftButton.setLayoutParams(lp);
}
private void updateRightAffordanceIcon() {
@@ -392,10 +410,17 @@
}
private void updateLeftAffordanceIcon() {
+ if (mDozing) {
+ mAltLeftButton.setVisibility(GONE);
+ } else if (mAltLeftButton.getDrawable() != null) {
+ mAltLeftButton.setVisibility(VISIBLE);
+ }
+
if (!mShowLeftAffordance || mDozing) {
mLeftAffordanceView.setVisibility(GONE);
return;
}
+
IconState state = mLeftButton.getIcon();
mLeftAffordanceView.setVisibility(state.isVisible ? View.VISIBLE : View.GONE);
if (state.drawable != mLeftAffordanceView.getDrawable()
@@ -669,6 +694,9 @@
public void startFinishDozeAnimation() {
long delay = 0;
+ if (mAltLeftButton.getVisibility() == View.VISIBLE) {
+ startFinishDozeAnimationElement(mAltLeftButton, delay);
+ }
if (mLeftAffordanceView.getVisibility() == View.VISIBLE) {
startFinishDozeAnimationElement(mLeftAffordanceView, delay);
delay += DOZE_ANIMATION_STAGGER_DELAY;
@@ -744,6 +772,10 @@
if (dozing) {
mOverlayContainer.setVisibility(INVISIBLE);
+ if (mControlsDialog != null) {
+ mControlsDialog.dismiss();
+ mControlsDialog = null;
+ }
} else {
mOverlayContainer.setVisibility(VISIBLE);
if (animate) {
@@ -773,6 +805,7 @@
mLeftAffordanceView.setAlpha(alpha);
mRightAffordanceView.setAlpha(alpha);
mIndicationArea.setAlpha(alpha);
+ mAltLeftButton.setAlpha(alpha);
}
private class DefaultLeftButton implements IntentButton {
@@ -844,4 +877,54 @@
}
return insets;
}
+
+ /**
+ * Show or hide controls, depending on the lock screen mode and controls
+ * availability.
+ */
+ public void setupControls(ControlsComponent component, BroadcastDispatcher dispatcher) {
+ mControlsComponent = component;
+ mBroadcastDispatcher = dispatcher;
+ setupControls();
+ }
+
+ private void setupControls() {
+ if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
+ mAltLeftButton.setVisibility(View.GONE);
+ mAltLeftButton.setOnClickListener(null);
+ return;
+ }
+
+ if (Settings.Global.getInt(mContext.getContentResolver(), "controls_lockscreen", 0) == 0) {
+ return;
+ }
+
+ if (mControlsComponent.getControlsListingController().isPresent()) {
+ mControlsComponent.getControlsListingController().get()
+ .addCallback(list -> {
+ if (!list.isEmpty()) {
+ mAltLeftButton.setImageDrawable(list.get(0).loadIcon());
+ mAltLeftButton.setVisibility(View.VISIBLE);
+ mAltLeftButton.setOnClickListener((v) -> {
+ ControlsUiController ui = mControlsComponent
+ .getControlsUiController().get();
+ mControlsDialog = new ControlsDialog(mContext, mBroadcastDispatcher)
+ .show(ui);
+ });
+
+ } else {
+ mAltLeftButton.setVisibility(View.GONE);
+ mAltLeftButton.setOnClickListener(null);
+ }
+ });
+ }
+ }
+
+ /**
+ * Optionally add controls when in the new lockscreen mode
+ */
+ public void onLockScreenModeChanged(int mode) {
+ mLockScreenMode = mode;
+ setupControls();
+ }
}
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 30c951a..8970a9a 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 com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Interpolators;
+import com.android.systemui.keyguard.KeyguardIndication;
import java.util.LinkedList;
@@ -35,13 +40,15 @@
* 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 @@
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 @@
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 @@
}
}
- /**
- * 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/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 3b47594..e0ef3b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -80,8 +80,10 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeLog;
@@ -229,6 +231,7 @@
public void onLockScreenModeChanged(int mode) {
mLockScreenMode = mode;
mClockPositionAlgorithm.onLockScreenModeChanged(mode);
+ mKeyguardBottomArea.onLockScreenModeChanged(mode);
}
@Override
@@ -295,6 +298,8 @@
private final QSDetailDisplayer mQSDetailDisplayer;
private final FeatureFlags mFeatureFlags;
private final ScrimController mScrimController;
+ private final ControlsComponent mControlsComponent;
+
// Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card.
// If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications
private final int mMaxKeyguardNotifications;
@@ -503,6 +508,7 @@
private NotificationShelfController mNotificationShelfController;
private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
+ private BroadcastDispatcher mBroadcastDispatcher;
private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
@Override
@@ -557,8 +563,9 @@
ScrimController scrimController,
MediaDataManager mediaDataManager,
AmbientState ambientState,
- FeatureFlags featureFlags
- ) {
+ FeatureFlags featureFlags,
+ ControlsComponent controlsComponent,
+ BroadcastDispatcher broadcastDispatcher) {
super(view, falsingManager, dozeLog, keyguardStateController,
(SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager,
@@ -592,6 +599,7 @@
mBiometricUnlockController = biometricUnlockController;
mScrimController = scrimController;
mMediaDataManager = mediaDataManager;
+ mControlsComponent = controlsComponent;
pulseExpansionHandler.setPulseExpandAbortListener(() -> {
if (mQs != null) {
mQs.animateHeaderSlidingOut();
@@ -630,6 +638,7 @@
mEntryManager = notificationEntryManager;
mConversationNotificationManager = conversationNotificationManager;
mAuthController = authController;
+ mBroadcastDispatcher = broadcastDispatcher;
mView.setBackgroundColor(Color.TRANSPARENT);
OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener();
@@ -838,6 +847,7 @@
mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper);
mKeyguardBottomArea.setStatusBar(mStatusBar);
mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete);
+ mKeyguardBottomArea.setupControls(mControlsComponent, mBroadcastDispatcher);
}
private void updateMaxDisplayedNotifications(boolean recompute) {
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 7095afd..1e19bee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -35,7 +35,6 @@
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
-import static com.android.systemui.statusbar.LightRevealScrimKt.getEnableLightReveal;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -181,6 +180,7 @@
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CrossFadeHelper;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyboardShortcuts;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -442,6 +442,7 @@
private final KeyguardViewMediator mKeyguardViewMediator;
protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final BrightnessSlider.Factory mBrightnessSliderFactory;
+ private final FeatureFlags mFeatureFlags;
private final List<ExpansionChangedListener> mExpansionChangedListeners;
@@ -765,7 +766,8 @@
Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
NotificationIconAreaController notificationIconAreaController,
- BrightnessSlider.Factory brightnessSliderFactory) {
+ BrightnessSlider.Factory brightnessSliderFactory,
+ FeatureFlags featureFlags) {
super(context);
mNotificationsController = notificationsController;
mLightBarController = lightBarController;
@@ -843,6 +845,7 @@
mDemoModeController = demoModeController;
mNotificationIconAreaController = notificationIconAreaController;
mBrightnessSliderFactory = brightnessSliderFactory;
+ mFeatureFlags = featureFlags;
mExpansionChangedListeners = new ArrayList<>();
@@ -1143,8 +1146,6 @@
mLockscreenWallpaper = mLockscreenWallpaperLazy.get();
}
- mKeyguardIndicationController.setIndicationArea(
- mNotificationShadeWindowView.findViewById(R.id.keyguard_indication_area));
mNotificationPanelViewController.setKeyguardIndicationController(
mKeyguardIndicationController);
@@ -1186,9 +1187,11 @@
mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim);
- if (getEnableLightReveal()) {
+ if (mFeatureFlags.useNewLockscreenAnimations() && mDozeParameters.getAlwaysOn()) {
mLightRevealScrim.setVisibility(View.VISIBLE);
mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE);
+ } else {
+ mLightRevealScrim.setVisibility(View.GONE);
}
mNotificationPanelViewController.initDependencies(
@@ -3620,26 +3623,19 @@
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();
@@ -3651,7 +3647,7 @@
@Override
public void onDozeAmountChanged(float linear, float eased) {
- if (getEnableLightReveal()) {
+ if (mFeatureFlags.useNewLockscreenAnimations()) {
mLightRevealScrim.setRevealAmount(1f - linear);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 36519ac..983b296e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -180,8 +180,8 @@
@Override
public void onMakeExpandedVisibleForRemoteInput(ExpandableNotificationRow row,
- View clickedView) {
- if (mKeyguardStateController.isShowing()) {
+ View clickedView, boolean deferBouncer, Runnable runnable) {
+ if (!deferBouncer && mKeyguardStateController.isShowing()) {
onLockedRemoteInput(row, clickedView);
} else {
if (row.isChildInGroup() && !row.areChildrenExpanded()) {
@@ -189,7 +189,7 @@
mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
}
row.setUserExpanded(true);
- row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick);
+ row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 9e9533d..b572c57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -47,6 +47,7 @@
import com.android.systemui.settings.brightness.BrightnessSlider;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -200,7 +201,8 @@
DismissCallbackRegistry dismissCallbackRegistry,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
NotificationIconAreaController notificationIconAreaController,
- BrightnessSlider.Factory brightnessSliderFactory) {
+ BrightnessSlider.Factory brightnessSliderFactory,
+ FeatureFlags featureFlags) {
return new StatusBar(
context,
notificationsController,
@@ -279,6 +281,7 @@
notificationShadeDepthController,
statusBarTouchableRegionManager,
notificationIconAreaController,
- brightnessSliderFactory);
+ brightnessSliderFactory,
+ featureFlags);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
index 08a4492..ccaa1f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -25,6 +25,8 @@
import com.android.systemui.statusbar.policy.NetworkController.IconState;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
@@ -42,11 +44,18 @@
private static final int MSG_MOBILE_DATA_ENABLED_CHANGED = 5;
private static final int MSG_ADD_REMOVE_EMERGENCY = 6;
private static final int MSG_ADD_REMOVE_SIGNAL = 7;
+ private static final int HISTORY_SIZE = 64;
+ private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
// All the callbacks.
private final ArrayList<EmergencyListener> mEmergencyListeners = new ArrayList<>();
private final ArrayList<SignalCallback> mSignalCallbacks = new ArrayList<>();
+ // Save the previous HISTORY_SIZE states for logging.
+ private final String[] mHistory = new String[HISTORY_SIZE];
+ // Where to copy the next state into.
+ private int mHistoryIndex;
+
public CallbackHandler() {
super(Looper.getMainLooper());
}
@@ -111,12 +120,27 @@
public void setWifiIndicators(final boolean enabled, final IconState statusIcon,
final IconState qsIcon, final boolean activityIn, final boolean activityOut,
final String description, boolean isTransient, String secondaryLabel) {
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("setWifiIndicators: ")
+ .append("enabled=").append(enabled).append(",")
+ .append("statusIcon=").append(statusIcon).append(",")
+ .append("qsIcon=").append(qsIcon).append(",")
+ .append("activityIn=").append(activityIn).append(",")
+ .append("activityOut=").append(activityOut).append(",")
+ .append("description=").append(description).append(",")
+ .append("isTransient=").append(isTransient).append(",")
+ .append("secondaryLabel=").append(secondaryLabel)
+ .toString();
+ recordLastCallback(log);
post(() -> {
for (SignalCallback callback : mSignalCallbacks) {
callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut,
description, isTransient, secondaryLabel);
}
});
+
+
}
@Override
@@ -125,6 +149,25 @@
final boolean activityOut, final CharSequence typeContentDescription,
CharSequence typeContentDescriptionHtml, final CharSequence description,
final boolean isWide, final int subId, boolean roaming, boolean showTriangle) {
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("setMobileDataIndicators: ")
+ .append("statusIcon=").append(statusIcon).append(",")
+ .append("qsIcon=").append(qsIcon).append(",")
+ .append("statusType=").append(statusType).append(",")
+ .append("qsType=").append(qsType).append(",")
+ .append("activityIn=").append(activityIn).append(",")
+ .append("activityOut=").append(activityOut).append(",")
+ .append("typeContentDescription=").append(typeContentDescription).append(",")
+ .append("typeContentDescriptionHtml=").append(typeContentDescriptionHtml)
+ .append(",")
+ .append("description=").append(description).append(",")
+ .append("isWide=").append(isWide).append(",")
+ .append("subId=").append(subId).append(",")
+ .append("roaming=").append(roaming).append(",")
+ .append("showTriangle=").append(showTriangle)
+ .toString();
+ recordLastCallback(log);
post(() -> {
for (SignalCallback signalCluster : mSignalCallbacks) {
signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType,
@@ -138,6 +181,14 @@
@Override
public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork,
boolean noNetworksAvailable) {
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("setConnectivityStatus: ")
+ .append("noDefaultNetwork=").append(noDefaultNetwork).append(",")
+ .append("noValidatedNetwork=").append(noValidatedNetwork).append(",")
+ .append("noNetworksAvailable=").append(noNetworksAvailable)
+ .toString();
+ recordLastCallback(log);
post(() -> {
for (SignalCallback signalCluster : mSignalCallbacks) {
signalCluster.setConnectivityStatus(
@@ -148,6 +199,13 @@
@Override
public void setNoCallingStatus(boolean noCalling, int subId) {
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("setNoCallingStatus: ")
+ .append("noCalling=").append(noCalling).append(",")
+ .append("subId=").append(subId)
+ .toString();
+ recordLastCallback(log);
post(() -> {
for (SignalCallback signalCluster : mSignalCallbacks) {
signalCluster.setNoCallingStatus(noCalling, subId);
@@ -157,16 +215,35 @@
@Override
public void setSubs(List<SubscriptionInfo> subs) {
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("setSubs: ")
+ .append("subs=").append(subs == null ? "" : subs.toString())
+ .toString();
+ recordLastCallback(log);
obtainMessage(MSG_SUBS_CHANGED, subs).sendToTarget();
}
@Override
public void setNoSims(boolean show, boolean simDetected) {
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("setNoSims: ")
+ .append("show=").append(show).append(",")
+ .append("simDetected=").append(simDetected)
+ .toString();
+ recordLastCallback(log);
obtainMessage(MSG_NO_SIM_VISIBLE_CHANGED, show ? 1 : 0, simDetected ? 1 : 0).sendToTarget();
}
@Override
public void setMobileDataEnabled(boolean enabled) {
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("setMobileDataEnabled: ")
+ .append("enabled=").append(enabled)
+ .toString();
+ recordLastCallback(log);
obtainMessage(MSG_MOBILE_DATA_ENABLED_CHANGED, enabled ? 1 : 0, 0).sendToTarget();
}
@@ -177,11 +254,23 @@
@Override
public void setEthernetIndicators(IconState icon) {
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("setEthernetIndicators: ")
+ .append("icon=").append(icon)
+ .toString();
+ recordLastCallback(log);
obtainMessage(MSG_ETHERNET_CHANGED, icon).sendToTarget();;
}
@Override
public void setIsAirplaneMode(IconState icon) {
+ String log = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("setIsAirplaneMode: ")
+ .append("icon=").append(icon)
+ .toString();
+ recordLastCallback(log);
obtainMessage(MSG_AIRPLANE_MODE_CHANGED, icon).sendToTarget();;
}
@@ -193,4 +282,25 @@
obtainMessage(MSG_ADD_REMOVE_SIGNAL, listening ? 1 : 0, 0, listener).sendToTarget();
}
+ protected void recordLastCallback(String callback) {
+ mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)] = callback;
+ }
+
+ /**
+ * Dump the Callback logs
+ */
+ public void dump(PrintWriter pw) {
+ pw.println(" - CallbackHandler -----");
+ int size = 0;
+ for (int i = 0; i < HISTORY_SIZE; i++) {
+ if (mHistory[i] != null) size++;
+ }
+ // Print out the previous states in ordered number.
+ for (int i = mHistoryIndex + HISTORY_SIZE - 1;
+ i >= mHistoryIndex + HISTORY_SIZE - size; i--) {
+ pw.println(" Previous Callback(" + (mHistoryIndex + HISTORY_SIZE - i) + "): "
+ + mHistory[i & (HISTORY_SIZE - 1)]);
+ }
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 39472de..0fe338e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -46,6 +46,7 @@
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.MobileMappings.Config;
import com.android.settingslib.mobile.MobileStatusTracker;
+import com.android.settingslib.mobile.MobileStatusTracker.MobileStatus;
import com.android.settingslib.mobile.MobileStatusTracker.SubscriptionDefaults;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.SignalStrengthUtil;
@@ -54,6 +55,7 @@
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.util.BitSet;
import java.util.List;
import java.util.Map;
@@ -62,6 +64,8 @@
* Monitors the mobile signal changes and update the SysUI icons.
*/
public class MobileSignalController extends SignalController<MobileState, MobileIconGroup> {
+ private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+
private final TelephonyManager mPhone;
private final SubscriptionDefaults mDefaults;
private final String mNetworkNameDefault;
@@ -90,6 +94,11 @@
@VisibleForTesting
MobileStatusTracker mMobileStatusTracker;
+ // Save the previous HISTORY_SIZE states for logging.
+ private final String[] mMobileStatusHistory = new String[HISTORY_SIZE];
+ // Where to copy the next state into.
+ private int mMobileStatusHistoryIndex;
+
// TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
// need listener lists anymore.
public MobileSignalController(Context context, Config config, boolean hasMobileData,
@@ -126,12 +135,17 @@
mCallback = new MobileStatusTracker.Callback() {
@Override
public void onMobileStatusChanged(boolean updateTelephony,
- MobileStatusTracker.MobileStatus mobileStatus) {
+ MobileStatus mobileStatus) {
if (Log.isLoggable(mTag, Log.DEBUG)) {
Log.d(mTag, "onMobileStatusChanged="
+ " updateTelephony=" + updateTelephony
+ " mobileStatus=" + mobileStatus.toString());
}
+ String status = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append(mobileStatus.toString())
+ .toString();
+ recordLastMobileStatus(status);
updateMobileStatus(mobileStatus);
if (updateTelephony) {
updateTelephony();
@@ -343,16 +357,8 @@
return Utils.isInService(mServiceState);
}
- String getNonDefaultCarrierName() {
- if (!mCurrentState.networkNameData.equals(mNetworkNameDefault)) {
- return mCurrentState.networkNameData;
- } else if (mSubscriptionInfo.getCarrierName() != null) {
- return mSubscriptionInfo.getCarrierName().toString();
- } else if (mSubscriptionInfo.getDisplayName() != null) {
- return mSubscriptionInfo.getDisplayName().toString();
- } else {
- return "";
- }
+ String getNetworkNameForCarrierWiFi() {
+ return mPhone.getSimOperatorName();
}
private boolean isRoaming() {
@@ -455,7 +461,7 @@
return CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
}
- private void updateMobileStatus(MobileStatusTracker.MobileStatus mobileStatus) {
+ private void updateMobileStatus(MobileStatus mobileStatus) {
mCurrentState.activityIn = mobileStatus.activityIn;
mCurrentState.activityOut = mobileStatus.activityOut;
mCurrentState.dataSim = mobileStatus.dataSim;
@@ -570,6 +576,10 @@
notifyListenersIfNecessary();
}
+ private void recordLastMobileStatus(String mobileStatus) {
+ mMobileStatusHistory[mMobileStatusHistoryIndex++ & (HISTORY_SIZE - 1)] = mobileStatus;
+ }
+
@Override
public void dump(PrintWriter pw) {
super.dump(pw);
@@ -580,5 +590,17 @@
pw.println(" mDataState=" + mDataState + ",");
pw.println(" mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
pw.println(" isDataDisabled=" + isDataDisabled() + ",");
+ pw.println(" MobileStatusHistory");
+ int size = 0;
+ for (int i = 0; i < HISTORY_SIZE; i++) {
+ if (mMobileStatusHistory[i] != null) size++;
+ }
+ // Print out the previous states in ordered number.
+ for (int i = mMobileStatusHistoryIndex + HISTORY_SIZE - 1;
+ i >= mMobileStatusHistoryIndex + HISTORY_SIZE - size; i--) {
+ pw.println(" Previous MobileStatus("
+ + (mMobileStatusHistoryIndex + HISTORY_SIZE - i) + "): "
+ + mMobileStatusHistory[i & (HISTORY_SIZE - 1)]);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index e13e30b..80c7811 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -76,6 +76,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
@@ -100,6 +101,8 @@
private static final int EMERGENCY_VOICE_CONTROLLER = 200;
private static final int EMERGENCY_NO_SUB = 300;
private static final int EMERGENCY_ASSUMED_VOICE_CONTROLLER = 400;
+ private static final int HISTORY_SIZE = 16;
+ private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
private final Context mContext;
private final TelephonyManager mPhone;
@@ -150,6 +153,11 @@
// This list holds our ordering.
private List<SubscriptionInfo> mCurrentSubscriptions = new ArrayList<>();
+ // Save the previous HISTORY_SIZE states for logging.
+ private final String[] mHistory = new String[HISTORY_SIZE];
+ // Where to copy the next state into.
+ private int mHistoryIndex;
+
@VisibleForTesting
boolean mListening;
@@ -307,6 +315,12 @@
public void onLost(Network network) {
mLastNetwork = null;
mLastNetworkCapabilities = null;
+ String callback = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("onLost: ")
+ .append("network=").append(network)
+ .toString();
+ recordLastNetworkCallback(callback);
updateConnectivity();
}
@@ -327,6 +341,13 @@
}
mLastNetwork = network;
mLastNetworkCapabilities = networkCapabilities;
+ String callback = new StringBuilder()
+ .append(SSDF.format(System.currentTimeMillis())).append(",")
+ .append("onCapabilitiesChanged: ")
+ .append("network=").append(network).append(",")
+ .append("networkCapabilities=").append(networkCapabilities)
+ .toString();
+ recordLastNetworkCallback(callback);
updateConnectivity();
}
};
@@ -528,9 +549,9 @@
return mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
}
- String getNonDefaultMobileDataNetworkName(int subId) {
+ String getNetworkNameForCarrierWiFi(int subId) {
MobileSignalController controller = getControllerWithSubId(subId);
- return controller != null ? controller.getNonDefaultCarrierName() : "";
+ return controller != null ? controller.getNetworkNameForCarrierWiFi() : "";
}
private void notifyControllersMobileDataChanged() {
@@ -996,6 +1017,19 @@
pw.print(" mEmergencySource=");
pw.println(emergencyToString(mEmergencySource));
+ pw.println(" - DefaultNetworkCallback -----");
+ int size = 0;
+ for (int i = 0; i < HISTORY_SIZE; i++) {
+ if (mHistory[i] != null) {
+ size++;
+ }
+ }
+ for (int i = mHistoryIndex + HISTORY_SIZE - 1;
+ i >= mHistoryIndex + HISTORY_SIZE - size; i--) {
+ pw.println(" Previous NetworkCallback(" + (mHistoryIndex + HISTORY_SIZE - i) + "): "
+ + mHistory[i & (HISTORY_SIZE - 1)]);
+ }
+
pw.println(" - config ------");
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
@@ -1006,6 +1040,8 @@
mEthernetSignalController.dump(pw);
mAccessPoints.dump(pw);
+
+ mCallbackHandler.dump(pw);
}
private static final String emergencyToString(int emergencySource) {
@@ -1235,6 +1271,10 @@
return s;
}
+ private void recordLastNetworkCallback(String callback) {
+ mHistory[mHistoryIndex++ & (HISTORY_SIZE - 1)] = callback;
+ }
+
private SubscriptionInfo addSignalController(int id, int simSlotIndex) {
SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
null, null, null, "", false, null, null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 9380d91..8e833c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -73,6 +73,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo;
@@ -80,6 +81,7 @@
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.LightBarController;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@@ -99,6 +101,8 @@
private final SendButtonTextWatcher mTextWatcher;
private final TextView.OnEditorActionListener mEditorActionHandler;
+ private final NotificationRemoteInputManager mRemoteInputManager;
+ private final List<OnFocusChangeListener> mEditTextFocusChangeListeners = new ArrayList<>();
private RemoteEditText mEditText;
private ImageButton mSendButton;
private ProgressBar mProgressBar;
@@ -121,12 +125,14 @@
private boolean mResetting;
private NotificationViewWrapper mWrapper;
private Consumer<Boolean> mOnVisibilityChangedListener;
+ private NotificationRemoteInputManager.BouncerChecker mBouncerChecker;
public RemoteInputView(Context context, AttributeSet attrs) {
super(context, attrs);
mTextWatcher = new SendButtonTextWatcher();
mEditorActionHandler = new EditorActionHandler();
mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class);
+ mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
mStatusBarManagerService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
}
@@ -200,6 +206,11 @@
}
private void sendRemoteInput(Intent intent) {
+ if (mBouncerChecker != null && mBouncerChecker.showBouncerIfNecessary()) {
+ mEditText.hideIme();
+ return;
+ }
+
mEditText.setEnabled(false);
mSendButton.setVisibility(INVISIBLE);
mProgressBar.setVisibility(VISIBLE);
@@ -351,6 +362,11 @@
}
}
+ /** Populates the text field of the remote input with the given content. */
+ public void setEditTextContent(@Nullable CharSequence editTextContent) {
+ mEditText.setText(editTextContent);
+ }
+
public void focusAnimated() {
if (getVisibility() != VISIBLE) {
Animator animator = ViewAnimationUtils.createCircularReveal(
@@ -552,6 +568,37 @@
return getVisibility() == VISIBLE && mController.isSpinning(mEntry.getKey(), mToken);
}
+ /**
+ * Sets a {@link com.android.systemui.statusbar.NotificationRemoteInputManager.BouncerChecker}
+ * that will be used to determine if the device needs to be unlocked before sending the
+ * RemoteInput.
+ */
+ public void setBouncerChecker(
+ @Nullable NotificationRemoteInputManager.BouncerChecker bouncerChecker) {
+ mBouncerChecker = bouncerChecker;
+ }
+
+ /** Registers a listener for focus-change events on the EditText */
+ public void addOnEditTextFocusChangedListener(View.OnFocusChangeListener listener) {
+ mEditTextFocusChangeListeners.add(listener);
+ }
+
+ /** Removes a previously-added listener for focus-change events on the EditText */
+ public void removeOnEditTextFocusChangedListener(View.OnFocusChangeListener listener) {
+ mEditTextFocusChangeListeners.remove(listener);
+ }
+
+ /** Determines if the EditText has focus. */
+ public boolean editTextHasFocus() {
+ return mEditText != null && mEditText.hasFocus();
+ }
+
+ private void onEditTextFocusChanged(RemoteEditText remoteEditText, boolean focused) {
+ for (View.OnFocusChangeListener listener : mEditTextFocusChangeListeners) {
+ listener.onFocusChange(remoteEditText, focused);
+ }
+ }
+
/** Handler for button click on send action in IME. */
private class EditorActionHandler implements TextView.OnEditorActionListener {
@@ -603,6 +650,7 @@
private RemoteInputView mRemoteInputView;
boolean mShowImeOnInputConnection;
private LightBarController mLightBarController;
+ private InputMethodManager mInputMethodManager;
UserHandle mUser;
public RemoteEditText(Context context, AttributeSet attrs) {
@@ -621,6 +669,12 @@
setOnReceiveContentListener(types, listener);
}
+ private void hideIme() {
+ if (mInputMethodManager != null) {
+ mInputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
+ }
+ }
+
private void defocusIfNeeded(boolean animate) {
if (mRemoteInputView != null && mRemoteInputView.mEntry.getRow().isChangingPosition()
|| isTemporarilyDetached()) {
@@ -654,6 +708,9 @@
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
+ if (mRemoteInputView != null) {
+ mRemoteInputView.onEditTextFocusChanged(this, focused);
+ }
if (!focused) {
defocusIfNeeded(true /* animate */);
}
@@ -724,17 +781,16 @@
if (mShowImeOnInputConnection && ic != null) {
Context targetContext = userContext != null ? userContext : getContext();
- final InputMethodManager imm =
- targetContext.getSystemService(InputMethodManager.class);
- if (imm != null) {
+ mInputMethodManager = targetContext.getSystemService(InputMethodManager.class);
+ if (mInputMethodManager != null) {
// onCreateInputConnection is called by InputMethodManager in the middle of
// setting up the connection to the IME; wait with requesting the IME until that
// work has completed.
post(new Runnable() {
@Override
public void run() {
- imm.viewClicked(RemoteEditText.this);
- imm.showSoftInput(RemoteEditText.this, 0);
+ mInputMethodManager.viewClicked(RemoteEditText.this);
+ mInputMethodManager.showSoftInput(RemoteEditText.this, 0);
}
});
}
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 4f4a504..738cab1 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.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 @@
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 @@
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 @@
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 0552396..68d74ef 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 @@
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 @@
}
mForcePictureLoadForUserId.clear();
- final boolean addUsersWhenLocked = mAddUsersWhenLocked;
+ final boolean addUsersWhenLocked = mAddUsersFromLockScreen;
new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() {
@SuppressWarnings("unchecked")
@Override
@@ -554,7 +556,7 @@
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 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) {
- break;
- } else {
- count++;
+ if (getUsers().get(i).isGuest && !includeGuest) {
+ continue;
}
+ if (getUsers().get(i).isRestricted && keyguardShowing) {
+ break;
+ }
+ count++;
}
return count;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index b2120d4..1fd2ccb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -147,7 +147,7 @@
IconState qsIcon = new IconState(
mCurrentState.connected, getQsCurrentIconIdForCarrierWifi(), contentDescription);
CharSequence description =
- mNetworkController.getNonDefaultMobileDataNetworkName(mCurrentState.subId);
+ mNetworkController.getNetworkNameForCarrierWiFi(mCurrentState.subId);
callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
mCurrentState.activityIn, mCurrentState.activityOut, dataContentDescription,
dataContentDescriptionHtml, description, icons.isWide,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index b7aa907..b42dde6 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -75,6 +75,7 @@
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.transition.RemoteTransitions;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -179,7 +180,7 @@
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
Optional<LegacySplitScreen> legacySplitScreenOptional,
- Optional<SplitScreen> splitScreenOptional,
+ Optional<SplitScreenController> splitScreenOptional,
Optional<AppPairs> appPairsOptional,
FullscreenTaskListener fullscreenTaskListener,
Transitions transitions,
@@ -204,7 +205,7 @@
static Optional<ShellCommandHandler> provideShellCommandHandler(
ShellTaskOrganizer shellTaskOrganizer,
Optional<LegacySplitScreen> legacySplitScreenOptional,
- Optional<SplitScreen> splitScreenOptional,
+ Optional<SplitScreenController> splitScreenOptional,
Optional<Pip> pipOptional,
Optional<OneHanded> oneHandedOptional,
Optional<HideDisplayCutout> hideDisplayCutout,
@@ -319,12 +320,21 @@
@WMSingleton
@Provides
- static Optional<SplitScreen> provideSplitScreen(ShellTaskOrganizer shellTaskOrganizer,
+ static Optional<SplitScreen> provideSplitScreen(
+ Optional<SplitScreenController> splitScreenController) {
+ return splitScreenController.map((controller) -> controller.asSplitScreen());
+ }
+
+ @WMSingleton
+ @Provides
+ static Optional<SplitScreenController> provideSplitScreenController(
+ ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue, Context context,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ @ShellMainThread ShellExecutor mainExecutor) {
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context,
- rootTaskDisplayAreaOrganizer));
+ rootTaskDisplayAreaOrganizer, mainExecutor));
} else {
return Optional.empty();
}
@@ -388,6 +398,12 @@
@WMSingleton
@Provides
+ static RemoteTransitions provideRemoteTransitions(Transitions transitions) {
+ return Transitions.asRemoteTransitions(transitions);
+ }
+
+ @WMSingleton
+ @Provides
static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool,
@ShellMainThread ShellExecutor mainExecutor,
@ShellAnimationThread ShellExecutor animExecutor) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index a2eaea1..70a7b7a 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 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 @@
NotificationIconAreaController mNotificationIconAreaController;
@Mock
ContentResolver mContentResolver;
+ @Mock
+ BroadcastDispatcher mBroadcastDispatcher;
private KeyguardClockSwitchController mController;
@@ -94,7 +97,8 @@
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 53d84db..7b4f14d 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 kotlin.math.ceil
-private val PAINT = arrayListOf(TextPaint().apply {
+private val PAINT = TextPaint().apply {
textSize = 32f
-})
+}
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -49,10 +49,10 @@
@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 @@
@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 1206dab..149e179 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.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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 0000000..b6729ad
--- /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 b7d1bc6..4ee2759 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.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.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 @@
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 @@
.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 @@
private ContentResolver mMockContentResolver;
@Mock
private Context mMockContext;
+ @Mock
+ private NotificationEntryManager mNotificationEntryManager;
@Before
public void setUp() throws RemoteException {
@@ -152,15 +212,17 @@
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 @@
// 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 @@
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 @@
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 @@
// 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 @@
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 @@
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,52 +405,126 @@
@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 testAugmentTileFromStorageWithNotification() {
+ public void testAugmentTileFromNotification() {
+ StatusBarNotification sbn = new SbnBuilder()
+ .setNotification(mNotification1)
+ .build();
+
PeopleSpaceTile tile =
new PeopleSpaceTile
- .Builder("id", "userName", ICON, new Intent())
+ .Builder(SHORTCUT_ID_1, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
.build();
PeopleSpaceTile actual = PeopleSpaceUtils
- .augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITH_SHORTCUT);
+ .augmentTileFromNotification(tile, sbn);
- assertThat(actual.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
- assertThat(actual.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
- assertThat(actual.getNotificationDataUri()).isEqualTo(URI);
+ assertThat(actual.getNotificationContent().toString()).isEqualTo(NOTIFICATION_TEXT_2);
}
@Test
- public void testAugmentTileFromStorageWithoutNotification() {
+ public void testAugmentTileFromNotificationNoContent() {
+ StatusBarNotification sbn = new SbnBuilder()
+ .setNotification(mNotification3)
+ .build();
+
PeopleSpaceTile tile =
new PeopleSpaceTile
- .Builder("id", "userName", ICON, new Intent())
+ .Builder(SHORTCUT_ID_3, "userName", ICON, new Intent())
+ .setPackageName(PACKAGE_NAME)
+ .setUid(0)
.build();
PeopleSpaceTile actual = PeopleSpaceUtils
- .augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITHOUT_SHORTCUT);
+ .augmentTileFromNotification(tile, sbn);
assertThat(actual.getNotificationKey()).isEqualTo(null);
- assertThat(actual.getNotificationKey()).isEqualTo(null);
- assertThat(actual.getNotificationDataUri()).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 8c0afb8..9470141 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
@@ -21,6 +21,8 @@
import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -31,26 +33,24 @@
import static java.util.Objects.requireNonNull;
-import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.Person;
+import android.app.people.ConversationChannel;
+import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
+import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
-import android.service.notification.ConversationChannelWrapper;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
-import android.widget.RemoteViews;
import androidx.preference.PreferenceManager;
import androidx.test.filters.SmallTest;
@@ -58,6 +58,7 @@
import com.android.internal.appwidget.IAppWidgetService;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.people.PeopleSpaceUtils;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
import com.android.systemui.statusbar.SbnBuilder;
@@ -74,26 +75,27 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
private static final long MIN_LINGER_DURATION = 5;
- private static final String TEST_PACKAGE_A = "com.test.package_a";
+ private static final String TEST_PACKAGE_A = "com.android.systemui.tests";
private static final String TEST_PACKAGE_B = "com.test.package_b";
private static final String TEST_CHANNEL_ID = "channel_id";
private static final String TEST_CHANNEL_NAME = "channel_name";
private static final String TEST_PARENT_CHANNEL_ID = "parent_channel_id";
private static final String TEST_CONVERSATION_ID = "conversation_id";
private static final int WIDGET_ID_WITH_SHORTCUT = 1;
+ private static final int SECOND_WIDGET_ID_WITH_SHORTCUT = 3;
private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2;
private static final String SHORTCUT_ID = "101";
private static final String OTHER_SHORTCUT_ID = "102";
- private static final String NOTIFICATION_KEY = "notification_key";
- private static final String NOTIFICATION_CONTENT = "notification_content";
+ private static final String NOTIFICATION_KEY = "0|com.android.systemui.tests|0|null|0";
+ private static final String NOTIFICATION_CONTENT = "message text";
private static final Uri URI = Uri.parse("fake_uri");
private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
private static final Person PERSON = new Person.Builder()
@@ -105,10 +107,14 @@
private static final PeopleSpaceTile PERSON_TILE =
new PeopleSpaceTile
.Builder(SHORTCUT_ID, "username", ICON, new Intent())
- .setNotificationKey(NOTIFICATION_KEY)
+ .setPackageName(TEST_PACKAGE_A)
+ .setUid(0)
+ .setNotificationKey(NOTIFICATION_KEY + "1")
.setNotificationContent(NOTIFICATION_CONTENT)
.setNotificationDataUri(URI)
.build();
+ private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
+ SHORTCUT_ID).setLongLabel("name").build();
private PeopleSpaceWidgetManager mManager;
@@ -119,175 +125,101 @@
@Mock
private AppWidgetManager mAppWidgetManager;
@Mock
- private INotificationManager mINotificationManager;
+ private IPeopleManager mIPeopleManager;
@Captor
private ArgumentCaptor<NotificationHandler> mListenerCaptor;
+ @Captor
+ private ArgumentCaptor<Bundle> mBundleArgumentCaptor;
private final NoManSimulator mNoMan = new NoManSimulator();
private final FakeSystemClock mClock = new FakeSystemClock();
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mManager =
new PeopleSpaceWidgetManager(mContext);
- mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mINotificationManager);
+ mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mIPeopleManager);
mManager.attach(mListenerService);
verify(mListenerService).addNotificationHandler(mListenerCaptor.capture());
NotificationHandler serviceListener = requireNonNull(mListenerCaptor.getValue());
mNoMan.addListener(serviceListener);
+ // Default to single People tile widgets.
Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 2);
+ Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
- SharedPreferences.Editor editor = sp.edit();
- editor.putString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), SHORTCUT_ID);
- editor.apply();
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+
Bundle options = new Bundle();
- options.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
-
+ options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
.thenReturn(options);
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT)))
.thenReturn(new Bundle());
+ when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(
+ getConversationWithShortcutId(SHORTCUT_ID));
}
@Test
- public void testDoNotNotifyAppWidgetIfNoWidgets() throws RemoteException {
+ public void testDoNotUpdateAppWidgetIfNoWidgets() throws Exception {
int[] widgetIdsArray = {};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- NotifEvent notif1 = mNoMan.postNotif(
- new NotificationEntryBuilder()
- .setId(0)
- .setPkg(TEST_PACKAGE_A));
+ StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(sbn)
+ .setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testDoNotNotifySingleConversationAppWidgetIfNoWidgets() throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
+ public void testDoNotUpdateAppWidgetIfNoShortcutInfo() throws Exception {
int[] widgetIdsArray = {};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- NotifEvent notif1 = mNoMan.postNotif(
- new NotificationEntryBuilder()
- .setId(0)
- .setPkg(TEST_PACKAGE_A));
+ Notification notificationWithoutShortcut = new Notification.Builder(mContext)
+ .setContentTitle("TEST_TITLE")
+ .setContentText("TEST_TEXT")
+ .setStyle(new Notification.MessagingStyle(PERSON)
+ .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
+ )
+ .build();
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(new SbnBuilder()
+ .setNotification(notificationWithoutShortcut)
+ .setPkg(TEST_PACKAGE_A)
+ .build())
+ .setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), any(RemoteViews.class));
- verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testNotifyAppWidgetIfNotificationPosted() throws RemoteException {
- int[] widgetIdsArray = {1};
+ public void testDoNotUpdateAppWidgetIfNoPackage() throws Exception {
+ int[] widgetIdsArray = {};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- NotifEvent notif1 = mNoMan.postNotif(
- new NotificationEntryBuilder()
- .setId(0)
- .setPkg(TEST_PACKAGE_A));
+ StatusBarNotification sbnWithoutPackageName = new SbnBuilder()
+ .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+ .build();
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(sbnWithoutPackageName)
+ .setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mIAppWidgetService, times(1))
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mIAppWidgetService, never()).updateAppWidgetIds(any(), any(),
- any(RemoteViews.class));
- }
-
- @Test
- public void testNotifySingleConversationAppWidgetOnceIfNotificationPosted()
- throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
- int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
- NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_A)
- .setId(1));
-
- verify(mIAppWidgetService, never())
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
- any(RemoteViews.class));
- verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITHOUT_SHORTCUT),
- any(RemoteViews.class));
- }
-
- @Test
- public void testNotifySingleConversationAppWidgetTwiceIfTwoNotificationsPosted()
- throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
- int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
- NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_A)
- .setId(1));
- mClock.advanceTime(4);
- NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_B)
- .setId(2));
-
- verify(mIAppWidgetService, never())
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
- any(RemoteViews.class));
- verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITHOUT_SHORTCUT),
- any(RemoteViews.class));
- }
-
- @Test
- public void testNotifyAppWidgetTwiceIfTwoNotificationsPosted() throws RemoteException {
- int[] widgetIdsArray = {1, 2};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
- NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_A)
- .setId(1));
- mClock.advanceTime(4);
- NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_B)
- .setId(2));
-
- verify(mIAppWidgetService, times(2))
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
- any(RemoteViews.class));
+ any());
}
@Test
- public void testNotifyAppWidgetTwiceIfNotificationPostedAndRemoved() throws RemoteException {
- int[] widgetIdsArray = {1, 2};
- when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
- NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE_A)
- .setId(1));
- mClock.advanceTime(4);
- NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
-
- verify(mIAppWidgetService, times(2))
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
- any(RemoteViews.class));
- }
-
- @Test
- public void testDoNotNotifyAppWidgetIfNonConversationChannelModified() throws RemoteException {
+ public void testDoNotUpdateAppWidgetIfNonConversationChannelModified() throws Exception {
int[] widgetIdsArray = {1};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
@@ -298,13 +230,12 @@
UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
- any(RemoteViews.class));
+ any());
}
@Test
- public void testNotifyAppWidgetIfConversationChannelModified() throws RemoteException {
+ public void testUpdateAppWidgetIfConversationChannelModified() throws Exception {
int[] widgetIdsArray = {1};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
@@ -316,45 +247,57 @@
UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mIAppWidgetService, times(1))
- .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
- verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
- any(RemoteViews.class));
+ verify(mAppWidgetManager, times(1)).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testDoNotUpdateNotificationPostedIfNoExistingTile() throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ public void testDoNotUpdateNotificationPostedIfDifferentShortcutId() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
verify(mAppWidgetManager, never())
- .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+ .updateAppWidgetOptions(anyInt(), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testDoNotUpdateNotificationRemovedIfNoExistingTile() throws RemoteException {
+ public void testDoNotUpdateNotificationPostedIfDifferentPackageName() throws Exception {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
+ .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+ .setPkg(TEST_PACKAGE_B)
+ .build();
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(sbnWithDifferentPackageName)
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, never())
+ .updateAppWidgetOptions(anyInt(), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
+ }
+
+ @Test
+ public void testDoNotUpdateNotificationRemovedIfDifferentShortcutId() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
.setId(1));
mClock.advanceTime(4);
NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
@@ -362,99 +305,222 @@
verify(mAppWidgetManager, never())
.updateAppWidgetOptions(anyInt(), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
}
@Test
- public void testUpdateNotificationPostedIfExistingTile() throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ public void testDoNotUpdateNotificationRemovedIfDifferentPackageName() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
- StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID);
+ StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
+ .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+ .setPkg(TEST_PACKAGE_B)
+ .build();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
- .setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
+ .setSbn(sbnWithDifferentPackageName)
+ .setId(1));
+ mClock.advanceTime(4);
+ NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, never())
+ .updateAppWidgetOptions(anyInt(), any());
+ verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+ any());
+ }
+
+ @Test
+ public void testUpdateNotificationPostedIfExistingTile() throws Exception {
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(createConversationNotification(SHORTCUT_ID))
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
verify(mAppWidgetManager, times(1))
- .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle bundle = mBundleArgumentCaptor.getValue();
+ PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+ assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ }
+
+ @Test
+ public void testUpdateNotificationPostedOnTwoExistingTiles() throws Exception {
+ Bundle options = new Bundle();
+ options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+ when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
+ .thenReturn(options);
+ // Set the same Person associated on another People Tile widget ID.
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
+
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
+ SECOND_WIDGET_ID_WITH_SHORTCUT};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(createConversationNotification(SHORTCUT_ID))
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ any());
+ }
+
+ @Test
+ public void testUpdateNotificationOnExistingTileAfterRemovingTileForSamePerson()
+ throws Exception {
+ Bundle options = new Bundle();
+ options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+ when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
+ .thenReturn(options);
+ // Set the same Person associated on another People Tile widget ID.
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+ setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
+
+ int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
+ SECOND_WIDGET_ID_WITH_SHORTCUT};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ PeopleSpaceUtils.removeStorageForTile(mContext, SECOND_WIDGET_ID_WITH_SHORTCUT);
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setSbn(createConversationNotification(SHORTCUT_ID))
+ .setId(1));
+ mClock.advanceTime(MIN_LINGER_DURATION);
+
+ verify(mAppWidgetManager, times(1))
+ .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, never())
+ .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ any());
+ verify(mAppWidgetManager, never()).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+ any());
}
@Test
public void testDoNotUpdateNotificationPostedWithoutMessagesIfExistingTile()
- throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ 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 notification = new Notification.Builder(mContext)
+ Notification notificationWithoutMessagingStyle = new Notification.Builder(mContext)
.setContentTitle("TEST_TITLE")
.setContentText("TEST_TEXT")
.setShortcutId(SHORTCUT_ID)
.build();
StatusBarNotification sbn = new SbnBuilder()
- .setNotification(notification)
+ .setNotification(notificationWithoutMessagingStyle)
+ .setPkg(TEST_PACKAGE_A)
+ .setUid(0)
.build();
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mAppWidgetManager, never())
- .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+ 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());
}
@Test
- public void testUpdateNotificationRemovedIfExistingTile() throws RemoteException {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
- when(mINotificationManager.getConversations(false)).thenReturn(
- new ParceledListSlice(getConversationWithShortcutId()));
+ public void testUpdateNotificationRemovedIfExistingTile() throws Exception {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setSbn(sbn)
- .setPkg(TEST_PACKAGE_A)
.setId(1));
mClock.advanceTime(MIN_LINGER_DURATION);
NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
mClock.advanceTime(MIN_LINGER_DURATION);
- verify(mAppWidgetManager, times(2))
- .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+ verify(mAppWidgetManager, times(2)).updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+ mBundleArgumentCaptor.capture());
+ Bundle bundle = mBundleArgumentCaptor.getValue();
+ PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+ assertThat(tile.getNotificationKey()).isEqualTo(null);
+ assertThat(tile.getNotificationContent()).isEqualTo(null);
+ assertThat(tile.getNotificationDataUri()).isEqualTo(null);
+ verify(mAppWidgetManager, times(2)).updateAppWidget(anyInt(),
+ any());
}
- /** Returns a list of a single conversation associated with {@code SHORTCUT_ID}. */
- private List<ConversationChannelWrapper> getConversationWithShortcutId() {
- List<ConversationChannelWrapper> convos = new ArrayList<>();
- ConversationChannelWrapper convo1 = new ConversationChannelWrapper();
- convo1.setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID).setLongLabel(
- "name").build());
- convos.add(convo1);
- return convos;
+ /**
+ * Returns a single conversation associated with {@code shortcutId}.
+ */
+ private ConversationChannel getConversationWithShortcutId(String shortcutId) throws Exception {
+ ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
+ "name").build();
+ ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
+ 0L, false);
+ return convo;
}
- private StatusBarNotification createConversationNotification(String shortcutId) {
- Notification notification = new Notification.Builder(mContext)
+ private Notification createMessagingStyleNotification(String shortcutId) {
+ return new Notification.Builder(mContext)
.setContentTitle("TEST_TITLE")
.setContentText("TEST_TEXT")
.setShortcutId(shortcutId)
.setStyle(new Notification.MessagingStyle(PERSON)
- .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
+ .addMessage(
+ new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10,
+ PERSON))
)
.build();
+ }
+
+ private StatusBarNotification createConversationNotification(String shortcutId) {
+ Notification notification = createMessagingStyleNotification(shortcutId);
return new SbnBuilder()
.setNotification(notification)
+ .setPkg(TEST_PACKAGE_A)
.build();
}
+
+ private void setStorageForTile(String shortcutId, String packageName, int widgetId) {
+ SharedPreferences widgetSp = mContext.getSharedPreferences(
+ String.valueOf(widgetId),
+ Context.MODE_PRIVATE);
+ SharedPreferences.Editor widgetEditor = widgetSp.edit();
+ widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, packageName);
+ widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, shortcutId);
+ widgetEditor.putInt(PeopleSpaceUtils.USER_ID, 0);
+ widgetEditor.apply();
+
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putString(String.valueOf(widgetId), shortcutId);
+ String key = PeopleSpaceUtils.getKey(shortcutId, packageName, 0);
+ Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+ storedWidgetIds.add(String.valueOf(widgetId));
+ editor.putStringSet(key, storedWidgetIds);
+ editor.apply();
+ }
}
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 f86b465..072f7b8 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 @@
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 @@
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 @@
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 @@
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_PERMISSION_APPS)
- assertThat(intentCaptor.value.getStringExtra(Intent.EXTRA_PERMISSION_GROUP_NAME))
- .isEqualTo(PERM_MICROPHONE)
+ 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.getParcelableExtra(Intent.EXTRA_USER) as? UserHandle)
+ .isEqualTo(UserHandle.of(ENT_USER_ID))
}
@Test
@@ -473,7 +490,7 @@
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 @@
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 eb5dd4e..28b44d9 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 @@
@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 @@
}
@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 @@
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 @@
val list = listOf(
PrivacyDialog.PrivacyElement(
PrivacyType.TYPE_CAMERA,
+ TEST_PACKAGE_NAME,
+ TEST_USER_ID,
"App",
null,
0L,
@@ -113,6 +122,8 @@
),
PrivacyDialog.PrivacyElement(
PrivacyType.TYPE_MICROPHONE,
+ TEST_PACKAGE_NAME,
+ TEST_USER_ID,
"App",
null,
0L,
@@ -130,6 +141,8 @@
fun testUsingText() {
val element = PrivacyDialog.PrivacyElement(
PrivacyType.TYPE_CAMERA,
+ TEST_PACKAGE_NAME,
+ TEST_USER_ID,
"App",
null,
0L,
@@ -154,6 +167,8 @@
fun testRecentText() {
val element = PrivacyDialog.PrivacyElement(
PrivacyType.TYPE_MICROPHONE,
+ TEST_PACKAGE_NAME,
+ TEST_USER_ID,
"App",
null,
0L,
@@ -178,6 +193,8 @@
fun testEnterprise() {
val element = PrivacyDialog.PrivacyElement(
PrivacyType.TYPE_MICROPHONE,
+ TEST_PACKAGE_NAME,
+ TEST_USER_ID,
"App",
null,
0L,
@@ -198,6 +215,8 @@
fun testPhoneCall() {
val element = PrivacyDialog.PrivacyElement(
PrivacyType.TYPE_MICROPHONE,
+ TEST_PACKAGE_NAME,
+ TEST_USER_ID,
"App",
null,
0L,
@@ -218,6 +237,8 @@
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/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index 78af20f..ffd747e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
@@ -75,6 +75,7 @@
mFakeSettings = new FakeSettings();
mTile = new ReduceBrightColorsTile(
+ true,
mHost,
mTestableLooper.getLooper(),
new Handler(mTestableLooper.getLooper()),
@@ -95,7 +96,6 @@
assertEquals(Tile.STATE_INACTIVE, mTile.getState().state);
assertEquals(mTile.getState().label.toString(),
mContext.getString(R.string.quick_settings_reduce_bright_colors_label));
- assertEquals(mTile.getState().secondaryLabel.toString(), "");
}
@Test
@@ -128,13 +128,5 @@
assertEquals(Tile.STATE_ACTIVE, mTile.getState().state);
assertEquals(mTile.getState().label.toString(),
mContext.getString(R.string.quick_settings_reduce_bright_colors_label));
-
- final int intensity = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0, mUserTracker.getUserId());
-
- assertEquals(
- mContext.getString(
- R.string.quick_settings_reduce_bright_colors_secondary_label, intensity),
- mTile.getState().secondaryLabel.toString());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
index 451c78f..6d2b8e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.java
@@ -43,7 +43,7 @@
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreen;
-import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.transition.RemoteTransitions;
import org.junit.Before;
import org.junit.Test;
@@ -78,7 +78,7 @@
@Mock private Optional<com.android.wm.shell.onehanded.OneHanded> mMockOneHandedOptional;
@Mock private PackageManager mPackageManager;
@Mock private SysUiState mMockSysUiState;
- @Mock private Transitions mMockTransitions;
+ @Mock private RemoteTransitions mMockTransitions;
@Before
public void setUp() throws RemoteException {
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 2c96535..0cae6742 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 @@
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.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.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 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 @@
private static final String ORGANIZATION_NAME = "organization";
private String mDisclosureWithOrganization;
+ private String mDisclosureGeneric;
@Mock
private DevicePolicyManager mDevicePolicyManager;
@@ -101,7 +104,7 @@
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
- private KeyguardIndicationTextView mDisclosure;
+ private KeyguardIndicationTextView mIndicationAreaBottom;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
@@ -118,8 +121,20 @@
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 @@
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 @@
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 @@
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 @@
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 @@
@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();
+ 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_hiddenWhenDozing() {
- when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
- when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
+ public void disclosure_deviceOwner_withOrganizationName() {
createController();
-
- mController.setVisible(true);
- mController.onDozeAmountChanged(1, 1);
- mController.setDozing(true);
-
- verify(mDisclosure).setVisibility(View.VISIBLE);
- verify(mDisclosure).setAlpha(0f);
- verify(mDisclosure).switchIndication(R.string.do_disclosure_generic);
- verifyNoMoreInteractions(mDisclosure);
- }
-
- @Test
- public void disclosure_visibleWhenDozing() {
- when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
- when(mDevicePolicyManager.getDeviceOwnerOrganizationName()).thenReturn(null);
- 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 void transientIndication_releasesWakeLock_afterHiding() {
createController();
- mController.setDozing(true);
+ mStatusBarStateListener.onDozingChanged(true);
mController.showTransientIndication("Test");
mController.hideTransientIndication();
@@ -406,7 +397,7 @@
mInstrumentation.runOnMainSync(() -> {
createController();
- mController.setDozing(true);
+ mStatusBarStateListener.onDozingChanged(true);
mController.showTransientIndication("Test");
mController.hideTransientIndicationDelayed(0);
});
@@ -425,7 +416,7 @@
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 @@
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 @@
"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 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 void updateMonitor_listener() {
createController();
verify(mKeyguardStateController).addCallback(eq(mController));
- verify(mStatusBarStateController).addCallback(eq(mController));
verify(mKeyguardUpdateMonitor, times(2)).registerCallback(any());
}
@@ -612,10 +602,14 @@
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/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index fa253e6..b9fd75e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -35,6 +35,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.tuner.TunerService;
@@ -57,6 +58,7 @@
@Mock private PowerManager mPowerManager;
@Mock private TunerService mTunerService;
@Mock private BatteryController mBatteryController;
+ @Mock private FeatureFlags mFeatureFlags;
@Before
public void setup() {
@@ -67,7 +69,8 @@
mAlwaysOnDisplayPolicy,
mPowerManager,
mBatteryController,
- mTunerService
+ mTunerService,
+ mFeatureFlags
);
}
@Test
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 0e77504..6068f0d 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 @@
@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/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index fa78ca6..c07ba72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -61,8 +61,10 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.controls.dagger.ControlsComponent;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
@@ -207,6 +209,10 @@
@Mock
private FeatureFlags mFeatureFlags;
@Mock
+ private ControlsComponent mControlsComponent;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
private NotificationsQuickSettingsContainer mNotificationContainerParent;
@Mock
private AmbientState mAmbientState;
@@ -300,7 +306,9 @@
mScrimController,
mMediaDataManager,
mAmbientState,
- mFeatureFlags);
+ mFeatureFlags,
+ mControlsComponent,
+ mBroadcastDispatcher);
mNotificationPanelViewController.initDependencies(
mStatusBar,
mNotificationShelfController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index cae488a..253460d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -97,6 +97,7 @@
import com.android.systemui.settings.brightness.BrightnessSlider;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -258,6 +259,7 @@
@Mock private DemoModeController mDemoModeController;
@Mock private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
@Mock private BrightnessSlider.Factory mBrightnessSliderFactory;
+ @Mock private FeatureFlags mFeatureFlags;
private ShadeController mShadeController;
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private InitController mInitController = new InitController();
@@ -418,7 +420,8 @@
mNotificationShadeDepthControllerLazy,
mStatusBarTouchableRegionManager,
mNotificationIconAreaController,
- mBrightnessSliderFactory);
+ mBrightnessSliderFactory,
+ mFeatureFlags);
when(mNotificationShadeWindowView.findViewById(R.id.lock_icon_container)).thenReturn(
mLockIconContainer);
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
index 4d95ef1..6dcad25 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/AlwaysOnDisconnectedDialog.java
@@ -20,14 +20,11 @@
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.net.IConnectivityManager;
+import android.net.ConnectivityManager;
import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.SpannableStringBuilder;
@@ -45,7 +42,7 @@
private static final String TAG = "VpnDisconnected";
- private IConnectivityManager mService;
+ private ConnectivityManager mService;
private int mUserId;
private String mVpnPackage;
@@ -53,10 +50,9 @@
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mService = IConnectivityManager.Stub.asInterface(
- ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
mUserId = UserHandle.myUserId();
- mVpnPackage = getAlwaysOnVpnPackage();
+ final ConnectivityManager cm = getSystemService(ConnectivityManager.class);
+ mVpnPackage = cm.getAlwaysOnVpnPackageForUser(mUserId);
if (mVpnPackage == null) {
finish();
return;
@@ -102,15 +98,6 @@
}
}
- private String getAlwaysOnVpnPackage() {
- try {
- return mService.getAlwaysOnVpnPackage(mUserId);
- } catch (RemoteException e) {
- Log.e(TAG, "Can't getAlwaysOnVpnPackage()", e);
- return null;
- }
- }
-
private CharSequence getVpnLabel() {
try {
return VpnConfig.getVpnLabel(this, mVpnPackage);
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index e66f2cc..aab01d0 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -18,15 +18,12 @@
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
-import android.net.IConnectivityManager;
+import android.net.ConnectivityManager;
import android.net.VpnManager;
import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.Html;
@@ -48,7 +45,8 @@
private String mPackage;
- private IConnectivityManager mService;
+ private ConnectivityManager mCm; // TODO: switch entirely to VpnManager once VPN code moves
+ private VpnManager mVm;
public ConfirmDialog() {
this(VpnManager.TYPE_VPN_SERVICE);
@@ -62,10 +60,10 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPackage = getCallingPackage();
- mService = IConnectivityManager.Stub.asInterface(
- ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+ mCm = getSystemService(ConnectivityManager.class);
+ mVm = getSystemService(VpnManager.class);
- if (prepareVpn()) {
+ if (mVm.prepareVpn(mPackage, null, UserHandle.myUserId())) {
setResult(RESULT_OK);
finish();
return;
@@ -74,7 +72,7 @@
finish();
return;
}
- final String alwaysOnVpnPackage = getAlwaysOnVpnPackage();
+ final String alwaysOnVpnPackage = mCm.getAlwaysOnVpnPackageForUser(UserHandle.myUserId());
// Can't prepare new vpn app when another vpn is always-on
if (alwaysOnVpnPackage != null && !alwaysOnVpnPackage.equals(mPackage)) {
finish();
@@ -97,24 +95,6 @@
button.setFilterTouchesWhenObscured(true);
}
- private String getAlwaysOnVpnPackage() {
- try {
- return mService.getAlwaysOnVpnPackage(UserHandle.myUserId());
- } catch (RemoteException e) {
- Log.e(TAG, "fail to call getAlwaysOnVpnPackage", e);
- // Fallback to null to show the dialog
- return null;
- }
- }
-
- private boolean prepareVpn() {
- try {
- return mService.prepareVpn(mPackage, null, UserHandle.myUserId());
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
- }
-
private CharSequence getVpnLabel() {
try {
return VpnConfig.getVpnLabel(this, mPackage);
@@ -146,10 +126,10 @@
@Override
public void onClick(DialogInterface dialog, int which) {
try {
- if (mService.prepareVpn(null, mPackage, UserHandle.myUserId())) {
+ if (mVm.prepareVpn(null, mPackage, UserHandle.myUserId())) {
// Authorize this app to initiate VPN connections in the future without user
// intervention.
- mService.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType);
+ mVm.setVpnPackageAuthorization(mPackage, UserHandle.myUserId(), mVpnType);
setResult(RESULT_OK);
}
} catch (Exception e) {
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
index 01dca7e..1fc74f7 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -16,13 +16,11 @@
package com.android.vpndialogs;
-import android.content.Context;
import android.content.DialogInterface;
-import android.net.IConnectivityManager;
+import android.net.VpnManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
@@ -41,7 +39,7 @@
private VpnConfig mConfig;
- private IConnectivityManager mService;
+ private VpnManager mVm;
private TextView mDuration;
private TextView mDataTransmitted;
@@ -55,11 +53,9 @@
super.onCreate(savedInstanceState);
try {
+ mVm = getSystemService(VpnManager.class);
- mService = IConnectivityManager.Stub.asInterface(
- ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
-
- mConfig = mService.getVpnConfig(UserHandle.myUserId());
+ mConfig = mVm.getVpnConfig(UserHandle.myUserId());
// mConfig can be null if we are a restricted user, in that case don't show this dialog
if (mConfig == null) {
@@ -118,9 +114,9 @@
} else if (which == DialogInterface.BUTTON_NEUTRAL) {
final int myUserId = UserHandle.myUserId();
if (mConfig.legacy) {
- mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId);
+ mVm.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, myUserId);
} else {
- mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId);
+ mVm.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN, myUserId);
}
}
} catch (Exception e) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 8f093c7..065e2bb 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1113,7 +1113,7 @@
try {
final IBinder overlayWindowToken = new Binder();
mWindowManagerService.addWindowToken(overlayWindowToken, TYPE_ACCESSIBILITY_OVERLAY,
- displayId);
+ displayId, null /* options */);
synchronized (mLock) {
mOverlayWindowTokens.put(displayId, overlayWindowToken);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 2626654..a9e8ea0 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -372,12 +372,11 @@
@Override
public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) {
- final boolean isTouchableDisplay = mWindowManagerService.isTouchableDisplay(displayId);
synchronized (mLock) {
if (mSecurityPolicy.canPerformGestures(this)) {
MotionEventInjector motionEventInjector =
mSystemSupport.getMotionEventInjectorForDisplayLocked(displayId);
- if (motionEventInjector != null && isTouchableDisplay) {
+ if (mWindowManagerService.isTouchOrFaketouchDevice()) {
motionEventInjector.injectEvents(
gestureSteps.getList(), mServiceInterface, sequence, displayId);
} else {
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 25f4abe..903a071 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 @@
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 @@
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/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index f9f064c..eff410c 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -55,7 +55,6 @@
import android.content.pm.LauncherApps;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
@@ -66,9 +65,8 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
-import android.graphics.Bitmap;
import android.graphics.Point;
-import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -119,7 +117,6 @@
import com.android.internal.widget.IRemoteViewsFactory;
import com.android.server.LocalServices;
import com.android.server.WidgetBackupProvider;
-import com.android.server.policy.IconUtilities;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -253,8 +250,6 @@
private boolean mSafeMode;
private int mMaxWidgetBitmapMemory;
- private IconUtilities mIconUtilities;
-
AppWidgetServiceImpl(Context context) {
mContext = context;
}
@@ -271,7 +266,6 @@
mCallbackHandler = new CallbackHandler(mContext.getMainLooper());
mBackupRestoreController = new BackupRestoreController();
mSecurityPolicy = new SecurityPolicy();
- mIconUtilities = new IconUtilities(mContext);
computeMaximumWidgetBitmapMemory();
registerBroadcastReceiver();
@@ -578,44 +572,6 @@
}
}
- private Bitmap createMaskedWidgetBitmap(String providerPackage, int providerUserId) {
- final long identity = Binder.clearCallingIdentity();
- try {
- // Load the unbadged application icon and pass it to the widget to appear on
- // the masked view.
- Context userContext = mContext.createPackageContextAsUser(providerPackage, 0,
- UserHandle.of(providerUserId));
- PackageManager pm = userContext.getPackageManager();
- Drawable icon = pm.getApplicationInfo(providerPackage, 0).loadUnbadgedIcon(pm).mutate();
- // Create a bitmap of the icon which is what the widget's remoteview requires.
- icon.setColorFilter(mIconUtilities.getDisabledColorFilter());
- return mIconUtilities.createIconBitmap(icon);
- } catch (NameNotFoundException e) {
- Slog.e(TAG, "Fail to get application icon", e);
- // Provider package removed, no need to mask its views as its state will be
- // purged very soon.
- return null;
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- private RemoteViews createMaskedWidgetRemoteViews(Bitmap icon, boolean showBadge,
- PendingIntent onClickIntent) {
- RemoteViews views = new RemoteViews(mContext.getPackageName(),
- R.layout.work_widget_mask_view);
- if (icon != null) {
- views.setImageViewBitmap(R.id.work_widget_app_icon, icon);
- }
- if (!showBadge) {
- views.setViewVisibility(R.id.work_widget_badge_icon, View.INVISIBLE);
- }
- if (onClickIntent != null) {
- views.setOnClickPendingIntent(R.id.work_widget_mask_frame, onClickIntent);
- }
- return views;
- }
-
/**
* Mask the target widget belonging to the specified provider, or all active widgets
* of the provider if target widget == null.
@@ -625,59 +581,63 @@
if (widgetCount == 0) {
return;
}
- final String providerPackage = provider.id.componentName.getPackageName();
- final int providerUserId = provider.getUserId();
- Bitmap iconBitmap = createMaskedWidgetBitmap(providerPackage, providerUserId);
- if (iconBitmap == null) {
- return;
- }
- final boolean showBadge;
- final Intent onClickIntent;
+ RemoteViews views = new RemoteViews(mContext.getPackageName(),
+ R.layout.work_widget_mask_view);
+ ApplicationInfo appInfo = provider.info.providerInfo.applicationInfo;
+ final int appUserId = provider.getUserId();
+ boolean showBadge;
+
final long identity = Binder.clearCallingIdentity();
try {
+ final Intent onClickIntent;
+
if (provider.maskedBySuspendedPackage) {
- showBadge = mUserManager.hasBadge(providerUserId);
+ showBadge = mUserManager.hasBadge(appUserId);
final String suspendingPackage = mPackageManagerInternal.getSuspendingPackage(
- providerPackage, providerUserId);
+ appInfo.packageName, appUserId);
if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
onClickIntent = mDevicePolicyManagerInternal.createShowAdminSupportIntent(
- providerUserId, true);
+ appUserId, true);
} else {
final SuspendDialogInfo dialogInfo =
- mPackageManagerInternal.getSuspendedDialogInfo(providerPackage,
- suspendingPackage, providerUserId);
+ mPackageManagerInternal.getSuspendedDialogInfo(
+ appInfo.packageName, suspendingPackage, appUserId);
// onUnsuspend is null because we don't want to start any activity on
// unsuspending from a suspended widget.
onClickIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(
- providerPackage, suspendingPackage, dialogInfo, null, null,
- providerUserId);
+ appInfo.packageName, suspendingPackage, dialogInfo, null, null,
+ appUserId);
}
} else if (provider.maskedByQuietProfile) {
showBadge = true;
- onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(
- providerUserId);
+ onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(appUserId);
} else /* provider.maskedByLockedProfile */ {
showBadge = true;
- onClickIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null,
- providerUserId);
+ onClickIntent = mKeyguardManager
+ .createConfirmDeviceCredentialIntent(null, null, appUserId);
if (onClickIntent != null) {
- onClickIntent.setFlags(FLAG_ACTIVITY_NEW_TASK
- | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ onClickIntent.setFlags(
+ FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
}
}
+
+ if (onClickIntent != null) {
+ views.setOnClickPendingIntent(R.id.work_widget_mask_frame,
+ PendingIntent.getActivity(mContext, 0, onClickIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
+ }
+
+ Icon icon = appInfo.icon != 0
+ ? Icon.createWithResource(appInfo.packageName, appInfo.icon)
+ : Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+ views.setImageViewIcon(R.id.work_widget_app_icon, icon);
+ if (!showBadge) {
+ views.setViewVisibility(R.id.work_widget_badge_icon, View.INVISIBLE);
+ }
+
for (int j = 0; j < widgetCount; j++) {
Widget widget = provider.widgets.get(j);
if (targetWidget != null && targetWidget != widget) continue;
- PendingIntent intent = null;
- if (onClickIntent != null) {
- // Rare informational activity click is okay being
- // immutable; the tradeoff is more security in exchange for
- // losing bounds-based window animations
- intent = PendingIntent.getActivity(mContext, widget.appWidgetId,
- onClickIntent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- }
- RemoteViews views = createMaskedWidgetRemoteViews(iconBitmap, showBadge, intent);
if (widget.replaceWithMaskedViewsLocked(views)) {
scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
}
@@ -2687,6 +2647,8 @@
info.icon = activityInfo.getIconResource();
info.previewImage = sa.getResourceId(
com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0);
+ info.previewLayout = sa.getResourceId(
+ com.android.internal.R.styleable.AppWidgetProviderInfo_previewLayout, 0);
info.autoAdvanceViewId = sa.getResourceId(
com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1);
info.resizeMode = sa.getInt(
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 67f654e..6d72ca7 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 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 @@
}
/**
+ * 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 55e3ef2..10b00d3 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -17,9 +17,17 @@
package com.android.server.companion;
+import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
+import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED;
+import static android.content.Context.BIND_IMPORTANT;
+import static android.content.pm.PackageManager.MATCH_ALL;
+
+import static com.android.internal.util.CollectionUtils.any;
import static com.android.internal.util.CollectionUtils.emptyIfNull;
+import static com.android.internal.util.CollectionUtils.filter;
import static com.android.internal.util.CollectionUtils.find;
import static com.android.internal.util.CollectionUtils.forEach;
+import static com.android.internal.util.CollectionUtils.map;
import static com.android.internal.util.FunctionalUtils.uncheckExceptions;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
@@ -40,22 +48,32 @@
import android.app.role.RoleManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
import android.companion.Association;
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
+import android.companion.CompanionDeviceService;
import android.companion.DeviceNotAssociatedException;
import android.companion.ICompanionDeviceDiscoveryService;
import android.companion.ICompanionDeviceManager;
+import android.companion.ICompanionDeviceService;
import android.companion.IFindDeviceCallback;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.net.NetworkPolicyManager;
import android.os.Binder;
@@ -77,6 +95,7 @@
import android.provider.Settings;
import android.provider.SettingsStringUtil.ComponentNameSet;
import android.text.BidiFormatter;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.ExceptionUtils;
@@ -115,6 +134,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -135,9 +155,14 @@
CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME,
".DeviceDiscoveryService");
+ private static final long DEVICE_DISAPPEARED_TIMEOUT_MS = 10 * 1000;
+ private static final long DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS = 10 * 60 * 1000;
+
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";
@@ -146,18 +171,24 @@
private static final String XML_ATTR_PACKAGE = "package";
private static final String XML_ATTR_DEVICE = "device";
private static final String XML_ATTR_PROFILE = "profile";
- private static final String XML_ATTR_PERSISTENT_PROFILE_GRANTS = "persistent_profile_grants";
+ 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;
private final ConcurrentMap<Integer, AtomicFile> mUidToStorage = new ConcurrentHashMap<>();
private PowerWhitelistManager mPowerWhitelistManager;
private PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>> mServiceConnectors;
+ /** userId -> packageName -> serviceConnector */
+ private PerUser<ArrayMap<String, ServiceConnector<ICompanionDeviceService>>>
+ mDeviceListenerServiceConnectors;
private IAppOpsService mAppOpsManager;
private RoleManager mRoleManager;
private BluetoothAdapter mBluetoothAdapter;
+ private UserManager mUserManager;
private IFindDeviceCallback mFindDeviceCallback;
+ private ScanCallback mBleScanCallback = new BleScanCallback();
private AssociationRequest mRequest;
private String mCallingPackage;
private AndroidFuture<Association> mOngoingDeviceDiscovery;
@@ -165,9 +196,16 @@
private BluetoothDeviceConnectedListener mBluetoothDeviceConnectedListener =
new BluetoothDeviceConnectedListener();
+ private BleStateBroadcastReceiver mBleStateBroadcastReceiver = new BleStateBroadcastReceiver();
private List<String> mCurrentlyConnectedDevices = new ArrayList<>();
+ private ArrayMap<String, Date> mDevicesLastNearby = new ArrayMap<>();
+ private UnbindDeviceListenersRunnable
+ mUnbindDeviceListenersRunnable = new UnbindDeviceListenersRunnable();
+ private ArrayMap<String, TriggerDeviceDisappearedRunnable> mTriggerDeviceDisappearedRunnables =
+ new ArrayMap<>();
private final Object mLock = new Object();
+ private final Handler mMainHandler = Handler.getMain();
/** userId -> [association] */
@GuardedBy("mLock")
@@ -189,6 +227,7 @@
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mPermissionControllerManager = requireNonNull(
context.getSystemService(PermissionControllerManager.class));
+ mUserManager = context.getSystemService(UserManager.class);
Intent serviceIntent = new Intent().setComponent(SERVICE_TO_BIND_TO);
mServiceConnectors = new PerUser<ServiceConnector<ICompanionDeviceDiscoveryService>>() {
@@ -201,6 +240,16 @@
}
};
+ mDeviceListenerServiceConnectors = new PerUser<ArrayMap<String,
+ ServiceConnector<ICompanionDeviceService>>>() {
+ @NonNull
+ @Override
+ protected ArrayMap<String, ServiceConnector<ICompanionDeviceService>> create(
+ int userId) {
+ return new ArrayMap<>();
+ }
+ };
+
registerPackageMonitor();
}
@@ -208,10 +257,13 @@
new PackageMonitor() {
@Override
public void onPackageRemoved(String packageName, int uid) {
+ int userId = getChangingUserId();
updateAssociations(
as -> CollectionUtils.filter(as,
a -> !Objects.equals(a.getPackageName(), packageName)),
- getChangingUserId());
+ userId);
+
+ unbindDevicePresenceListener(packageName, userId);
}
@Override
@@ -225,6 +277,15 @@
}.register(getContext(), FgThread.get().getLooper(), UserHandle.ALL, true);
}
+ private void unbindDevicePresenceListener(String packageName, int userId) {
+ ServiceConnector<ICompanionDeviceService> deviceListener =
+ mDeviceListenerServiceConnectors.forUser(userId)
+ .remove(packageName);
+ if (deviceListener != null) {
+ deviceListener.unbind();
+ }
+ }
+
@Override
public void onStart() {
publishBinderService(Context.COMPANION_DEVICE_SERVICE, mImpl);
@@ -233,11 +294,17 @@
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ // Init Bluetooth
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter != null) {
mBluetoothAdapter.registerBluetoothConnectionCallback(
getContext().getMainExecutor(),
mBluetoothDeviceConnectedListener);
+ getContext().registerReceiver(
+ mBleStateBroadcastReceiver, mBleStateBroadcastReceiver.mIntentFilter);
+ initBleScanning();
+ } else {
+ Log.w(LOG_TAG, "No BluetoothAdapter available");
}
}
}
@@ -287,7 +354,7 @@
@Override
public void binderDied() {
- Handler.getMain().post(this::cleanup);
+ mMainHandler.post(this::cleanup);
}
private void cleanup() {
@@ -399,7 +466,7 @@
checkCallerIsSystemOr(callingPackage, userId);
checkUsesFeature(callingPackage, getCallingUserId());
}
- return new ArrayList<>(CollectionUtils.map(
+ return new ArrayList<>(map(
getAllAssociations(userId, callingPackage),
a -> a.getDeviceMacAddress()));
}
@@ -497,7 +564,7 @@
return true;
}
- return CollectionUtils.any(
+ return any(
getAllAssociations(userId, packageName),
a -> Objects.equals(a.getDeviceMacAddress(), macAddress));
}
@@ -506,22 +573,18 @@
public void registerDevicePresenceListenerService(
String packageName, String deviceAddress)
throws RemoteException {
- checkCanRegisterObserverService(packageName, deviceAddress);
-
- //TODO(eugenesusla) implement
+ registerDevicePresenceListenerActive(packageName, deviceAddress, true);
}
@Override
public void unregisterDevicePresenceListenerService(
String packageName, String deviceAddress)
throws RemoteException {
- checkCanRegisterObserverService(packageName, deviceAddress);
-
- //TODO(eugenesusla) implement
+ registerDevicePresenceListenerActive(packageName, deviceAddress, false);
}
- private void checkCanRegisterObserverService(String packageName, String deviceAddress)
- throws RemoteException {
+ private void registerDevicePresenceListenerActive(String packageName, String deviceAddress,
+ boolean active) throws RemoteException {
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE,
"[un]registerDevicePresenceListenerService");
@@ -537,6 +600,21 @@
+ " is not associated with device " + deviceAddress
+ " for user " + userId));
}
+
+ updateAssociations(associations -> map(associations, association -> {
+ if (Objects.equals(association.getPackageName(), packageName)
+ && Objects.equals(association.getDeviceMacAddress(), deviceAddress)) {
+ return new Association(
+ association.getUserId(),
+ association.getDeviceMacAddress(),
+ association.getPackageName(),
+ association.getDeviceProfile(),
+ active, /* notifyOnDeviceNearby */
+ association.getTimeApprovedMs());
+ } else {
+ return association;
+ }
+ }));
}
private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
@@ -565,6 +643,15 @@
}
@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 {
@@ -693,6 +780,10 @@
if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddress())) {
grantDeviceProfile(association);
}
+
+ if (association.isNotifyOnDeviceNearby()) {
+ restartBleScan();
+ }
}
private void exemptFromAutoRevoke(String packageName, int uid) {
@@ -795,10 +886,12 @@
association.getDeviceMacAddress());
if (association.getDeviceProfile() != null) {
tag.attribute(null, XML_ATTR_PROFILE, association.getDeviceProfile());
- tag.attribute(null, XML_ATTR_PERSISTENT_PROFILE_GRANTS,
+ tag.attribute(null, XML_ATTR_NOTIFY_DEVICE_NEARBY,
Boolean.toString(
- association.isKeepProfilePrivilegesWhenDeviceAway()));
+ association.isNotifyOnDeviceNearby()));
}
+ tag.attribute(null, XML_ATTR_TIME_APPROVED,
+ Long.toString(association.getTimeApprovedMs()));
tag.endTag(null, XML_TAG_ASSOCIATION);
});
@@ -835,16 +928,41 @@
}
private List<UserInfo> getAllUsers() {
- return getContext().getSystemService(UserManager.class).getUsers();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ return mUserManager.getUsers();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
- @Nullable
private Set<Association> getAllAssociations(int userId, @Nullable String packageFilter) {
return CollectionUtils.filter(
getAllAssociations(userId),
a -> Objects.equals(packageFilter, a.getPackageName()));
}
+ private Set<Association> getAllAssociations() {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ ArraySet<Association> result = new ArraySet<>();
+ for (UserInfo user : mUserManager.getAliveUsers()) {
+ result.addAll(getAllAssociations(user.id));
+ }
+ return result;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ 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);
@@ -865,13 +983,15 @@
final String profile = parser.getAttributeValue(null, XML_ATTR_PROFILE);
final boolean persistentGrants = Boolean.valueOf(
- parser.getAttributeValue(null, XML_ATTR_PERSISTENT_PROFILE_GRANTS));
+ 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) {
@@ -896,6 +1016,8 @@
}
}
}
+
+ onDeviceNearby(address);
}
private void grantDeviceProfile(Association association) {
@@ -919,6 +1041,267 @@
void onDeviceDisconnected(String address) {
mCurrentlyConnectedDevices.remove(address);
+
+ onDeviceDisappeared(address);
+ }
+
+ private ServiceConnector<ICompanionDeviceService> getDeviceListenerServiceConnector(
+ Association a) {
+ return mDeviceListenerServiceConnectors.forUser(a.getUserId()).computeIfAbsent(
+ a.getPackageName(),
+ pkg -> createDeviceListenerServiceConnector(a));
+ }
+
+ private ServiceConnector<ICompanionDeviceService> createDeviceListenerServiceConnector(
+ Association a) {
+ List<ResolveInfo> resolveInfos = getContext().getPackageManager().queryIntentServicesAsUser(
+ new Intent(CompanionDeviceService.SERVICE_INTERFACE), MATCH_ALL, a.getUserId());
+ List<ResolveInfo> packageResolveInfos = filter(resolveInfos,
+ info -> Objects.equals(info.serviceInfo.packageName, a.getPackageName()));
+ if (packageResolveInfos.size() != 1) {
+ Log.w(LOG_TAG, "Device presence listener package must have exactly one "
+ + "CompanionDeviceService, but " + a.getPackageName()
+ + " has " + packageResolveInfos.size());
+ return new ServiceConnector.NoOp<>();
+ }
+ ComponentName componentName = packageResolveInfos.get(0).serviceInfo.getComponentName();
+ Log.i(LOG_TAG, "Initializing CompanionDeviceService binding for " + componentName);
+ return new ServiceConnector.Impl<>(getContext(),
+ new Intent(CompanionDeviceService.SERVICE_INTERFACE).setComponent(componentName),
+ BIND_IMPORTANT,
+ a.getUserId(),
+ ICompanionDeviceService.Stub::asInterface);
+ }
+
+ private class BleScanCallback extends ScanCallback {
+ @Override
+ public void onScanResult(int callbackType, ScanResult result) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "onScanResult(callbackType = "
+ + callbackType + ", result = " + result + ")");
+ }
+
+ onDeviceNearby(result.getDevice().getAddress());
+ }
+
+ @Override
+ public void onBatchScanResults(List<ScanResult> results) {
+ for (int i = 0, size = results.size(); i < size; i++) {
+ onScanResult(CALLBACK_TYPE_ALL_MATCHES, results.get(i));
+ }
+ }
+
+ @Override
+ public void onScanFailed(int errorCode) {
+ if (errorCode == SCAN_FAILED_ALREADY_STARTED) {
+ // ignore - this might happen if BT tries to auto-restore scans for us in the
+ // future
+ Log.i(LOG_TAG, "Ignoring BLE scan error: SCAN_FAILED_ALREADY_STARTED");
+ } else {
+ Log.w(LOG_TAG, "Failed to start BLE scan: error " + errorCode);
+ }
+ }
+ }
+
+ private class BleStateBroadcastReceiver extends BroadcastReceiver {
+
+ final IntentFilter mIntentFilter =
+ new IntentFilter(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int previousState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, -1);
+ int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+ Log.i(LOG_TAG, "Received BT state transition broadcast: "
+ + BluetoothAdapter.nameForState(previousState)
+ + " -> " + BluetoothAdapter.nameForState(newState));
+
+ boolean bleOn = newState == BluetoothAdapter.STATE_ON
+ || newState == BluetoothAdapter.STATE_BLE_ON;
+ if (bleOn) {
+ if (mBluetoothAdapter.getBluetoothLeScanner() != null) {
+ startBleScan();
+ } else {
+ Log.wtf(LOG_TAG, "BLE on, but BluetoothLeScanner == null");
+ }
+ }
+ }
+ }
+
+ private class UnbindDeviceListenersRunnable implements Runnable {
+
+ public String getJobId(String address) {
+ return "CDM_deviceGone_unbind_" + address;
+ }
+
+ @Override
+ public void run() {
+ int size = mDevicesLastNearby.size();
+ for (int i = 0; i < size; i++) {
+ String address = mDevicesLastNearby.keyAt(i);
+ Date lastNearby = mDevicesLastNearby.valueAt(i);
+
+ if (System.currentTimeMillis() - lastNearby.getTime()
+ >= DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS) {
+ for (Association association : getAllAssociations(address)) {
+ if (association.isNotifyOnDeviceNearby()) {
+ getDeviceListenerServiceConnector(association).unbind();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private class TriggerDeviceDisappearedRunnable implements Runnable {
+
+ private final String mAddress;
+
+ TriggerDeviceDisappearedRunnable(String address) {
+ mAddress = address;
+ }
+
+ public void schedule() {
+ mMainHandler.removeCallbacks(this);
+ mMainHandler.postDelayed(this, this, DEVICE_DISAPPEARED_TIMEOUT_MS);
+ }
+
+ @Override
+ public void run() {
+ onDeviceDisappeared(mAddress);
+ }
+ }
+
+ private Set<Association> getAllAssociations(String deviceAddress) {
+ List<UserInfo> aliveUsers = mUserManager.getAliveUsers();
+ Set<Association> result = new ArraySet<>();
+ for (int i = 0, size = aliveUsers.size(); i < size; i++) {
+ UserInfo user = aliveUsers.get(i);
+ for (Association association : getAllAssociations(user.id)) {
+ if (Objects.equals(association.getDeviceMacAddress(), deviceAddress)) {
+ result.add(association);
+ }
+ }
+ }
+ return result;
+ }
+
+ private void onDeviceNearby(String address) {
+ Date timestamp = new Date();
+ Date oldTimestamp = mDevicesLastNearby.put(address, timestamp);
+
+ cancelUnbindDeviceListener(address);
+
+ mTriggerDeviceDisappearedRunnables
+ .computeIfAbsent(address, addr -> new TriggerDeviceDisappearedRunnable(address))
+ .schedule();
+
+ // Avoid spamming the app if device is already known to be nearby
+ boolean justAppeared = oldTimestamp == null
+ || timestamp.getTime() - oldTimestamp.getTime() >= DEVICE_DISAPPEARED_TIMEOUT_MS;
+ if (justAppeared) {
+ for (Association association : getAllAssociations(address)) {
+ if (association.isNotifyOnDeviceNearby()) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Device " + address
+ + " managed by " + association.getPackageName()
+ + " is nearby on " + timestamp);
+ }
+ getDeviceListenerServiceConnector(association).run(
+ service -> service.onDeviceAppeared(association.getDeviceMacAddress()));
+ }
+ }
+ }
+ }
+
+ private void onDeviceDisappeared(String address) {
+ boolean hasDeviceListeners = false;
+ for (Association association : getAllAssociations(address)) {
+ if (association.isNotifyOnDeviceNearby()) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Device " + address
+ + " managed by " + association.getPackageName()
+ + " disappeared; last seen on " + mDevicesLastNearby.get(address));
+ }
+
+ getDeviceListenerServiceConnector(association).run(
+ service -> service.onDeviceDisappeared(address));
+ hasDeviceListeners = true;
+ }
+ }
+
+ cancelUnbindDeviceListener(address);
+ if (hasDeviceListeners) {
+ mMainHandler.postDelayed(
+ mUnbindDeviceListenersRunnable,
+ mUnbindDeviceListenersRunnable.getJobId(address),
+ DEVICE_DISAPPEARED_UNBIND_TIMEOUT_MS);
+ }
+ }
+
+ private void cancelUnbindDeviceListener(String address) {
+ mMainHandler.removeCallbacks(
+ mUnbindDeviceListenersRunnable, mUnbindDeviceListenersRunnable.getJobId(address));
+ }
+
+ private void initBleScanning() {
+ Log.i(LOG_TAG, "initBleScanning()");
+
+ boolean bluetoothReady = mBluetoothAdapter.registerServiceLifecycleCallback(
+ new BluetoothAdapter.ServiceLifecycleCallback() {
+ @Override
+ public void onBluetoothServiceUp() {
+ Log.i(LOG_TAG, "Bluetooth stack is up");
+ startBleScan();
+ }
+
+ @Override
+ public void onBluetoothServiceDown() {
+ Log.w(LOG_TAG, "Bluetooth stack is down");
+ }
+ });
+ if (bluetoothReady) {
+ startBleScan();
+ }
+ }
+
+ void startBleScan() {
+ Log.i(LOG_TAG, "startBleScan()");
+
+ List<ScanFilter> filters = getBleScanFilters();
+ if (filters.isEmpty()) {
+ return;
+ }
+ BluetoothLeScanner scanner = mBluetoothAdapter.getBluetoothLeScanner();
+ if (scanner == null) {
+ Log.w(LOG_TAG, "scanner == null (likely BLE isn't ON yet)");
+ } else {
+ scanner.startScan(
+ filters,
+ new ScanSettings.Builder().setScanMode(SCAN_MODE_BALANCED).build(),
+ mBleScanCallback);
+ }
+ }
+
+ void restartBleScan() {
+ mBluetoothAdapter.getBluetoothLeScanner().stopScan(mBleScanCallback);
+ startBleScan();
+ }
+
+ private List<ScanFilter> getBleScanFilters() {
+ ArrayList<ScanFilter> result = new ArrayList<>();
+ ArraySet<String> addressesSeen = new ArraySet<>();
+ for (Association association : getAllAssociations()) {
+ String address = association.getDeviceMacAddress();
+ if (addressesSeen.contains(address)) {
+ continue;
+ }
+ if (association.isNotifyOnDeviceNearby()) {
+ result.add(new ScanFilter.Builder().setDeviceAddress(address).build());
+ addressesSeen.add(address);
+ }
+ }
+ return result;
}
private AndroidFuture<String> getDeviceProfilePermissionDescription(String deviceProfile) {
@@ -934,6 +1317,15 @@
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"
@@ -962,7 +1354,8 @@
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 e01c4df..83a5036 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -100,11 +100,10 @@
libs: [
"services.net",
"android.hardware.light-V2.0-java",
- "android.hardware.gnss-java",
- "android.hardware.power-java",
+ "android.hardware.gnss-V1-java",
+ "android.hardware.power-V1-java",
"android.hardware.power-V1.0-java",
- "android.hardware.vibrator-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",
@@ -128,22 +127,22 @@
"android.hardware.health-V1.0-java",
"android.hardware.health-V2.0-java",
"android.hardware.health-V2.1-java",
- "android.hardware.light-java",
+ "android.hardware.light-V1-java",
"android.hardware.tv.cec-V1.0-java",
"android.hardware.weaver-V1.0-java",
"android.hardware.biometrics.face-V1.1-java",
- "android.hardware.biometrics.face-java",
+ "android.hardware.biometrics.face-V1-java",
"android.hardware.biometrics.fingerprint-V2.3-java",
- "android.hardware.biometrics.fingerprint-java",
+ "android.hardware.biometrics.fingerprint-V1-java",
"android.hardware.oemlock-V1.0-java",
"android.hardware.configstore-V1.0-java",
"android.hardware.contexthub-V1.0-java",
- "android.hardware.rebootescrow-java",
+ "android.hardware.rebootescrow-V1-java",
"android.hardware.soundtrigger-V2.3-java",
- "android.hardware.power.stats-java",
+ "android.hardware.power.stats-V1-java",
"android.hidl.manager-V1.2-java",
"capture_state_listener-aidl-java",
- "dnsresolver_aidl_interface-java",
+ "dnsresolver_aidl_interface-V7-java",
"icu4j_calendar_astronomer",
"netd-client",
"overlayable_policy_aidl-java",
diff --git a/services/core/java/android/content/pm/OWNERS b/services/core/java/android/content/pm/OWNERS
new file mode 100644
index 0000000..5eed0b5
--- /dev/null
+++ b/services/core/java/android/content/pm/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/content/pm/OWNERS
\ No newline at end of file
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index dad8bd8..737a9e4 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.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 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 @@
* 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.
@@ -997,28 +998,6 @@
public abstract boolean isSuspendingAnyPackages(String suspendingPackage, int userId);
/**
- * Register to listen for loading progress of an installed package.
- * @param packageName The name of the installed package
- * @param callback To loading reporting progress
- * @param userId The user under which to check.
- * @return Whether the registration was successful. It can fail if the package has not been
- * installed yet.
- */
- public abstract boolean registerInstalledLoadingProgressCallback(@NonNull String packageName,
- @NonNull InstalledLoadingProgressCallback callback, int userId);
-
- /**
- * Unregister to stop listening to loading progress of an installed package
- * @param packageName The name of the installed package
- * @param callback To unregister
- * @return True if the callback is removed from registered callback list. False is the callback
- * does not exist on the registered callback list, which can happen if the callback has
- * already been unregistered.
- */
- public abstract boolean unregisterInstalledLoadingProgressCallback(@NonNull String packageName,
- @NonNull InstalledLoadingProgressCallback callback);
-
- /**
* Returns the string representation of a known package. For example,
* {@link #PACKAGE_SETUP_WIZARD} is represented by the string Setup Wizard.
*
@@ -1137,7 +1116,8 @@
*/
public abstract void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int optional, @Checksum.Type int required,
- @Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId,
+ @Nullable List trustedInstallers,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
@NonNull Executor executor, @NonNull Handler handler);
/**
diff --git a/services/core/java/android/os/OWNERS b/services/core/java/android/os/OWNERS
new file mode 100644
index 0000000..d0a2daf
--- /dev/null
+++ b/services/core/java/android/os/OWNERS
@@ -0,0 +1 @@
+per-file BatteryStats* = file:/BATTERY_STATS_OWNERS
diff --git a/services/core/java/com/android/server/BundleUtils.java b/services/core/java/com/android/server/BundleUtils.java
new file mode 100644
index 0000000..20ebe29
--- /dev/null
+++ b/services/core/java/com/android/server/BundleUtils.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+
+/**
+ * Utility methods for handling {@link Bundle}.
+ *
+ */
+public final class BundleUtils {
+ private BundleUtils() {
+ }
+
+ /**
+ * Returns true if {@code in} is null or empty.
+ */
+ public static boolean isEmpty(@Nullable Bundle in) {
+ return (in == null) || (in.size() == 0);
+ }
+
+ /**
+ * Creates a copy of the {@code in} Bundle. If {@code in} is null, it'll return an empty
+ * bundle.
+ *
+ * <p>The resulting {@link Bundle} is always writable. (i.e. it won't return
+ * {@link Bundle#EMPTY})
+ */
+ public static @NonNull Bundle clone(@Nullable Bundle in) {
+ return (in != null) ? new Bundle(in) : new Bundle();
+ }
+
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index bf9d564..42b6a7f 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -45,6 +45,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
@@ -120,6 +121,7 @@
import android.net.NetworkTestResultParcelable;
import android.net.NetworkUtils;
import android.net.NetworkWatchlistManager;
+import android.net.OemNetworkPreferences;
import android.net.PrivateDnsConfigParcel;
import android.net.ProxyInfo;
import android.net.QosCallbackException;
@@ -130,12 +132,14 @@
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;
@@ -281,15 +285,18 @@
// connect anyway?" dialog after the user selects a network that doesn't validate.
private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
- // Default to 30s linger time-out. Modifiable only for testing.
+ // Default to 30s linger time-out, and 5s for nascent network. Modifiable only for testing.
private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
private static final int DEFAULT_LINGER_DELAY_MS = 30_000;
+ private static final int DEFAULT_NASCENT_DELAY_MS = 5_000;
// The maximum number of network request allowed per uid before an exception is thrown.
private static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
@VisibleForTesting
protected int mLingerDelayMs; // Can't be final, or test subclass constructors can't change it.
+ @VisibleForTesting
+ protected int mNascentDelayMs;
// How long to delay to removal of a pending intent based request.
// See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
@@ -1034,7 +1041,8 @@
mNetworkRanker = new NetworkRanker();
final NetworkRequest defaultInternetRequest = createDefaultInternetRequestForTransport(
-1, NetworkRequest.Type.REQUEST);
- mDefaultRequest = new NetworkRequestInfo(null, defaultInternetRequest, new Binder());
+ mDefaultRequest = new NetworkRequestInfo(null, defaultInternetRequest, new Binder(),
+ null /* attributionTag */);
mNetworkRequests.put(defaultInternetRequest, mDefaultRequest);
mDefaultNetworkRequests.add(mDefaultRequest);
mNetworkRequestInfoLogs.log("REGISTER " + mDefaultRequest);
@@ -1063,6 +1071,8 @@
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
+ // TODO: Consider making the timer customizable.
+ mNascentDelayMs = DEFAULT_NASCENT_DELAY_MS;
mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
mStatsService = Objects.requireNonNull(statsService, "missing INetworkStatsService");
@@ -1240,6 +1250,7 @@
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
+ netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
netCap.setSingleUid(uid);
return netCap;
@@ -1249,6 +1260,7 @@
int transportType, NetworkRequest.Type type) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
+ netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
if (transportType > -1) {
netCap.addTransportType(transportType);
@@ -1302,7 +1314,7 @@
if (enable) {
handleRegisterNetworkRequest(new NetworkRequestInfo(
- null, networkRequest, new Binder()));
+ null, networkRequest, new Binder(), null /* attributionTag */));
} else {
handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID,
/* callOnUnavailable */ false);
@@ -1637,7 +1649,7 @@
@Override
public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
- int userId, String callingPackageName) {
+ int userId, String callingPackageName, @Nullable String callingAttributionTag) {
// The basic principle is: if an app's traffic could possibly go over a
// network, without the app doing anything multinetwork-specific,
// (hence, by "default"), then include that network's capabilities in
@@ -1668,7 +1680,8 @@
result.put(
nai.network,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- nc, mDeps.getCallingUid(), callingPackageName));
+ nc, mDeps.getCallingUid(), callingPackageName,
+ callingAttributionTag));
}
}
@@ -1681,7 +1694,8 @@
result.put(
network,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- nc, mDeps.getCallingUid(), callingPackageName));
+ nc, mDeps.getCallingUid(), callingPackageName,
+ callingAttributionTag));
}
}
}
@@ -1756,12 +1770,13 @@
}
@Override
- public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) {
+ public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName,
+ @Nullable String callingAttributionTag) {
mAppOpsManager.checkPackage(mDeps.getCallingUid(), callingPackageName);
enforceAccessPermission();
return createWithLocationInfoSanitizedIfNecessaryWhenParceled(
getNetworkCapabilitiesInternal(network),
- mDeps.getCallingUid(), callingPackageName);
+ mDeps.getCallingUid(), callingPackageName, callingAttributionTag);
}
@VisibleForTesting
@@ -1780,11 +1795,12 @@
return newNc;
}
- private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName) {
+ private boolean hasLocationPermission(int callerUid, @NonNull String callerPkgName,
+ @Nullable String callingAttributionTag) {
final long token = Binder.clearCallingIdentity();
try {
return mLocationPermissionChecker.checkLocationPermission(
- callerPkgName, null /* featureId */, callerUid, null /* message */);
+ callerPkgName, callingAttributionTag, callerUid, null /* message */);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1793,7 +1809,8 @@
@VisibleForTesting
@Nullable
NetworkCapabilities createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName) {
+ @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName,
+ @Nullable String callingAttributionTag) {
if (nc == null) {
return null;
}
@@ -1802,7 +1819,8 @@
// Avoid doing location permission check if the transport info has no location sensitive
// data.
if (nc.getTransportInfo() != null && nc.getTransportInfo().hasLocationSensitiveFields()) {
- hasLocationPermission = hasLocationPermission(callerUid, callerPkgName);
+ hasLocationPermission =
+ hasLocationPermission(callerUid, callerPkgName, callingAttributionTag);
newNc = new NetworkCapabilities(nc, hasLocationPermission);
} else {
newNc = new NetworkCapabilities(nc, false /* parcelLocationSensitiveFields */);
@@ -1819,7 +1837,8 @@
}
if (hasLocationPermission == null) {
// Location permission not checked yet, check now for masking owner UID.
- hasLocationPermission = hasLocationPermission(callerUid, callerPkgName);
+ hasLocationPermission =
+ hasLocationPermission(callerUid, callerPkgName, callingAttributionTag);
}
// Reset owner uid if the app has no location permission.
if (!hasLocationPermission) {
@@ -1877,7 +1896,8 @@
final ArrayList<NetworkState> result = new ArrayList<>();
for (Network network : getAllNetworks()) {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
- if (nai != null) {
+ // TODO: Consider include SUSPENDED networks.
+ if (nai != null && nai.networkInfo.isConnected()) {
// TODO (b/73321673) : NetworkState contains a copy of the
// NetworkCapabilities, which may contain UIDs of apps to which the
// network applies. Should the UIDs be cleared so as not to leak or
@@ -2030,7 +2050,7 @@
mHandler.sendMessage(mHandler.obtainMessage(
EVENT_PRIVATE_DNS_VALIDATION_UPDATE,
new PrivateDnsValidationUpdate(netId,
- InetAddress.parseNumericAddress(ipAddress),
+ InetAddresses.parseNumericAddress(ipAddress),
hostname, validated)));
} catch (IllegalArgumentException e) {
loge("Error parsing ip address in validation event");
@@ -3334,7 +3354,7 @@
// 3. If this network is unneeded (which implies it is not lingering), and there is at least
// one lingered request, set inactive.
nai.updateInactivityTimer();
- if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
+ if (nai.isInactive() && nai.numForegroundNetworkRequests() > 0) {
if (DBG) log("Unsetting inactive " + nai.toShortString());
nai.unsetInactive();
logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
@@ -3588,10 +3608,7 @@
// As this request was not satisfied on rematch and thus never had any scores sent to the
// factories, send null now for each request of type REQUEST.
for (final NetworkRequest req : nri.mRequests) {
- if (!req.isRequest()) {
- continue;
- }
- sendUpdatedScoreToFactories(req, null);
+ if (req.isRequest()) sendUpdatedScoreToFactories(req, null);
}
}
@@ -3631,7 +3648,7 @@
return true;
}
- if (!nai.everConnected || nai.isVPN() || nai.isLingering() || numRequests > 0) {
+ if (!nai.everConnected || nai.isVPN() || nai.isInactive() || numRequests > 0) {
return false;
}
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
@@ -3768,7 +3785,7 @@
mNetworkRequestInfoLogs.log("RELEASE " + nri);
if (null != nri.getActiveRequest()) {
- if (nri.getActiveRequest().isRequest()) {
+ if (!nri.getActiveRequest().isListen()) {
removeSatisfiedNetworkRequestFromNetwork(nri);
} else {
nri.setSatisfier(null, null);
@@ -5516,7 +5533,7 @@
}
// The network currently satisfying this NRI. Only one request in an NRI can have a
- // satisfier. For non-multilayer requests, only REQUEST-type requests can have a satisfier.
+ // satisfier. For non-multilayer requests, only non-listen requests can have a satisfier.
@Nullable
private NetworkAgentInfo mSatisfier;
NetworkAgentInfo getSatisfier() {
@@ -5538,6 +5555,8 @@
final int mPid;
final int mUid;
final Messenger messenger;
+ @Nullable
+ final String mCallingAttributionTag;
/**
* Get the list of UIDs this nri applies to.
@@ -5551,7 +5570,8 @@
return uids;
}
- NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
+ NetworkRequestInfo(NetworkRequest r, PendingIntent pi,
+ @Nullable String callingAttributionTag) {
mRequests = initializeRequests(r);
ensureAllNetworkRequestsHaveType(mRequests);
mPendingIntent = pi;
@@ -5560,9 +5580,11 @@
mPid = getCallingPid();
mUid = mDeps.getCallingUid();
mNetworkRequestCounter.incrementCountOrThrow(mUid);
+ mCallingAttributionTag = callingAttributionTag;
}
- NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder) {
+ NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder,
+ @Nullable String callingAttributionTag) {
super();
messenger = m;
mRequests = initializeRequests(r);
@@ -5572,6 +5594,7 @@
mUid = mDeps.getCallingUid();
mPendingIntent = null;
mNetworkRequestCounter.incrementCountOrThrow(mUid);
+ mCallingAttributionTag = callingAttributionTag;
try {
mBinder.linkToDeath(this, 0);
@@ -5581,7 +5604,7 @@
}
NetworkRequestInfo(NetworkRequest r) {
- this(r, null);
+ this(r, null /* pi */, null /* callingAttributionTag */);
}
// True if this NRI is being satisfied. It also accounts for if the nri has its satisifer
@@ -5726,6 +5749,7 @@
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 {
@@ -5736,11 +5760,15 @@
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.
@@ -5759,6 +5787,7 @@
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
@@ -5774,9 +5803,20 @@
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
nextNetworkRequestId(), reqType);
- NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder);
+ NetworkRequestInfo nri =
+ 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,
@@ -5863,7 +5903,8 @@
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
nextNetworkRequestId(), NetworkRequest.Type.REQUEST);
- NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation);
+ NetworkRequestInfo nri =
+ new NetworkRequestInfo(networkRequest, operation, callingAttributionTag);
if (DBG) log("pendingRequest for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT,
nri));
@@ -5907,7 +5948,8 @@
@Override
public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
- Messenger messenger, IBinder binder, @NonNull String callingPackageName) {
+ Messenger messenger, IBinder binder, @NonNull String callingPackageName,
+ @Nullable String callingAttributionTag) {
final int callingUid = mDeps.getCallingUid();
if (!hasWifiNetworkListenPermission(networkCapabilities)) {
enforceAccessPermission();
@@ -5927,7 +5969,8 @@
NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
NetworkRequest.Type.LISTEN);
- NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder);
+ NetworkRequestInfo nri =
+ new NetworkRequestInfo(messenger, networkRequest, binder, callingAttributionTag);
if (VDBG) log("listenForNetwork for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -5936,7 +5979,8 @@
@Override
public void pendingListenForNetwork(NetworkCapabilities networkCapabilities,
- PendingIntent operation, @NonNull String callingPackageName) {
+ PendingIntent operation, @NonNull String callingPackageName,
+ @Nullable String callingAttributionTag) {
Objects.requireNonNull(operation, "PendingIntent cannot be null.");
final int callingUid = mDeps.getCallingUid();
if (!hasWifiNetworkListenPermission(networkCapabilities)) {
@@ -5950,7 +5994,8 @@
NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
NetworkRequest.Type.LISTEN);
- NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation);
+ NetworkRequestInfo nri =
+ new NetworkRequestInfo(networkRequest, operation, callingAttributionTag);
if (VDBG) log("pendingListenForNetwork for " + nri);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -7004,8 +7049,8 @@
private void sendUpdatedScoreToFactories(NetworkAgentInfo nai) {
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest nr = nai.requestAt(i);
- // Don't send listening requests to factories. b/17393458
- if (nr.isListen()) continue;
+ // Don't send listening or track default request to factories. b/17393458
+ if (!nr.isRequest()) continue;
sendUpdatedScoreToFactories(nr, nai);
}
}
@@ -7067,10 +7112,10 @@
ensureRunningOnConnectivityServiceThread();
for (final NetworkRequestInfo nri : getNrisFromGlobalRequests()) {
for (final NetworkRequest req : nri.mRequests) {
- if (req.isListen() && nri.getActiveRequest() == req) {
+ if (!req.isRequest() && nri.getActiveRequest() == req) {
break;
}
- if (req.isListen()) {
+ if (!req.isRequest()) {
continue;
}
// Only set the nai for the request it is satisfying.
@@ -7165,7 +7210,8 @@
putParcelable(
bundle,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- nc, nri.mUid, nrForCallback.getRequestorPackageName()));
+ nc, nri.mUid, nrForCallback.getRequestorPackageName(),
+ nri.mCallingAttributionTag));
putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
networkAgent.linkProperties, nri.mPid, nri.mUid));
// For this notification, arg1 contains the blocked status.
@@ -7184,7 +7230,8 @@
putParcelable(
bundle,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- netCap, nri.mUid, nrForCallback.getRequestorPackageName()));
+ netCap, nri.mUid, nrForCallback.getRequestorPackageName(),
+ nri.mCallingAttributionTag));
break;
}
case ConnectivityManager.CALLBACK_IP_CHANGED: {
@@ -7220,8 +7267,8 @@
if (nai.numRequestNetworkRequests() != 0) {
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest nr = nai.requestAt(i);
- // Ignore listening requests.
- if (nr.isListen()) continue;
+ // Ignore listening and track default requests.
+ if (!nr.isRequest()) continue;
loge("Dead network still had at least " + nr);
break;
}
@@ -7244,7 +7291,7 @@
// Tear the network down.
teardownUnneededNetwork(oldNetwork);
} else {
- // Put the network in the background.
+ // Put the network in the background if it doesn't satisfy any foreground request.
updateCapabilitiesForNetwork(oldNetwork);
}
}
@@ -7498,6 +7545,14 @@
} else {
if (VDBG || DDBG) log(" accepting network in place of null");
}
+
+ // To prevent constantly CPU wake up for nascent timer, if a network comes up
+ // and immediately satisfies a request then remove the timer. This will happen for
+ // all networks except in the case of an underlying network for a VCN.
+ if (newSatisfier.isNascent()) {
+ newSatisfier.unlingerRequest(NetworkRequest.REQUEST_ID_NONE);
+ }
+
newSatisfier.unlingerRequest(newRequest.requestId);
if (!newSatisfier.addRequest(newRequest)) {
Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
@@ -7640,19 +7695,19 @@
}
}
- // Update the linger state before processing listen callbacks, because the background
- // computation depends on whether the network is lingering. Don't send the LOSING callbacks
+ // Update the inactivity state before processing listen callbacks, because the background
+ // computation depends on whether the network is inactive. Don't send the LOSING callbacks
// just yet though, because they have to be sent after the listens are processed to keep
// backward compatibility.
- final ArrayList<NetworkAgentInfo> lingeredNetworks = new ArrayList<>();
+ final ArrayList<NetworkAgentInfo> inactiveNetworks = new ArrayList<>();
for (final NetworkAgentInfo nai : nais) {
- // Rematching may have altered the linger state of some networks, so update all linger
- // timers. updateLingerState reads the state from the network agent and does nothing
- // if the state has not changed : the source of truth is controlled with
- // NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which have been
- // called while rematching the individual networks above.
+ // Rematching may have altered the inactivity state of some networks, so update all
+ // inactivity timers. updateInactivityState reads the state from the network agent
+ // and does nothing if the state has not changed : the source of truth is controlled
+ // with NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which
+ // have been called while rematching the individual networks above.
if (updateInactivityState(nai, now)) {
- lingeredNetworks.add(nai);
+ inactiveNetworks.add(nai);
}
}
@@ -7669,7 +7724,11 @@
processNewlySatisfiedListenRequests(nai);
}
- for (final NetworkAgentInfo nai : lingeredNetworks) {
+ for (final NetworkAgentInfo nai : inactiveNetworks) {
+ // For nascent networks, if connecting with no foreground request, skip broadcasting
+ // LOSING for backward compatibility. This is typical when mobile data connected while
+ // wifi connected with mobile data always-on enabled.
+ if (nai.isNascent()) continue;
notifyNetworkLosing(nai, now);
}
@@ -7910,6 +7969,15 @@
// doing.
updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
+ // Before first rematching networks, put an inactivity timer without any request, this
+ // allows {@code updateInactivityState} to update the state accordingly and prevent
+ // tearing down for any {@code unneeded} evaluation in this period.
+ // Note that the timer will not be rescheduled since the expiry time is
+ // fixed after connection regardless of the network satisfying other requests or not.
+ // But it will be removed as soon as the network satisfies a request for the first time.
+ networkAgent.lingerRequest(NetworkRequest.REQUEST_ID_NONE,
+ SystemClock.elapsedRealtime(), mNascentDelayMs);
+
// Consider network even though it is not yet validated.
rematchAllNetworksAndRequests();
@@ -8412,22 +8480,11 @@
}
}
- /**
- * 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;
}
/**
@@ -8437,14 +8494,6 @@
* 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);
}
@@ -8452,8 +8501,15 @@
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;
}
@@ -9100,6 +9156,7 @@
}
}
}
+
/**
* Registers {@link QosSocketFilter} with {@link IQosCallback}.
*
@@ -9149,4 +9206,10 @@
public void unregisterQosCallback(@NonNull final IQosCallback callback) {
mQosCallbackTracker.unregisterCallback(callback);
}
+
+ @Override
+ public void setOemNetworkPreference(@NonNull final OemNetworkPreferences preference) {
+ // TODO http://b/176495594 track multiple default networks with networkPreferences
+ if (DBG) log("setOemNetworkPreference() called with: " + preference.toString());
+ }
}
diff --git a/services/core/java/com/android/server/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java
index c56cef2..a83c981 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.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 @@
* 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 @@
private static final long START_NANOTIME = System.nanoTime();
private final String randomDevice;
- private final String hwRandomDevice;
private final String entropyFile;
/**
@@ -80,7 +71,6 @@
Slog.e(TAG, "Will not process invalid message");
return;
}
- addHwRandomEntropy();
writeEntropy();
scheduleEntropyWriter();
}
@@ -94,25 +84,21 @@
};
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 @@
}
}
- /**
- * 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 7d65156..1ad0176 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -890,7 +890,7 @@
ZramWriteback.scheduleZramWriteback(mContext);
}
- updateTranscodeEnabled();
+ configureTranscoding();
}
/**
@@ -922,7 +922,7 @@
}
}
- private void updateTranscodeEnabled() {
+ private void configureTranscoding() {
// See MediaProvider TranscodeHelper#getBooleanProperty for more information
boolean transcodeEnabled = false;
boolean defaultValue = true;
@@ -935,6 +935,15 @@
"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/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index e96fd39..96f832d 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -50,6 +50,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.NetdUtils;
+import com.android.net.module.util.NetworkStackConstants;
import java.io.UncheckedIOException;
import java.net.Inet4Address;
@@ -280,10 +281,12 @@
// Add global routes (but as non-default, non-internet providing network)
if (allowIPv4) {
- lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null, iface));
+ lp.addRoute(new RouteInfo(new IpPrefix(
+ NetworkStackConstants.IPV4_ADDR_ANY, 0), null, iface));
}
if (allowIPv6) {
- lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null, iface));
+ lp.addRoute(new RouteInfo(new IpPrefix(
+ NetworkStackConstants.IPV6_ADDR_ANY, 0), null, iface));
}
final TestNetworkAgent agent = new TestNetworkAgent(context, looper, nc, lp,
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 916bec2..27210da 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -66,6 +66,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
@@ -291,8 +292,9 @@
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
- @NonNull TelephonySubscriptionSnapshot snapshot) {
- return new Vcn(vcnContext, subscriptionGroup, config, snapshot);
+ @NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull VcnSafemodeCallback safemodeCallback) {
+ return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safemodeCallback);
}
/** Gets the subId indicated by the given {@link WifiInfo}. */
@@ -438,7 +440,12 @@
// TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active
// VCN.
- final Vcn newInstance = mDeps.newVcn(mVcnContext, subscriptionGroup, config, mLastSnapshot);
+ final VcnSafemodeCallbackImpl safemodeCallback =
+ new VcnSafemodeCallbackImpl(subscriptionGroup);
+
+ final Vcn newInstance =
+ mDeps.newVcn(
+ mVcnContext, subscriptionGroup, config, mLastSnapshot, safemodeCallback);
mVcns.put(subscriptionGroup, newInstance);
// Now that a new VCN has started, notify all registered listeners to refresh their
@@ -536,7 +543,7 @@
}
}
- /** Get current configuration list for testing purposes */
+ /** Get current VCNs for testing purposes */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public Map<ParcelUuid, Vcn> getAllVcns() {
synchronized (mLock) {
@@ -634,21 +641,61 @@
}
boolean isVcnManagedNetwork = false;
+ boolean isRestrictedCarrierWifi = false;
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
synchronized (mLock) {
ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId);
- // TODO(b/178140910): only mark the Network as VCN-managed if not in safe mode
- if (mVcns.containsKey(subGroup)) {
- isVcnManagedNetwork = true;
+ Vcn vcn = mVcns.get(subGroup);
+ 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);
}
+
+ /** Callback for signalling when a Vcn has entered Safemode. */
+ public interface VcnSafemodeCallback {
+ /** Called by a Vcn to signal that it has entered Safemode. */
+ void onEnteredSafemode();
+ }
+
+ /** VcnSafemodeCallback is used by Vcns to notify VcnManagementService on entering Safemode. */
+ private class VcnSafemodeCallbackImpl implements VcnSafemodeCallback {
+ @NonNull private final ParcelUuid mSubGroup;
+
+ private VcnSafemodeCallbackImpl(@NonNull final ParcelUuid subGroup) {
+ mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup");
+ }
+
+ @Override
+ public void onEnteredSafemode() {
+ synchronized (mLock) {
+ // Ignore if this subscription group doesn't exist anymore
+ if (!mVcns.containsKey(mSubGroup)) {
+ return;
+ }
+
+ notifyAllPolicyListenersLocked();
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/VibratorManagerService.java b/services/core/java/com/android/server/VibratorManagerService.java
index 6a816af..e7e5d67 100644
--- a/services/core/java/com/android/server/VibratorManagerService.java
+++ b/services/core/java/com/android/server/VibratorManagerService.java
@@ -111,6 +111,7 @@
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 @@
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 @@
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 @@
// 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 @@
}
}
+ 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 @@
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 @@
}
}
+ /** 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 @@
}
@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 @@
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 @@
}
}
+ /** 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 026eb63..2ac365d 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -119,11 +119,17 @@
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 void triggerSyncedVibration(long vibrationId) {
+ public boolean triggerSyncedVibration(long vibrationId) {
+ return false;
+ }
+
+ @Override
+ 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 ecb915f..f0f29a9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -145,6 +145,7 @@
import android.app.ActivityManagerInternal;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.ActivityThread;
+import android.app.AnrController;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AppOpsManagerInternal.CheckOpsDelegate;
@@ -5632,7 +5633,7 @@
final int modeFlags, int userId) {
enforceNotIsolatedCaller("grantUriPermission");
GrantUri grantUri = new GrantUri(userId, uri, modeFlags);
- synchronized (mProcLock) {
+ synchronized (this) {
final ProcessRecord r = getRecordForAppLOSP(caller);
if (r == null) {
throw new SecurityException("Unable to find app for caller "
@@ -5666,7 +5667,7 @@
public void revokeUriPermission(IApplicationThread caller, String targetPackage, Uri uri,
final int modeFlags, int userId) {
enforceNotIsolatedCaller("revokeUriPermission");
- synchronized (mProcLock) {
+ synchronized (this) {
final ProcessRecord r = getRecordForAppLOSP(caller);
if (r == null) {
throw new SecurityException("Unable to find app for caller "
@@ -10699,13 +10700,13 @@
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 + ");
@@ -10714,11 +10715,34 @@
// 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()
@@ -15861,6 +15885,16 @@
// PackageManagerService.
return mConstants.mBootTimeTempAllowlistDuration;
}
+
+ @Override
+ public void registerAnrController(AnrController controller) {
+ mActivityTaskManager.registerAnrController(controller);
+ }
+
+ @Override
+ public void unregisterAnrController(AnrController controller) {
+ mActivityTaskManager.unregisterAnrController(controller);
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index fe71fbf..c971bd2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2984,7 +2984,13 @@
pw.println("Reset all changes for " + packageName + " to default value.");
return 0;
}
- if (platformCompat.clearOverride(changeId, packageName)) {
+ boolean existed;
+ if (killPackage) {
+ existed = platformCompat.clearOverride(changeId, packageName);
+ } else {
+ existed = platformCompat.clearOverrideForTest(changeId, packageName);
+ }
+ if (existed) {
pw.println("Reset change " + changeId + " for " + packageName
+ " to default value.");
} else {
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 8f11a5a..3ff5872 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 @@
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 @@
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 773e313..336a595 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -126,15 +126,20 @@
.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 +177,6 @@
};
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 +200,12 @@
*/
@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 +241,22 @@
@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 +332,14 @@
} 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);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index c18031f..7bdf43c 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 @@
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 @@
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 @@
}
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 @@
}
}
}
- } 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/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 64e307a..1653123 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -416,6 +416,14 @@
return;
}
+ // Retrieve max ANR delay from AnrControllers without the mService lock since the
+ // controllers might in turn call into apps
+ long anrDialogDelayMs = mService.mActivityTaskManager.getMaxAnrDelayMillis(aInfo);
+ if (aInfo != null && aInfo.packageName != null && anrDialogDelayMs > 0) {
+ Slog.i(TAG, "Delaying ANR dialog for " + aInfo.packageName + " for " + anrDialogDelayMs
+ + "ms");
+ }
+
synchronized (mService) {
// mBatteryStatsService can be null if the AMS is constructed with injector only. This
// will only happen in tests.
@@ -447,7 +455,7 @@
msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
msg.obj = new AppNotRespondingDialog.Data(mApp, aInfo, aboveSystem);
- mService.mUiHandler.sendMessage(msg);
+ mService.mUiHandler.sendMessageDelayed(msg, anrDialogDelayMs);
}
}
}
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 7299e81..e022e97 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -81,6 +81,7 @@
static final String[] sDeviceConfigScopes = new String[] {
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
DeviceConfig.NAMESPACE_CONFIGURATION,
+ DeviceConfig.NAMESPACE_CONNECTIVITY,
DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS,
DeviceConfig.NAMESPACE_MEDIA_NATIVE,
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 3acad49..76c3467 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 @@
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 @@
}
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 @@
}
}
- //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 @@
}
}
- //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/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 9aea7c4..f72fb1f 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -765,10 +765,6 @@
return mAudioService.getVssVolumeForDevice(streamType, device);
}
- /*package*/ int getModeOwnerPid() {
- return mModeOwnerPid;
- }
-
/*package*/ int getDeviceForStream(int streamType) {
return mAudioService.getDeviceForStream(streamType);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 1529d71..6f625a7 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -95,6 +95,7 @@
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMetrics;
+import android.media.MediaRecorder.AudioSource;
import android.media.PlayerBase;
import android.media.VolumePolicy;
import android.media.audiofx.AudioEffect;
@@ -161,9 +162,11 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -300,6 +303,10 @@
private static final int MSG_STREAM_DEVICES_CHANGED = 32;
private static final int MSG_UPDATE_VOLUME_STATES_FOR_DEVICE = 33;
private static final int MSG_REINIT_VOLUMES = 34;
+ private static final int MSG_UPDATE_A11Y_SERVICE_UIDS = 35;
+ private static final int MSG_UPDATE_AUDIO_MODE = 36;
+ private static final int MSG_RECORDING_CONFIG_CHANGE = 37;
+
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -699,8 +706,9 @@
private long mLoweredFromNormalToVibrateTime;
// Array of Uids of valid accessibility services to check if caller is one of them
- private int[] mAccessibilityServiceUids;
private final Object mAccessibilityServiceUidsLock = new Object();
+ @GuardedBy("mAccessibilityServiceUidsLock")
+ private int[] mAccessibilityServiceUids;
// Uid of the active input method service to check if caller is the one or not.
private int mInputMethodServiceUid = android.os.Process.INVALID_UID;
@@ -939,6 +947,7 @@
mDeviceBroker = new AudioDeviceBroker(mContext, this);
mRecordMonitor = new RecordingActivityMonitor(mContext);
+ mRecordMonitor.registerRecordingCallback(mVoiceRecordingActivityMonitor, true);
// must be called before readPersistedSettings() which needs a valid mStreamVolumeAlias[]
// array initialized by updateStreamVolumeAlias()
@@ -948,6 +957,8 @@
mPlaybackMonitor =
new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]);
+ mPlaybackMonitor.registerPlaybackCallback(mVoicePlaybackActivityMonitor, true);
+
mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
readAndSetLowRamDevice();
@@ -1196,12 +1207,8 @@
// Restore call state
synchronized (mDeviceBroker.mSetModeLock) {
- if (mAudioSystem.setPhoneState(mMode, getModeOwnerUid())
- == AudioSystem.AUDIO_STATUS_OK) {
- mModeLogger.log(new AudioEventLogger.StringEvent(
- "onAudioServerDied causes setPhoneState(" + AudioSystem.modeToString(mMode)
- + ", uid=" + getModeOwnerUid() + ")"));
- }
+ onUpdateAudioMode(AudioSystem.MODE_CURRENT, android.os.Process.myPid(),
+ mContext.getPackageName());
}
final int forSys;
synchronized (mSettingsLock) {
@@ -2989,7 +2996,7 @@
}
/*package*/ int getHearingAidStreamType() {
- return getHearingAidStreamType(mMode);
+ return getHearingAidStreamType(getMode());
}
private int getHearingAidStreamType(int mode) {
@@ -3002,15 +3009,15 @@
// other conditions will influence the stream type choice, read on...
break;
}
- if (mVoiceActive.get()) {
+ if (mVoicePlaybackActive.get()) {
return AudioSystem.STREAM_VOICE_CALL;
}
return AudioSystem.STREAM_MUSIC;
}
- private AtomicBoolean mVoiceActive = new AtomicBoolean(false);
+ private AtomicBoolean mVoicePlaybackActive = new AtomicBoolean(false);
- private final IPlaybackConfigDispatcher mVoiceActivityMonitor =
+ private final IPlaybackConfigDispatcher mVoicePlaybackActivityMonitor =
new IPlaybackConfigDispatcher.Stub() {
@Override
public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs,
@@ -3032,16 +3039,126 @@
break;
}
}
- if (mVoiceActive.getAndSet(voiceActive) != voiceActive) {
+ if (mVoicePlaybackActive.getAndSet(voiceActive) != voiceActive) {
updateHearingAidVolumeOnVoiceActivityUpdate();
}
+
+ // Update playback active state for all apps in audio mode stack.
+ // When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE
+ // and request an audio mode update immediately. Upon any other change, queue the message
+ // and request an audio mode update after a grace period.
+ synchronized (mDeviceBroker.mSetModeLock) {
+ boolean updateAudioMode = false;
+ int existingMsgPolicy = SENDMSG_QUEUE;
+ int delay = CHECK_MODE_FOR_UID_PERIOD_MS;
+ for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+ boolean wasActive = h.isActive();
+ h.setPlaybackActive(false);
+ for (AudioPlaybackConfiguration config : configs) {
+ final int usage = config.getAudioAttributes().getUsage();
+ if (config.getClientUid() == h.getUid()
+ && (usage == AudioAttributes.USAGE_VOICE_COMMUNICATION
+ || usage == AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
+ && config.getPlayerState()
+ == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
+ h.setPlaybackActive(true);
+ break;
+ }
+ }
+ if (wasActive != h.isActive()) {
+ updateAudioMode = true;
+ if (h.isActive() && h == getAudioModeOwnerHandler()) {
+ existingMsgPolicy = SENDMSG_REPLACE;
+ delay = 0;
+ }
+ }
+ }
+ if (updateAudioMode) {
+ sendMsg(mAudioHandler,
+ MSG_UPDATE_AUDIO_MODE,
+ existingMsgPolicy,
+ AudioSystem.MODE_CURRENT,
+ android.os.Process.myPid(),
+ mContext.getPackageName(),
+ delay);
+ }
+ }
+ }
+
+ private final IRecordingConfigDispatcher mVoiceRecordingActivityMonitor =
+ new IRecordingConfigDispatcher.Stub() {
+ @Override
+ public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
+ sendMsg(mAudioHandler, MSG_RECORDING_CONFIG_CHANGE, SENDMSG_REPLACE,
+ 0 /*arg1 ignored*/, 0 /*arg2 ignored*/,
+ configs /*obj*/, 0 /*delay*/);
+ }
+ };
+
+ private void onRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
+ // Update recording active state for all apps in audio mode stack.
+ // When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE
+ // and request an audio mode update immediately. Upon any other change, queue the message
+ // and request an audio mode update after a grace period.
+ synchronized (mDeviceBroker.mSetModeLock) {
+ boolean updateAudioMode = false;
+ int existingMsgPolicy = SENDMSG_QUEUE;
+ int delay = CHECK_MODE_FOR_UID_PERIOD_MS;
+ for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+ boolean wasActive = h.isActive();
+ h.setRecordingActive(false);
+ for (AudioRecordingConfiguration config : configs) {
+ if (config.getClientUid() == h.getUid()
+ && config.getAudioSource() == AudioSource.VOICE_COMMUNICATION) {
+ h.setRecordingActive(true);
+ break;
+ }
+ }
+ if (wasActive != h.isActive()) {
+ updateAudioMode = true;
+ if (h.isActive() && h == getAudioModeOwnerHandler()) {
+ existingMsgPolicy = SENDMSG_REPLACE;
+ delay = 0;
+ }
+ }
+ }
+ if (updateAudioMode) {
+ sendMsg(mAudioHandler,
+ MSG_UPDATE_AUDIO_MODE,
+ existingMsgPolicy,
+ AudioSystem.MODE_CURRENT,
+ android.os.Process.myPid(),
+ mContext.getPackageName(),
+ delay);
+ }
+ }
+ }
+
+ private void dumpAudioMode(PrintWriter pw) {
+ pw.println("\nAudio mode: ");
+ pw.println("- Current mode = " + AudioSystem.modeToString(getMode()));
+ pw.println("- Mode owner: ");
+ SetModeDeathHandler hdlr = getAudioModeOwnerHandler();
+ if (hdlr != null) {
+ hdlr.dump(pw, -1);
+ } else {
+ pw.println(" None");
+ }
+ pw.println("- Mode owner stack: ");
+ if (mSetModeDeathHandlers.isEmpty()) {
+ pw.println(" Empty");
+ } else {
+ for (int i = 0; i < mSetModeDeathHandlers.size(); i++) {
+ mSetModeDeathHandlers.get(i).dump(pw, i);
+ }
+ }
}
private void updateHearingAidVolumeOnVoiceActivityUpdate() {
final int streamType = getHearingAidStreamType();
final int index = getStreamVolume(streamType);
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_HEARING_AID,
- mVoiceActive.get(), streamType, index));
+ mVoicePlaybackActive.get(), streamType, index));
mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType);
}
@@ -4058,65 +4175,46 @@
}
- /**
- * Return the pid of the current audio mode owner
- * @return 0 if nobody owns the mode
- */
- /*package*/ int getModeOwnerPid() {
- int modeOwnerPid = 0;
- try {
- modeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
- } catch (Exception e) {
- // nothing to do, modeOwnerPid is not modified
- }
- return modeOwnerPid;
- }
-
- /**
- * Return the uid of the current audio mode owner
- * @return 0 if nobody owns the mode
- */
- /*package*/ int getModeOwnerUid() {
- int modeOwnerUid = 0;
- try {
- modeOwnerUid = mSetModeDeathHandlers.get(0).getUid();
- } catch (Exception e) {
- // nothing to do, modeOwnerUid is not modified
- }
- return modeOwnerUid;
- }
-
private class SetModeDeathHandler implements IBinder.DeathRecipient {
private final IBinder mCb; // To be notified of client's death
private final int mPid;
private final int mUid;
private final boolean mIsPrivileged;
private final String mPackage;
- private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
+ private int mMode;
+ private long mUpdateTime;
+ private boolean mPlaybackActive = false;
+ private boolean mRecordingActive = false;
- SetModeDeathHandler(IBinder cb, int pid, int uid, boolean isPrivileged, String caller) {
+ SetModeDeathHandler(IBinder cb, int pid, int uid, boolean isPrivileged,
+ String caller, int mode) {
+ mMode = mode;
mCb = cb;
mPid = pid;
mUid = uid;
- mIsPrivileged = isPrivileged;
mPackage = caller;
+ mIsPrivileged = isPrivileged;
+ mUpdateTime = java.lang.System.currentTimeMillis();
}
public void binderDied() {
- int newModeOwnerPid = 0;
synchronized (mDeviceBroker.mSetModeLock) {
- Log.w(TAG, "setMode() client died");
+ Log.w(TAG, "SetModeDeathHandler client died");
int index = mSetModeDeathHandlers.indexOf(this);
if (index < 0) {
- Log.w(TAG, "unregistered setMode() client died");
+ Log.w(TAG, "unregistered SetModeDeathHandler client died");
} else {
- newModeOwnerPid = setModeInt(
- AudioSystem.MODE_NORMAL, mCb, mPid, mUid, mIsPrivileged, TAG);
+ SetModeDeathHandler h = mSetModeDeathHandlers.get(index);
+ mSetModeDeathHandlers.remove(index);
+ sendMsg(mAudioHandler,
+ MSG_UPDATE_AUDIO_MODE,
+ SENDMSG_QUEUE,
+ AudioSystem.MODE_CURRENT,
+ android.os.Process.myPid(),
+ mContext.getPackageName(),
+ 0);
}
}
- // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
- // SCO connections not started by the application changing the mode when pid changes
- mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, AudioService.this.getMode());
}
public int getPid() {
@@ -4125,6 +4223,7 @@
public void setMode(int mode) {
mMode = mode;
+ mUpdateTime = java.lang.System.currentTimeMillis();
}
public int getMode() {
@@ -4146,25 +4245,131 @@
public boolean isPrivileged() {
return mIsPrivileged;
}
+
+ public long getUpdateTime() {
+ return mUpdateTime;
+ }
+
+ public void setPlaybackActive(boolean active) {
+ mPlaybackActive = active;
+ }
+
+ public void setRecordingActive(boolean active) {
+ mRecordingActive = active;
+ }
+
+ /**
+ * An app is considered active if:
+ * - It is privileged (has MODIFY_PHONE_STATE permission)
+ * or
+ * - It requests mode MODE_IN_COMMUNICATION, and it is either playing
+ * or recording for VOICE_COMMUNICATION.
+ * or
+ * - It requests a mode different from MODE_IN_COMMUNICATION or MODE_NORMAL
+ */
+ public boolean isActive() {
+ return mIsPrivileged
+ || ((mMode == AudioSystem.MODE_IN_COMMUNICATION)
+ && (mRecordingActive || mPlaybackActive))
+ || mMode == AudioSystem.MODE_IN_CALL
+ || mMode == AudioSystem.MODE_RINGTONE
+ || mMode == AudioSystem.MODE_CALL_SCREENING;
+ }
+
+ public void dump(PrintWriter pw, int index) {
+ SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm:ss:SSS");
+
+ if (index >= 0) {
+ pw.println(" Requester # " + (index + 1) + ":");
+ }
+ pw.println(" - Mode: " + AudioSystem.modeToString(mMode));
+ pw.println(" - Binder: " + mCb);
+ pw.println(" - Pid: " + mPid);
+ pw.println(" - Uid: " + mUid);
+ pw.println(" - Package: " + mPackage);
+ pw.println(" - Privileged: " + mIsPrivileged);
+ pw.println(" - Active: " + isActive());
+ pw.println(" Playback active: " + mPlaybackActive);
+ pw.println(" Recording active: " + mRecordingActive);
+ pw.println(" - update time: " + format.format(new Date(mUpdateTime)));
+ }
+ }
+
+ @GuardedBy("mDeviceBroker.mSetModeLock")
+ private SetModeDeathHandler getAudioModeOwnerHandler() {
+ // The Audio mode owner is:
+ // 1) the most recent privileged app in the stack
+ // 2) the most recent active app in the tack
+ SetModeDeathHandler modeOwner = null;
+ SetModeDeathHandler privilegedModeOwner = null;
+ for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+ if (h.isActive()) {
+ // privileged apps are always active
+ if (h.isPrivileged()) {
+ if (privilegedModeOwner == null
+ || h.getUpdateTime() > privilegedModeOwner.getUpdateTime()) {
+ privilegedModeOwner = h;
+ }
+ } else {
+ if (modeOwner == null
+ || h.getUpdateTime() > modeOwner.getUpdateTime()) {
+ modeOwner = h;
+ }
+ }
+ }
+ }
+ return privilegedModeOwner != null ? privilegedModeOwner : modeOwner;
+ }
+
+ /**
+ * Return the pid of the current audio mode owner
+ * @return 0 if nobody owns the mode
+ */
+ @GuardedBy("mDeviceBroker.mSetModeLock")
+ /*package*/ int getModeOwnerPid() {
+ SetModeDeathHandler hdlr = getAudioModeOwnerHandler();
+ if (hdlr != null) {
+ return hdlr.getPid();
+ }
+ return 0;
+ }
+
+ /**
+ * Return the uid of the current audio mode owner
+ * @return 0 if nobody owns the mode
+ */
+ @GuardedBy("mDeviceBroker.mSetModeLock")
+ /*package*/ int getModeOwnerUid() {
+ SetModeDeathHandler hdlr = getAudioModeOwnerHandler();
+ if (hdlr != null) {
+ return hdlr.getUid();
+ }
+ return 0;
}
/** @see AudioManager#setMode(int) */
public void setMode(int mode, IBinder cb, String callingPackage) {
+ int pid = Binder.getCallingPid();
+ int uid = Binder.getCallingUid();
if (DEBUG_MODE) {
- Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")");
+ Log.v(TAG, "setMode(mode=" + mode + ", pid=" + pid
+ + ", uid=" + uid + ", caller=" + callingPackage + ")");
}
if (!checkAudioSettingsPermission("setMode()")) {
return;
}
- final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_PHONE_STATE)
- == PackageManager.PERMISSION_GRANTED;
- final int callingPid = Binder.getCallingPid();
- if ((mode == AudioSystem.MODE_IN_CALL) && !hasModifyPhoneStatePermission) {
- Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid="
- + callingPid + ", uid=" + Binder.getCallingUid());
+ if (cb == null) {
+ Log.e(TAG, "setMode() called with null binder");
return;
}
+ if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
+ Log.w(TAG, "setMode() invalid mode: " + mode);
+ return;
+ }
+
+ if (mode == AudioSystem.MODE_CURRENT) {
+ mode = getMode();
+ }
if (mode == AudioSystem.MODE_CALL_SCREENING && !mIsCallScreeningModeSupported) {
Log.w(TAG, "setMode(MODE_CALL_SCREENING) not permitted "
@@ -4172,171 +4377,152 @@
return;
}
- if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
+ final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE)
+ == PackageManager.PERMISSION_GRANTED;
+ if ((mode == AudioSystem.MODE_IN_CALL) && !hasModifyPhoneStatePermission) {
+ Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid="
+ + pid + ", uid=" + Binder.getCallingUid());
return;
}
- int newModeOwnerPid;
+ SetModeDeathHandler currentModeHandler = null;
synchronized (mDeviceBroker.mSetModeLock) {
- if (mode == AudioSystem.MODE_CURRENT) {
- mode = mMode;
+ for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+ if (h.getPid() == pid) {
+ currentModeHandler = h;
+ break;
+ }
}
- int oldModeOwnerPid = getModeOwnerPid();
- // Do not allow changing mode if a call is active and the requester
- // does not have permission to modify phone state or is not the mode owner,
- // unless returning to NORMAL mode (will not change current mode owner) or
- // not changing mode in which case the mode owner will reflect the last
- // requester of current mode
- if (!((mode == mMode) || (mode == AudioSystem.MODE_NORMAL))
- && ((mMode == AudioSystem.MODE_IN_CALL)
- || (mMode == AudioSystem.MODE_IN_COMMUNICATION))
- && !(hasModifyPhoneStatePermission || (oldModeOwnerPid == callingPid))) {
- Log.w(TAG, "setMode(" + mode + ") from pid=" + callingPid
- + ", uid=" + Binder.getCallingUid()
- + ", cannot change mode from " + mMode
- + " without permission or being mode owner");
- return;
+
+ if (mode == AudioSystem.MODE_NORMAL) {
+ if (currentModeHandler != null) {
+ if (!currentModeHandler.isPrivileged()
+ && currentModeHandler.getMode() == AudioSystem.MODE_IN_COMMUNICATION) {
+ mAudioHandler.removeEqualMessages(
+ MSG_CHECK_MODE_FOR_UID, currentModeHandler);
+ }
+ mSetModeDeathHandlers.remove(currentModeHandler);
+ if (DEBUG_MODE) {
+ Log.v(TAG, "setMode(" + mode + ") removing hldr for pid: " + pid);
+ }
+ try {
+ currentModeHandler.getBinder().unlinkToDeath(currentModeHandler, 0);
+ } catch (NoSuchElementException e) {
+ Log.w(TAG, "setMode link does not exist ...");
+ }
+ }
+ } else {
+ if (currentModeHandler != null) {
+ currentModeHandler.setMode(mode);
+ if (DEBUG_MODE) {
+ Log.v(TAG, "setMode(" + mode + ") updating hldr for pid: " + pid);
+ }
+ } else {
+ currentModeHandler = new SetModeDeathHandler(cb, pid, uid,
+ hasModifyPhoneStatePermission, callingPackage, mode);
+ // Register for client death notification
+ try {
+ cb.linkToDeath(currentModeHandler, 0);
+ } catch (RemoteException e) {
+ // Client has died!
+ Log.w(TAG, "setMode() could not link to " + cb + " binder death");
+ return;
+ }
+ mSetModeDeathHandlers.add(currentModeHandler);
+ if (DEBUG_MODE) {
+ Log.v(TAG, "setMode(" + mode + ") adding handler for pid=" + pid);
+ }
+ }
+ if (mode == AudioSystem.MODE_IN_COMMUNICATION) {
+ // Force active state when entering/updating the stack to avoid glitches when
+ // an app starts playing/recording after settng the audio mode,
+ // and send a reminder to check activity after a grace period.
+ if (!currentModeHandler.isPrivileged()) {
+ currentModeHandler.setPlaybackActive(true);
+ currentModeHandler.setRecordingActive(true);
+ sendMsg(mAudioHandler,
+ MSG_CHECK_MODE_FOR_UID,
+ SENDMSG_QUEUE,
+ 0,
+ 0,
+ currentModeHandler,
+ CHECK_MODE_FOR_UID_PERIOD_MS);
+ }
+ }
}
- newModeOwnerPid = setModeInt(mode, cb, callingPid, Binder.getCallingUid(),
- hasModifyPhoneStatePermission, callingPackage);
+
+ sendMsg(mAudioHandler,
+ MSG_UPDATE_AUDIO_MODE,
+ SENDMSG_REPLACE,
+ mode,
+ pid,
+ callingPackage,
+ 0);
}
- // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
- // SCO connections not started by the application changing the mode when pid changes
- mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, getMode());
}
- // setModeInt() returns a valid PID if the audio mode was successfully set to
- // any mode other than NORMAL.
@GuardedBy("mDeviceBroker.mSetModeLock")
- private int setModeInt(
- int mode, IBinder cb, int pid, int uid, boolean isPrivileged, String caller) {
+ void onUpdateAudioMode(int requestedMode, int requesterPid, String requesterPackage) {
+ if (requestedMode == AudioSystem.MODE_CURRENT) {
+ requestedMode = getMode();
+ }
+ int mode = AudioSystem.MODE_NORMAL;
+ int uid = 0;
+ int pid = 0;
+ SetModeDeathHandler currentModeHandler = getAudioModeOwnerHandler();
+ if (currentModeHandler != null) {
+ mode = currentModeHandler.getMode();
+ uid = currentModeHandler.getUid();
+ pid = currentModeHandler.getPid();
+ }
if (DEBUG_MODE) {
- Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid
- + ", uid=" + uid + ", caller=" + caller + ")");
+ Log.v(TAG, "onUpdateAudioMode() mode: " + mode + ", mMode: " + mMode
+ + " requestedMode: " + requestedMode);
}
- int newModeOwnerPid = 0;
- if (cb == null) {
- Log.e(TAG, "setModeInt() called with null binder");
- return newModeOwnerPid;
- }
+ if (mode != mMode) {
+ final long identity = Binder.clearCallingIdentity();
+ int status = mAudioSystem.setPhoneState(mode, uid);
+ Binder.restoreCallingIdentity(identity);
+ if (status == AudioSystem.AUDIO_STATUS_OK) {
+ if (DEBUG_MODE) {
+ Log.v(TAG, "onUpdateAudioMode: mode successfully set to " + mode);
+ }
+ int previousMode = mMode;
+ mMode = mode;
+ // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL
+ mModeLogger.log(new PhoneStateEvent(requesterPackage, requesterPid,
+ requestedMode, pid, mode));
- SetModeDeathHandler hdlr = null;
- Iterator iter = mSetModeDeathHandlers.iterator();
- while (iter.hasNext()) {
- SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
- if (h.getPid() == pid) {
- hdlr = h;
- // Remove from client list so that it is re-inserted at top of list
- iter.remove();
- if (hdlr.getMode() == AudioSystem.MODE_IN_COMMUNICATION) {
- mAudioHandler.removeEqualMessages(MSG_CHECK_MODE_FOR_UID, hdlr);
- }
- try {
- hdlr.getBinder().unlinkToDeath(hdlr, 0);
- if (cb != hdlr.getBinder()){
- hdlr = null;
- }
- } catch (NoSuchElementException e) {
- hdlr = null;
- Log.w(TAG, "link does not exist ...");
- }
- break;
- }
- }
- final int oldMode = mMode;
- int status = AudioSystem.AUDIO_STATUS_OK;
- int actualMode;
- do {
- actualMode = mode;
- if (mode == AudioSystem.MODE_NORMAL) {
- // get new mode from client at top the list if any
- if (!mSetModeDeathHandlers.isEmpty()) {
- hdlr = mSetModeDeathHandlers.get(0);
- cb = hdlr.getBinder();
- actualMode = hdlr.getMode();
- if (DEBUG_MODE) {
- Log.w(TAG, " using mode=" + mode + " instead due to death hdlr at pid="
- + hdlr.mPid);
- }
- }
+ int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
+ int device = getDeviceForStream(streamType);
+ int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device);
+ setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true,
+ requesterPackage, true /*hasModifyAudioSettings*/);
+
+ updateStreamVolumeAlias(true /*updateVolumes*/, requesterPackage);
+
+ // change of mode may require volume to be re-applied on some devices
+ updateAbsVolumeMultiModeDevices(previousMode, mode);
+
+ // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO
+ // connections not started by the application changing the mode when pid changes
+ mDeviceBroker.postSetModeOwnerPid(pid, mode);
} else {
- if (hdlr == null) {
- hdlr = new SetModeDeathHandler(cb, pid, uid, isPrivileged, caller);
- }
- // Register for client death notification
- try {
- cb.linkToDeath(hdlr, 0);
- } catch (RemoteException e) {
- // Client has died!
- Log.w(TAG, "setMode() could not link to "+cb+" binder death");
- }
-
- // Last client to call setMode() is always at top of client list
- // as required by SetModeDeathHandler.binderDied()
- mSetModeDeathHandlers.add(0, hdlr);
- hdlr.setMode(mode);
- }
-
- if (actualMode != mMode) {
- final long identity = Binder.clearCallingIdentity();
- status = mAudioSystem.setPhoneState(actualMode, getModeOwnerUid());
- Binder.restoreCallingIdentity(identity);
- if (status == AudioSystem.AUDIO_STATUS_OK) {
- if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + actualMode); }
- mMode = actualMode;
- } else {
- if (hdlr != null) {
- mSetModeDeathHandlers.remove(hdlr);
- cb.unlinkToDeath(hdlr, 0);
- }
- // force reading new top of mSetModeDeathHandlers stack
- if (DEBUG_MODE) { Log.w(TAG, " mode set to MODE_NORMAL after phoneState pb"); }
- mode = AudioSystem.MODE_NORMAL;
- }
- } else {
- status = AudioSystem.AUDIO_STATUS_OK;
- }
- } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty());
-
- if (status == AudioSystem.AUDIO_STATUS_OK) {
- if (actualMode != AudioSystem.MODE_NORMAL) {
- newModeOwnerPid = getModeOwnerPid();
- if (newModeOwnerPid == 0) {
- Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
- }
- }
- // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL
- mModeLogger.log(
- new PhoneStateEvent(caller, pid, mode, newModeOwnerPid, actualMode));
-
- int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
- int device = getDeviceForStream(streamType);
- int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device);
- setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, caller,
- true /*hasModifyAudioSettings*/);
-
- updateStreamVolumeAlias(true /*updateVolumes*/, caller);
-
- // change of mode may require volume to be re-applied on some devices
- updateAbsVolumeMultiModeDevices(oldMode, actualMode);
-
- if (actualMode == AudioSystem.MODE_IN_COMMUNICATION
- && !hdlr.isPrivileged()) {
- sendMsg(mAudioHandler,
- MSG_CHECK_MODE_FOR_UID,
- SENDMSG_QUEUE,
- 0,
- 0,
- hdlr,
- CHECK_MODE_FOR_UID_PERIOD_MS);
+ Log.w(TAG, "onUpdateAudioMode: failed to set audio mode to: " + mode);
}
}
- return newModeOwnerPid;
}
/** @see AudioManager#getMode() */
public int getMode() {
- return mMode;
+ synchronized (mDeviceBroker.mSetModeLock) {
+ SetModeDeathHandler currentModeHandler = getAudioModeOwnerHandler();
+ if (currentModeHandler != null) {
+ return currentModeHandler.getMode();
+ }
+ return AudioSystem.MODE_NORMAL;
+ }
}
/** cached value read from audiopolicy manager after initialization. */
@@ -5683,11 +5869,6 @@
throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
+ " (dis)connection, got " + state);
}
- if (state == BluetoothProfile.STATE_CONNECTED) {
- mPlaybackMonitor.registerPlaybackCallback(mVoiceActivityMonitor, true);
- } else {
- mPlaybackMonitor.unregisterPlaybackCallback(mVoiceActivityMonitor);
- }
mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
device, state, suppressNoisyIntent, musicDevice, "AudioService");
}
@@ -7032,6 +7213,9 @@
case MSG_PLAYBACK_CONFIG_CHANGE:
onPlaybackConfigChange((List<AudioPlaybackConfiguration>) msg.obj);
break;
+ case MSG_RECORDING_CONFIG_CHANGE:
+ onRecordingConfigChange((List<AudioRecordingConfiguration>) msg.obj);
+ break;
case MSG_BROADCAST_MICROPHONE_MUTE:
mSystemServer.sendMicrophoneMuteChangedIntent();
@@ -7042,30 +7226,19 @@
if (msg.obj == null) {
break;
}
- // If no other app is currently owning the audio mode and
- // the app corresponding to this mode death handler object is still in the
- // mode owner stack but not capturing or playing audio after 3 seconds,
- // remove it from the stack.
- // Otherwise, check again in 3 seconds.
+ // Update active playback/recording for apps requesting IN_COMMUNICATION
+ // mode after a grace period following the mode change
SetModeDeathHandler h = (SetModeDeathHandler) msg.obj;
if (mSetModeDeathHandlers.indexOf(h) < 0) {
break;
}
- if (getModeOwnerUid() != h.getUid()
- || mRecordMonitor.isRecordingActiveForUid(h.getUid())
- || mPlaybackMonitor.isPlaybackActiveForUid(h.getUid())) {
- sendMsg(mAudioHandler,
- MSG_CHECK_MODE_FOR_UID,
- SENDMSG_QUEUE,
- 0,
- 0,
- h,
- CHECK_MODE_FOR_UID_PERIOD_MS);
- break;
+ boolean wasActive = h.isActive();
+ h.setPlaybackActive(mPlaybackMonitor.isPlaybackActiveForUid(h.getUid()));
+ h.setRecordingActive(mRecordMonitor.isRecordingActiveForUid(h.getUid()));
+ if (wasActive != h.isActive()) {
+ onUpdateAudioMode(AudioSystem.MODE_CURRENT,
+ android.os.Process.myPid(), mContext.getPackageName());
}
- setModeInt(AudioSystem.MODE_NORMAL, h.getBinder(), h.getPid(), h.getUid(),
- h.isPrivileged(), "MSG_CHECK_MODE_FOR_UID");
- mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid()));
}
break;
@@ -7082,6 +7255,16 @@
case MSG_REINIT_VOLUMES:
onReinitVolumes((String) msg.obj);
break;
+
+ case MSG_UPDATE_A11Y_SERVICE_UIDS:
+ onUpdateAccessibilityServiceUids();
+ break;
+
+ case MSG_UPDATE_AUDIO_MODE:
+ synchronized (mDeviceBroker.mSetModeLock) {
+ onUpdateAudioMode(msg.arg1, msg.arg2, (String) msg.obj);
+ }
+ break;
}
}
}
@@ -8112,6 +8295,7 @@
dumpStreamStates(pw);
dumpVolumeGroups(pw);
dumpRingerMode(pw);
+ dumpAudioMode(pw);
pw.println("\nAudio routes:");
pw.print(" mMainType=0x"); pw.println(Integer.toHexString(
mDeviceBroker.getCurAudioRoutes().mainType));
@@ -8148,6 +8332,9 @@
+ " FromRestrictions=" + mMicMuteFromRestrictions
+ " FromApi=" + mMicMuteFromApi
+ " from system=" + mMicMuteFromSystemCached);
+ pw.print("\n mAssistantUid="); pw.println(mAssistantUid);
+ pw.print(" mCurrentImeUid="); pw.println(mCurrentImeUid);
+ dumpAccessibilityServiceUids(pw);
dumpAudioPolicies(pw);
mDynPolicyLogger.dump(pw);
@@ -8181,6 +8368,19 @@
}
}
+ private void dumpAccessibilityServiceUids(PrintWriter pw) {
+ synchronized (mSupportedSystemUsagesLock) {
+ if (mAccessibilityServiceUids != null && mAccessibilityServiceUids.length > 0) {
+ pw.println(" Accessibility service Uids:");
+ for (int uid : mAccessibilityServiceUids) {
+ pw.println(" - " + uid);
+ }
+ } else {
+ pw.println(" No accessibility service Uids.");
+ }
+ }
+ }
+
/**
* Audio Analytics ids.
*/
@@ -8484,7 +8684,8 @@
mAccessibilityServiceUids = uids.toArray();
}
}
- AudioSystem.setA11yServicesUids(mAccessibilityServiceUids);
+ sendMsg(mAudioHandler, MSG_UPDATE_A11Y_SERVICE_UIDS, SENDMSG_REPLACE,
+ 0, 0, null, 0);
}
}
@@ -8502,6 +8703,14 @@
}
}
+ private void onUpdateAccessibilityServiceUids() {
+ int[] accessibilityServiceUids;
+ synchronized (mAccessibilityServiceUidsLock) {
+ accessibilityServiceUids = mAccessibilityServiceUids;
+ }
+ AudioSystem.setA11yServicesUids(accessibilityServiceUids);
+ }
+
//==========================================================================================
// Audio policy management
//==========================================================================================
@@ -9057,6 +9266,18 @@
}
/**
+ * Update player session ID
+ * @param piid Player id to update
+ * @param sessionId The new audio session ID
+ */
+ public void playerSessionId(int piid, int sessionId) {
+ if (sessionId <= AudioSystem.AUDIO_SESSION_ALLOCATE) {
+ throw new IllegalArgumentException("invalid session Id " + sessionId);
+ }
+ mPlaybackMonitor.playerSessionId(piid, sessionId, Binder.getCallingUid());
+ }
+
+ /**
* Update player event
* @param piid Player id to update
* @param event The new player event
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 36c67cd..68a084e 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -197,6 +197,28 @@
}
}
+ /**
+ * Update player session ID
+ * @param piid Player id to update
+ * @param sessionId The new audio session ID
+ * @param binderUid Calling binder uid
+ */
+ public void playerSessionId(int piid, int sessionId, int binderUid) {
+ final boolean change;
+ synchronized (mPlayerLock) {
+ final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
+ if (checkConfigurationCaller(piid, apc, binderUid)) {
+ change = apc.handleSessionIdEvent(sessionId);
+ } else {
+ Log.e(TAG, "Error updating audio session");
+ change = false;
+ }
+ }
+ if (change) {
+ dispatchPlaybackChange(false);
+ }
+ }
+
private static final int FLAGS_FOR_SILENCE_OVERRIDE =
AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY |
AudioAttributes.FLAG_BYPASS_MUTE;
@@ -921,6 +943,7 @@
private final int mClientUid;
private final int mClientPid;
private final AudioAttributes mPlayerAttr;
+ private final int mSessionId;
NewPlayerEvent(AudioPlaybackConfiguration apc) {
mPlayerIId = apc.getPlayerInterfaceId();
@@ -928,6 +951,7 @@
mClientUid = apc.getClientUid();
mClientPid = apc.getClientPid();
mPlayerAttr = apc.getAudioAttributes();
+ mSessionId = apc.getSessionId();
}
@Override
@@ -935,7 +959,8 @@
return new String("new player piid:" + mPlayerIId + " uid/pid:" + mClientUid + "/"
+ mClientPid + " type:"
+ AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType)
- + " attr:" + mPlayerAttr);
+ + " attr:" + mPlayerAttr
+ + " session:" + mSessionId);
}
}
diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java
index 14292d9c..c956043 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 @@
* @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/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index 6905b3d..6851d71 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -90,6 +90,7 @@
int userId, PromptInfo promptInfo, String opPackageName,
boolean checkDevicePolicyManager)
throws RemoteException {
+
final boolean confirmationRequested = promptInfo.isConfirmationRequested();
final boolean biometricRequested = Utils.isBiometricRequested(promptInfo);
final int requestedStrength = Utils.getPublicBiometricStrength(promptInfo);
@@ -111,7 +112,7 @@
@AuthenticatorStatus int status = getStatusForBiometricAuthenticator(
devicePolicyManager, settingObserver, sensor, userId, opPackageName,
- checkDevicePolicyManager, requestedStrength);
+ checkDevicePolicyManager, requestedStrength, promptInfo.getSensorId());
Slog.d(TAG, "Package: " + opPackageName
+ " Sensor ID: " + sensor.id
@@ -141,7 +142,11 @@
DevicePolicyManager devicePolicyManager,
BiometricService.SettingObserver settingObserver,
BiometricSensor sensor, int userId, String opPackageName,
- boolean checkDevicePolicyManager, int requestedStrength) {
+ boolean checkDevicePolicyManager, int requestedStrength, int requestedSensorId) {
+
+ if (requestedSensorId != BiometricManager.SENSOR_ID_ANY && sensor.id != requestedSensorId) {
+ return BIOMETRIC_NO_HARDWARE;
+ }
final boolean wasStrongEnough =
Utils.isAtLeastStrength(sensor.oemStrength, requestedStrength);
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index d87af42..5cd0bbf 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -399,10 +399,15 @@
}
}
- public static boolean isKeyguard(Context context, String clientPackage) {
- final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
- == PackageManager.PERMISSION_GRANTED;
-
+ /**
+ * Checks if a client package matches Keyguard and can perform internal biometric operations.
+ *
+ * @param context The system context.
+ * @param clientPackage The name of the package to be checked against Keyguard.
+ * @return Whether the given package matches Keyguard.
+ */
+ public static boolean isKeyguard(@NonNull Context context, @Nullable String clientPackage) {
+ final boolean hasPermission = hasInternalPermission(context);
final ComponentName keyguardComponent = ComponentName.unflattenFromString(
context.getResources().getString(R.string.config_keyguardComponent));
final String keyguardPackage = keyguardComponent != null
@@ -410,6 +415,34 @@
return hasPermission && keyguardPackage != null && keyguardPackage.equals(clientPackage);
}
+ /**
+ * Checks if a client package matches the Android system and can perform internal biometric
+ * operations.
+ *
+ * @param context The system context.
+ * @param clientPackage The name of the package to be checked against the Android system.
+ * @return Whether the given package matches the Android system.
+ */
+ public static boolean isSystem(@NonNull Context context, @Nullable String clientPackage) {
+ return hasInternalPermission(context) && "android".equals(clientPackage);
+ }
+
+ /**
+ * Checks if a client package matches Settings and can perform internal biometric operations.
+ *
+ * @param context The system context.
+ * @param clientPackage The name of the package to be checked against Settings.
+ * @return Whether the given package matches Settings.
+ */
+ public static boolean isSettings(@NonNull Context context, @Nullable String clientPackage) {
+ return hasInternalPermission(context) && "com.android.settings".equals(clientPackage);
+ }
+
+ private static boolean hasInternalPermission(@NonNull Context context) {
+ return context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
public static String getClientName(@Nullable BaseClientMonitor client) {
return client != null ? client.getClass().getSimpleName() : "null";
}
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 14433fb..b31a54b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -149,9 +149,10 @@
pm.incrementAuthForUser(getTargetUserId(), authenticated);
}
- // Ensure authentication only succeeds if the client activity is on top or is keyguard.
+ // Ensure authentication only succeeds if the client activity is on top.
boolean isBackgroundAuth = false;
- if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString())) {
+ if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString())
+ && !Utils.isSystem(getContext(), getOwnerString())) {
final List<ActivityManager.RunningTaskInfo> tasks =
mActivityTaskManager.getTasks(1);
if (tasks == null || tasks.isEmpty()) {
@@ -166,7 +167,7 @@
final String topPackage = topActivity.getPackageName();
if (!topPackage.contentEquals(getOwnerString())) {
Slog.e(TAG, "Background authentication detected, top: " + topPackage
- + ", client: " + this);
+ + ", client: " + getOwnerString());
isBackgroundAuth = true;
}
}
@@ -310,4 +311,9 @@
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 8fa3bbb..81ce2d5 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 @@
@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 c5237ab..20c25c3 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 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 @@
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 da76af8..25b7add 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 @@
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 8d81016..e1320d8e 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 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 ce24e5e..de57186 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 @@
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 @@
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 @@
}
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 @@
// 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 @@
+ 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 9d19fdf..e3feb74 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 @@
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 @@
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/AidlConversionUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
new file mode 100644
index 0000000..769c47a
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlConversionUtils.java
@@ -0,0 +1,103 @@
+/*
+ * 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.biometrics.sensors.face.aidl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.biometrics.face.AuthenticationFrame;
+import android.hardware.biometrics.face.BaseFrame;
+import android.hardware.biometrics.face.Cell;
+import android.hardware.biometrics.face.EnrollmentFrame;
+import android.hardware.face.FaceAuthenticationFrame;
+import android.hardware.face.FaceDataFrame;
+import android.hardware.face.FaceEnrollCell;
+import android.hardware.face.FaceEnrollFrame;
+
+/**
+ * Utilities for converting between hardware and framework-defined AIDL models.
+ */
+final class AidlConversionUtils {
+ // Prevent instantiation.
+ private AidlConversionUtils() {}
+
+ @NonNull
+ public static FaceAuthenticationFrame convert(@NonNull AuthenticationFrame frame) {
+ return new FaceAuthenticationFrame(convert(frame.data));
+ }
+
+ @NonNull
+ public static AuthenticationFrame convert(@NonNull FaceAuthenticationFrame frame) {
+ final AuthenticationFrame convertedFrame = new AuthenticationFrame();
+ convertedFrame.data = convert(frame.getData());
+ return convertedFrame;
+ }
+
+ @NonNull
+ public static FaceEnrollFrame convert(@NonNull EnrollmentFrame frame) {
+ return new FaceEnrollFrame(convert(frame.cell), frame.stage, convert(frame.data));
+ }
+
+ @NonNull
+ public static EnrollmentFrame convert(@NonNull FaceEnrollFrame frame) {
+ final EnrollmentFrame convertedFrame = new EnrollmentFrame();
+ convertedFrame.cell = convert(frame.getCell());
+ convertedFrame.stage = (byte) frame.getStage();
+ convertedFrame.data = convert(frame.getData());
+ return convertedFrame;
+ }
+
+ @NonNull
+ public static FaceDataFrame convert(@NonNull BaseFrame frame) {
+ return new FaceDataFrame(
+ frame.acquiredInfo,
+ frame.vendorCode,
+ frame.pan,
+ frame.tilt,
+ frame.distance,
+ frame.isCancellable);
+ }
+
+ @NonNull
+ public static BaseFrame convert(@NonNull FaceDataFrame frame) {
+ final BaseFrame convertedFrame = new BaseFrame();
+ convertedFrame.acquiredInfo = (byte) frame.getAcquiredInfo();
+ convertedFrame.vendorCode = frame.getVendorCode();
+ convertedFrame.pan = frame.getPan();
+ convertedFrame.tilt = frame.getTilt();
+ convertedFrame.distance = frame.getDistance();
+ convertedFrame.isCancellable = frame.isCancellable();
+ return convertedFrame;
+ }
+
+ @Nullable
+ public static FaceEnrollCell convert(@Nullable Cell cell) {
+ return cell == null ? null : new FaceEnrollCell(cell.x, cell.y, cell.z);
+ }
+
+ @Nullable
+ public static Cell convert(@Nullable FaceEnrollCell cell) {
+ if (cell == null) {
+ return null;
+ }
+
+ final Cell convertedCell = new Cell();
+ convertedCell.x = cell.getX();
+ convertedCell.y = cell.getY();
+ convertedCell.z = cell.getZ();
+ return convertedCell;
+ }
+}
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 d2673d2..897ebd7 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.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 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 @@
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 @@
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 a7bfc4b..8f55402 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
@@ -28,6 +28,7 @@
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.FaceManager;
import android.os.IBinder;
import android.os.RemoteException;
@@ -177,22 +178,42 @@
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);
}
+ /**
+ * Called each time a new frame is received during face authentication.
+ *
+ * @param frame Information about the current frame.
+ */
+ public void onAuthenticationFrame(@NonNull FaceAuthenticationFrame frame) {
+ // 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) {
mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED);
// Lockout metrics are logged as an error code.
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 afc7f64..898d81b 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,6 +27,7 @@
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.Face;
+import android.hardware.face.FaceEnrollFrame;
import android.hardware.face.FaceManager;
import android.os.IBinder;
import android.os.NativeHandle;
@@ -99,17 +100,40 @@
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);
}
+ /**
+ * Called each time a new frame is received during face enrollment.
+ *
+ * @param frame Information about the current frame.
+ */
+ public void onEnrollmentFrame(@NonNull FaceEnrollFrame frame) {
+ // 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
protected void startHalOperation() {
final ArrayList<Byte> token = new ArrayList<>();
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index e685ee2..1b6b9d7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -643,8 +643,7 @@
final Sensor sensor = mSensors.valueAt(i);
final int sensorId = mSensors.keyAt(i);
PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount();
- sensor.getScheduler().recordCrashState();
- sensor.getScheduler().reset();
+ sensor.onBinderDied();
}
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
index f49601a..baeb3fd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java
@@ -33,7 +33,6 @@
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.Handler;
-import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserManager;
import android.util.Slog;
@@ -45,7 +44,6 @@
import com.android.server.biometrics.SensorStateProto;
import com.android.server.biometrics.UserStateProto;
import com.android.server.biometrics.Utils;
-import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
@@ -64,7 +62,7 @@
/**
* Maintains the state of a single sensor within an instance of the {@link IFace} HAL.
*/
-public class Sensor implements IBinder.DeathRecipient {
+public class Sensor {
private boolean mTestHalEnabled;
@@ -170,33 +168,39 @@
@Override
public void onAuthenticationFrame(AuthenticationFrame frame) {
- // TODO(b/174619156): propagate the frame to an AuthenticationClient
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AcquisitionClient)) {
- Slog.e(mTag, "onAcquired for non-acquisition client: "
+ if (!(client instanceof FaceAuthenticationClient)) {
+ Slog.e(mTag, "onAuthenticationFrame for incompatible client: "
+ + Utils.getClientName(client));
+ return;
+
+ }
+ if (frame == null) {
+ Slog.e(mTag, "Received null authentication frame for client: "
+ Utils.getClientName(client));
return;
}
-
- final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
- acquisitionClient.onAcquired(frame.data.acquiredInfo, frame.data.vendorCode);
+ ((FaceAuthenticationClient) client).onAuthenticationFrame(
+ AidlConversionUtils.convert(frame));
});
}
@Override
public void onEnrollmentFrame(EnrollmentFrame frame) {
- // TODO(b/174619156): propagate the frame to an EnrollmentClient
mHandler.post(() -> {
final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (!(client instanceof AcquisitionClient)) {
- Slog.e(mTag, "onAcquired for non-acquisition client: "
+ if (!(client instanceof FaceEnrollClient)) {
+ Slog.e(mTag, "onEnrollmentFrame for incompatible client: "
+ Utils.getClientName(client));
return;
}
-
- final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
- acquisitionClient.onAcquired(frame.data.acquiredInfo, frame.data.vendorCode);
+ if (frame == null) {
+ Slog.e(mTag, "Received null enrollment frame for client: "
+ + Utils.getClientName(client));
+ return;
+ }
+ ((FaceEnrollClient) client).onEnrollmentFrame(AidlConversionUtils.convert(frame));
});
}
@@ -476,7 +480,6 @@
mTag, mScheduler, sensorId, userId, callback);
final ISession newSession = daemon.createSession(sensorId, userId, resultController);
- newSession.asBinder().linkToDeath(this, 0 /* flags */);
mCurrentSession = new Session(mTag, newSession, userId, resultController);
}
@@ -518,24 +521,21 @@
proto.end(sensorToken);
}
- @Override
- public void binderDied() {
- Slog.e(mTag, "Binder died");
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (client instanceof Interruptable) {
- Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
- final Interruptable interruptable = (Interruptable) client;
- interruptable.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */);
+ public void onBinderDied() {
+ final BaseClientMonitor client = mScheduler.getCurrentClient();
+ if (client instanceof Interruptable) {
+ Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+ final Interruptable interruptable = (Interruptable) client;
+ interruptable.onError(FaceManager.FACE_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
- mScheduler.recordCrashState();
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+ BiometricsProtoEnums.MODALITY_FACE,
+ BiometricsProtoEnums.ISSUE_HAL_DEATH);
+ }
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
- BiometricsProtoEnums.MODALITY_FACE,
- BiometricsProtoEnums.ISSUE_HAL_DEATH);
- mCurrentSession = null;
- }
- });
+ mScheduler.recordCrashState();
+ mScheduler.reset();
+ mCurrentSession = null;
}
}
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 4142a52..d519d60 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.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 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/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 0265cb9..b0e42cd 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -25,6 +25,10 @@
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR;
+import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -32,6 +36,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricManager;
+import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricService;
@@ -49,6 +54,7 @@
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.Binder;
import android.os.Build;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
@@ -80,6 +86,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* A service to manage multiple clients that want to access the fingerprint HAL API.
@@ -190,7 +197,7 @@
@Override // Binder call
public void enroll(final IBinder token, final byte[] hardwareAuthToken, final int userId,
final IFingerprintServiceReceiver receiver, final String opPackageName,
- boolean shouldLogMetrics) {
+ @FingerprintManager.EnrollReason int enrollReason) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -200,7 +207,7 @@
}
provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
- receiver, opPackageName, shouldLogMetrics);
+ receiver, opPackageName, enrollReason);
}
@Override // Binder call
@@ -219,8 +226,8 @@
@SuppressWarnings("deprecation")
@Override // Binder call
public void authenticate(final IBinder token, final long operationId,
- @FingerprintManager.SensorId final int sensorId, final int userId,
- final IFingerprintServiceReceiver receiver, final String opPackageName) {
+ final int sensorId, final int userId, final IFingerprintServiceReceiver receiver,
+ final String opPackageName) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
@@ -236,7 +243,7 @@
final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName);
// Clear calling identity when checking LockPatternUtils for StrongAuth flags.
- final long identity = Binder.clearCallingIdentity();
+ long identity = Binder.clearCallingIdentity();
try {
if (isKeyguard && Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) {
// If this happens, something in KeyguardUpdateMonitor is wrong.
@@ -266,9 +273,101 @@
return;
}
- provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
- 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName,
- restricted, statsClient, isKeyguard);
+ final FingerprintSensorPropertiesInternal sensorProps =
+ provider.second.getSensorProperties(sensorId);
+ if (!isKeyguard && !Utils.isSettings(getContext(), opPackageName)
+ && sensorProps != null && sensorProps.isAnyUdfpsType()) {
+ identity = Binder.clearCallingIdentity();
+ try {
+ authenticateWithPrompt(operationId, sensorProps, userId, receiver);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } else {
+ provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
+ 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName,
+ restricted, statsClient, isKeyguard);
+ }
+ }
+
+ private void authenticateWithPrompt(
+ final long operationId,
+ @NonNull final FingerprintSensorPropertiesInternal props,
+ final int userId,
+ final IFingerprintServiceReceiver receiver) {
+
+ final Context context = getUiContext();
+ final Executor executor = context.getMainExecutor();
+
+ final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(context)
+ .setTitle(context.getString(R.string.biometric_dialog_default_title))
+ .setSubtitle(context.getString(R.string.fingerprint_dialog_default_subtitle))
+ .setNegativeButton(
+ context.getString(R.string.cancel),
+ executor,
+ (dialog, which) -> {
+ try {
+ receiver.onError(
+ FINGERPRINT_ERROR_USER_CANCELED, 0 /* vendorCode */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in negative button onClick()", e);
+ }
+ })
+ .setSensorId(props.sensorId)
+ .build();
+
+ final BiometricPrompt.AuthenticationCallback promptCallback =
+ new BiometricPrompt.AuthenticationCallback() {
+ @Override
+ public void onAuthenticationError(int errorCode, CharSequence errString) {
+ try {
+ if (FingerprintUtils.isKnownErrorCode(errorCode)) {
+ receiver.onError(errorCode, 0 /* vendorCode */);
+ } else {
+ receiver.onError(FINGERPRINT_ERROR_VENDOR, errorCode);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in onAuthenticationError()", e);
+ }
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(
+ BiometricPrompt.AuthenticationResult result) {
+ final Fingerprint fingerprint = new Fingerprint("", 0, 0L);
+ final boolean isStrong = props.sensorStrength == STRENGTH_STRONG;
+ try {
+ receiver.onAuthenticationSucceeded(fingerprint, userId, isStrong);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in onAuthenticationSucceeded()", e);
+ }
+ }
+
+ @Override
+ public void onAuthenticationFailed() {
+ try {
+ receiver.onAuthenticationFailed();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in onAuthenticationFailed()", e);
+ }
+ }
+
+ @Override
+ public void onAuthenticationAcquired(int acquireInfo) {
+ try {
+ if (FingerprintUtils.isKnownAcquiredCode(acquireInfo)) {
+ receiver.onAcquired(acquireInfo, 0 /* vendorCode */);
+ } else {
+ receiver.onAcquired(FINGERPRINT_ACQUIRED_VENDOR, acquireInfo);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception in onAuthenticationAcquired()", e);
+ }
+ }
+ };
+
+ biometricPrompt.authenticateUserForOperation(
+ new CancellationSignal(), executor, promptCallback, userId, operationId);
}
@Override
@@ -374,6 +473,7 @@
@Override // Binder call
public void cancelAuthenticationFromService(final int sensorId, final IBinder token,
final String opPackageName) {
+
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
final ServiceProvider provider = getProviderForSensor(sensorId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
index dc6fd3a..d69151d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java
@@ -16,8 +16,18 @@
package com.android.server.biometrics.sensors.fingerprint;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_IMAGER_DIRTY;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_INSUFFICIENT;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_PARTIAL;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_TOO_FAST;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_TOO_SLOW;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR;
+
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.biometrics.fingerprint.V2_1.FingerprintError;
import android.hardware.fingerprint.Fingerprint;
import android.text.TextUtils;
import android.util.SparseArray;
@@ -138,5 +148,51 @@
return state;
}
}
+
+ /**
+ * Checks if the given error code corresponds to a known fingerprint error.
+ *
+ * @param errorCode The error code to be checked.
+ * @return Whether the error code corresponds to a known error.
+ */
+ public static boolean isKnownErrorCode(int errorCode) {
+ switch (errorCode) {
+ case FingerprintError.ERROR_HW_UNAVAILABLE:
+ case FingerprintError.ERROR_UNABLE_TO_PROCESS:
+ case FingerprintError.ERROR_TIMEOUT:
+ case FingerprintError.ERROR_NO_SPACE:
+ case FingerprintError.ERROR_CANCELED:
+ case FingerprintError.ERROR_UNABLE_TO_REMOVE:
+ case FingerprintError.ERROR_LOCKOUT:
+ case FingerprintError.ERROR_VENDOR:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Checks if the given acquired code corresponds to a known fingerprint error.
+ *
+ * @param acquiredCode The acquired code to be checked.
+ * @return Whether the acquired code corresponds to a known error.
+ */
+ public static boolean isKnownAcquiredCode(int acquiredCode) {
+ switch (acquiredCode) {
+ case FINGERPRINT_ACQUIRED_GOOD:
+ case FINGERPRINT_ACQUIRED_PARTIAL:
+ case FINGERPRINT_ACQUIRED_INSUFFICIENT:
+ case FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
+ case FINGERPRINT_ACQUIRED_TOO_SLOW:
+ case FINGERPRINT_ACQUIRED_TOO_FAST:
+ case FINGERPRINT_ACQUIRED_VENDOR:
+ case FINGERPRINT_ACQUIRED_START:
+ return true;
+
+ default:
+ return false;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 303c080..f672ae5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -78,7 +78,7 @@
void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- boolean shouldLogMetrics);
+ @FingerprintManager.EnrollReason int enrollReason);
void cancelEnrollment(int sensorId, @NonNull IBinder token);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
index d092e86..37f8e8c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
@@ -65,6 +65,17 @@
}
}
+ public static int getReasonFromEnrollReason(@FingerprintManager.EnrollReason int reason) {
+ switch (reason) {
+ case FingerprintManager.ENROLL_FIND_SENSOR:
+ return IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR;
+ case FingerprintManager.ENROLL_ENROLL:
+ return IUdfpsOverlayController.REASON_ENROLL_ENROLLING;
+ default:
+ return IUdfpsOverlayController.REASON_UNKNOWN;
+ }
+ }
+
public static void showUdfpsOverlay(int sensorId, int reason,
@Nullable IUdfpsOverlayController udfpsOverlayController) {
if (udfpsOverlayController == null) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index c2a30be..ea9c709 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.hardware.biometrics.ITestSession;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Binder;
import android.util.Slog;
@@ -131,7 +132,7 @@
Utils.checkPermission(mContext, TEST_BIOMETRIC);
mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
- mContext.getOpPackageName(), true /* shouldLogMetrics */);
+ mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL);
}
@Override
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 9611192..bcd1b8b 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 @@
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/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 08cc464..ae64c77 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -25,6 +25,7 @@
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
import android.os.RemoteException;
@@ -43,6 +44,7 @@
private static final String TAG = "FingerprintEnrollClient";
@Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+ private final @FingerprintManager.EnrollReason int mEnrollReason;
@Nullable private ICancellationSignal mCancellationSignal;
private final int mMaxTemplatesPerUser;
@@ -52,13 +54,17 @@
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Fingerprint> utils, int sensorId,
@Nullable IUdfpsOverlayController udfpsOvelayController, int maxTemplatesPerUser,
- boolean shouldLogMetrics) {
+ @FingerprintManager.EnrollReason int enrollReason) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
true /* shouldVibrate */);
mUdfpsOverlayController = udfpsOvelayController;
mMaxTemplatesPerUser = maxTemplatesPerUser;
- setShouldLog(shouldLogMetrics);
+
+ mEnrollReason = enrollReason;
+ if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
+ setShouldLog(false);
+ }
}
@Override
@@ -72,6 +78,7 @@
}
}
+
@Override
public void onAcquired(int acquiredInfo, int vendorCode) {
super.onAcquired(acquiredInfo, vendorCode);
@@ -112,7 +119,8 @@
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(getSensorId(), IUdfpsOverlayController.REASON_ENROLL,
+ UdfpsHelper.showUdfpsOverlay(getSensorId(),
+ UdfpsHelper.getReasonFromEnrollReason(mEnrollReason),
mUdfpsOverlayController);
try {
mCancellationSignal = getFreshDaemon().enroll(mSequentialId,
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index f845024..0bd2f24 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -28,6 +28,7 @@
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.biometrics.fingerprint.SensorProps;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -96,7 +97,8 @@
Slog.e(getTag(), "Task stack changed for client: " + client);
continue;
}
- if (Utils.isKeyguard(mContext, client.getOwnerString())) {
+ if (Utils.isKeyguard(mContext, client.getOwnerString())
+ || Utils.isSystem(mContext, client.getOwnerString())) {
continue; // Keyguard is always allowed
}
@@ -365,7 +367,7 @@
@Override
public void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken,
int userId, @NonNull IFingerprintServiceReceiver receiver,
- @NonNull String opPackageName, boolean shouldLogMetrics) {
+ @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason) {
mHandler.post(() -> {
final IFingerprint daemon = getHalInstance();
if (daemon == null) {
@@ -387,7 +389,7 @@
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
- mUdfpsOverlayController, maxTemplatesPerUser, shouldLogMetrics);
+ mUdfpsOverlayController, maxTemplatesPerUser, enrollReason);
scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
@@ -695,8 +697,7 @@
final Sensor sensor = mSensors.valueAt(i);
final int sensorId = mSensors.keyAt(i);
PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount();
- sensor.getScheduler().recordCrashState();
- sensor.getScheduler().reset();
+ sensor.onBinderDied();
}
});
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
index f0e7e1c..7e4ee9e7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java
@@ -31,7 +31,6 @@
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.Handler;
-import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserManager;
import android.util.Slog;
@@ -65,7 +64,7 @@
* {@link android.hardware.biometrics.fingerprint.IFingerprint} HAL.
*/
@SuppressWarnings("deprecation")
-class Sensor implements IBinder.DeathRecipient {
+class Sensor {
private boolean mTestHalEnabled;
@@ -461,7 +460,6 @@
mTag, mScheduler, sensorId, userId, callback);
final ISession newSession = daemon.createSession(sensorId, userId, resultController);
- newSession.asBinder().linkToDeath(this, 0 /* flags */);
mCurrentSession = new Session(mTag, newSession, userId, resultController);
}
@@ -503,24 +501,21 @@
proto.end(sensorToken);
}
- @Override
- public void binderDied() {
- Slog.e(mTag, "Binder died");
- mHandler.post(() -> {
- final BaseClientMonitor client = mScheduler.getCurrentClient();
- if (client instanceof Interruptable) {
- Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
- final Interruptable interruptable = (Interruptable) client;
- interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
- 0 /* vendorCode */);
+ public void onBinderDied() {
+ final BaseClientMonitor client = mScheduler.getCurrentClient();
+ if (client instanceof Interruptable) {
+ Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+ final Interruptable interruptable = (Interruptable) client;
+ interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
- mScheduler.recordCrashState();
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+ BiometricsProtoEnums.MODALITY_FINGERPRINT,
+ BiometricsProtoEnums.ISSUE_HAL_DEATH);
+ }
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
- BiometricsProtoEnums.MODALITY_FINGERPRINT,
- BiometricsProtoEnums.ISSUE_HAL_DEATH);
- mCurrentSession = null;
- }
- });
+ mScheduler.recordCrashState();
+ mScheduler.reset();
+ mCurrentSession = null;
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
index 6893e72..312ee0a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.hardware.biometrics.ITestSession;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.Binder;
import android.util.Slog;
@@ -132,7 +133,7 @@
Utils.checkPermission(mContext, TEST_BIOMETRIC);
mFingerprint21.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
- mContext.getOpPackageName(), true/* shouldLogMetrics */);
+ mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 1135126..7a74c6a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -33,6 +33,7 @@
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -49,6 +50,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.biometrics.SensorServiceStateProto;
import com.android.server.biometrics.SensorStateProto;
@@ -113,7 +115,7 @@
@NonNull private final HalResultController mHalResultController;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
private int mCurrentUserId = UserHandle.USER_NULL;
- private boolean mIsUdfps = false;
+ private final boolean mIsUdfps;
private final int mSensorId;
private final class BiometricTaskStackListener extends TaskStackListener {
@@ -125,7 +127,8 @@
Slog.e(TAG, "Task stack changed for client: " + client);
return;
}
- if (Utils.isKeyguard(mContext, client.getOwnerString())) {
+ if (Utils.isKeyguard(mContext, client.getOwnerString())
+ || Utils.isSystem(mContext, client.getOwnerString())) {
return; // Keyguard is always allowed
}
@@ -335,23 +338,14 @@
Slog.e(TAG, "Unable to register user switch observer");
}
- final IBiometricsFingerprint daemon = getDaemon();
- mIsUdfps = false;
- android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
- android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
- daemon);
- if (extension != null) {
- try {
- mIsUdfps = extension.isUdfps(sensorId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception while quering udfps", e);
- mIsUdfps = false;
- }
- }
+ // TODO(b/179175438): Remove this code block after transition to AIDL.
+ // The existence of config_udfps_sensor_props indicates that the sensor is UDFPS.
+ mIsUdfps = !ArrayUtils.isEmpty(
+ mContext.getResources().getIntArray(R.array.config_udfps_sensor_props));
final @FingerprintSensorProperties.SensorType int sensorType =
mIsUdfps ? FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
- : FingerprintSensorProperties.TYPE_REAR;
+ : FingerprintSensorProperties.TYPE_REAR;
// resetLockout is controlled by the framework, so hardwareAuthToken is not required
final boolean resetLockoutRequiresHardwareAuthToken = false;
final int maxEnrollmentsPerUser = mContext.getResources()
@@ -415,7 +409,7 @@
Slog.d(TAG, "Daemon was null, reconnecting, current operation: "
+ mScheduler.getCurrentClient());
try {
- mDaemon = IBiometricsFingerprint.getService(true /* retry */);
+ mDaemon = IBiometricsFingerprint.getService();
} catch (java.util.NoSuchElementException e) {
// Service doesn't exist or cannot be opened.
Slog.w(TAG, "NoSuchElementException", e);
@@ -554,7 +548,7 @@
public void scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
- boolean shouldLogMetrics) {
+ @FingerprintManager.EnrollReason int enrollReason) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
@@ -562,7 +556,7 @@
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
hardwareAuthToken, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController,
- shouldLogMetrics);
+ enrollReason);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
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 7989dca..8acb284 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 @@
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/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index d927aa7..33db64c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -24,6 +24,7 @@
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.IBinder;
import android.os.RemoteException;
@@ -46,6 +47,7 @@
private static final String TAG = "FingerprintEnrollClient";
@Nullable private final IUdfpsOverlayController mUdfpsOverlayController;
+ private final @FingerprintManager.EnrollReason int mEnrollReason;
FingerprintEnrollClient(@NonNull Context context,
@NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token,
@@ -53,12 +55,16 @@
@NonNull byte[] hardwareAuthToken, @NonNull String owner,
@NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId,
@Nullable IUdfpsOverlayController udfpsOverlayController,
- boolean shouldLogMetrics) {
+ @FingerprintManager.EnrollReason int enrollReason) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId,
true /* shouldVibrate */);
mUdfpsOverlayController = udfpsOverlayController;
- setShouldLog(shouldLogMetrics);
+
+ mEnrollReason = enrollReason;
+ if (enrollReason == FingerprintManager.ENROLL_FIND_SENSOR) {
+ setShouldLog(false);
+ }
}
@Override
@@ -76,7 +82,8 @@
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(getSensorId(), IUdfpsOverlayController.REASON_ENROLL,
+ UdfpsHelper.showUdfpsOverlay(getSensorId(),
+ UdfpsHelper.getReasonFromEnrollReason(mEnrollReason),
mUdfpsOverlayController);
try {
// GroupId was never used. In fact, groupId is always the same as userId.
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 6b2a1c9..51ba5f7 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -211,9 +211,9 @@
}
@Override
- public void clearOverrideForTest(long changeId, String packageName) {
+ public boolean clearOverrideForTest(long changeId, String packageName) {
checkCompatChangeOverridePermission();
- mCompatConfig.removeOverride(changeId, packageName);
+ return mCompatConfig.removeOverride(changeId, packageName);
}
@Override
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index c70bb08..43d9ade 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -32,6 +32,7 @@
import android.content.Context;
import android.content.Intent;
import android.net.IDnsResolver;
+import android.net.InetAddresses;
import android.net.LinkProperties;
import android.net.Network;
import android.net.ResolverOptionsParcel;
@@ -190,7 +191,7 @@
for (String ipAddress : ipAddresses) {
try {
latestDnses.add(new Pair(hostname,
- InetAddress.parseNumericAddress(ipAddress)));
+ InetAddresses.parseNumericAddress(ipAddress)));
} catch (IllegalArgumentException e) {}
}
// Remove <hostname, ipAddress> pairs that should not be tracked.
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 952193b..46c49e7 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -34,9 +34,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.net.module.util.NetworkStackConstants;
import com.android.server.net.BaseNetworkObserver;
-import java.net.Inet4Address;
import java.net.Inet6Address;
import java.util.Objects;
@@ -433,7 +433,7 @@
// clat IPv4 address itself (for those apps, it doesn't matter what
// the IP of the gateway is, only that there is one).
RouteInfo ipv4Default = new RouteInfo(
- new LinkAddress(Inet4Address.ANY, 0),
+ new LinkAddress(NetworkStackConstants.IPV4_ADDR_ANY, 0),
clatAddress.getAddress(), mIface);
stacked.addRoute(ipv4Default);
stacked.addLinkAddress(clatAddress);
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index a9a705f..c05e253 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -122,6 +122,13 @@
//
// When ConnectivityService disconnects a network:
// -----------------------------------------------
+// If a network is just connected, ConnectivityService will think it will be used soon, but might
+// not be used. Thus, a 5s timer will be held to prevent the network being torn down immediately.
+// This "nascent" state is implemented by the "lingering" logic below without relating to any
+// request, and is used in some cases where network requests race with network establishment. The
+// nascent state ends when the 5-second timer fires, or as soon as the network satisfies a
+// request, whichever is earlier. In this state, the network is considered in the background.
+//
// If a network has no chance of satisfying any requests (even if it were to become validated
// and enter state #5), ConnectivityService will disconnect the NetworkAgent's AsyncChannel.
//
@@ -271,7 +278,8 @@
// All inactivity timers for this network, sorted by expiry time. A timer is added whenever
// a request is moved to a network with a better score, regardless of whether the network is or
- // was lingering or not.
+ // was lingering or not. An inactivity timer is also added when a network connects
+ // without immediately satisfying any requests.
// TODO: determine if we can replace this with a smaller or unsorted data structure. (e.g.,
// SparseLongArray) combined with the timestamp of when the last timer is scheduled to fire.
private final SortedSet<InactivityTimer> mInactivityTimers = new TreeSet<>();
@@ -709,8 +717,9 @@
mNumBackgroundNetworkRequests += delta;
break;
- case TRACK_DEFAULT:
case LISTEN:
+ case TRACK_DEFAULT:
+ case TRACK_SYSTEM_DEFAULT:
break;
case NONE:
@@ -896,7 +905,12 @@
/**
* Sets the specified requestId to linger on this network for the specified time. Called by
- * ConnectivityService when the request is moved to another network with a higher score.
+ * ConnectivityService when the request is moved to another network with a higher score, or
+ * when a network is newly created.
+ *
+ * @param requestId The requestId of the request that no longer need to be served by this
+ * network. Or {@link NetworkRequest.REQUEST_ID_NONE} if this is the
+ * {@code LingerTimer} for a newly created network.
*/
public void lingerRequest(int requestId, long now, long duration) {
if (mInactivityTimerForRequest.get(requestId) != null) {
@@ -969,10 +983,23 @@
mInactive = false;
}
- public boolean isLingering() {
+ public boolean isInactive() {
return mInactive;
}
+ public boolean isLingering() {
+ return mInactive && !isNascent();
+ }
+
+ /**
+ * Return whether the network is just connected and about to be torn down because of not
+ * satisfying any request.
+ */
+ public boolean isNascent() {
+ return mInactive && mInactivityTimers.size() == 1
+ && mInactivityTimers.first().requestId == NetworkRequest.REQUEST_ID_NONE;
+ }
+
public void clearInactivityState() {
if (mInactivityMessage != null) {
mInactivityMessage.cancel();
@@ -1022,7 +1049,7 @@
+ "network{" + network + "} handle{" + network.getNetworkHandle() + "} ni{"
+ networkInfo.toShortString() + "} "
+ " Score{" + getCurrentScore() + "} "
- + (isLingering() ? " lingering" : "")
+ + (isNascent() ? " nascent" : (isLingering() ? " lingering" : ""))
+ (everValidated ? " everValidated" : "")
+ (lastValidated ? " lastValidated" : "")
+ (partialConnectivity ? " partialConnectivity" : "")
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index d507b5f..8d21f6f 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -265,7 +265,10 @@
for (Entry<Integer, Boolean> app : apps.entrySet()) {
List<Integer> list = app.getValue() ? system : network;
for (int user : users) {
- list.add(UserHandle.getUid(user, app.getKey()));
+ final UserHandle handle = UserHandle.of(user);
+ if (handle == null) continue;
+
+ list.add(UserHandle.getUid(handle, app.getKey()));
}
}
try {
@@ -550,7 +553,10 @@
for (UidRange range : ranges) {
for (int userId = range.getStartUser(); userId <= range.getEndUser(); userId++) {
for (int appId : appIds) {
- final int uid = UserHandle.getUid(userId, appId);
+ final UserHandle handle = UserHandle.of(userId);
+ if (handle == null) continue;
+
+ final int uid = UserHandle.getUid(handle, appId);
if (range.contains(uid)) {
result.add(uid);
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 5956fe1..33c19b1 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -51,6 +51,7 @@
import android.net.INetd;
import android.net.INetworkManagementEventObserver;
import android.net.Ikev2VpnProfile;
+import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.IpSecManager;
import android.net.IpSecManager.IpSecTunnelInterface;
@@ -73,6 +74,7 @@
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;
@@ -111,6 +113,7 @@
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
+import com.android.net.module.util.NetworkStackConstants;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.net.BaseNetworkObserver;
@@ -203,6 +206,7 @@
protected final NetworkCapabilities mNetworkCapabilities;
private final SystemServices mSystemServices;
private final Ikev2SessionCreator mIkev2SessionCreator;
+ private final UserManager mUserManager;
/**
* Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
@@ -331,7 +335,7 @@
public InetAddress resolve(final String endpoint)
throws ExecutionException, InterruptedException {
try {
- return InetAddress.parseNumericAddress(endpoint);
+ return InetAddresses.parseNumericAddress(endpoint);
} catch (IllegalArgumentException e) {
// Endpoint is not numeric : fall through and resolve
}
@@ -409,6 +413,7 @@
mLooper = looper;
mSystemServices = systemServices;
mIkev2SessionCreator = ikev2SessionCreator;
+ mUserManager = mContext.getSystemService(UserManager.class);
mPackage = VpnConfig.LEGACY_VPN;
mOwnerUID = getAppUid(mPackage, mUserId);
@@ -431,6 +436,7 @@
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);
}
@@ -925,6 +931,7 @@
jniReset(mInterface);
mInterface = null;
mNetworkCapabilities.setUids(null);
+ mNetworkCapabilities.setTransportInfo(null);
}
// Revoke the connection or stop the VpnRunner.
@@ -995,6 +1002,8 @@
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;
@@ -1025,6 +1034,8 @@
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;
}
@@ -1123,7 +1134,7 @@
if (mConfig.dnsServers != null) {
for (String dnsServer : mConfig.dnsServers) {
- InetAddress address = InetAddress.parseNumericAddress(dnsServer);
+ InetAddress address = InetAddresses.parseNumericAddress(dnsServer);
lp.addDnsServer(address);
allowIPv4 |= address instanceof Inet4Address;
allowIPv6 |= address instanceof Inet6Address;
@@ -1133,10 +1144,12 @@
lp.setHttpProxy(mConfig.proxyInfo);
if (!allowIPv4) {
- lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
+ lp.addRoute(new RouteInfo(new IpPrefix(
+ NetworkStackConstants.IPV4_ADDR_ANY, 0), RTN_UNREACHABLE));
}
if (!allowIPv6) {
- lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
+ lp.addRoute(new RouteInfo(new IpPrefix(
+ NetworkStackConstants.IPV6_ADDR_ANY, 0), RTN_UNREACHABLE));
}
// Concatenate search domains into a string.
@@ -1205,6 +1218,8 @@
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) {
@@ -1435,7 +1450,7 @@
final long token = Binder.clearCallingIdentity();
List<UserInfo> users;
try {
- users = UserManager.get(mContext).getAliveUsers();
+ users = mUserManager.getAliveUsers();
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1519,7 +1534,7 @@
*/
public void onUserAdded(int userId) {
// If the user is restricted tie them to the parent user's VPN
- UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+ UserInfo user = mUserManager.getUserInfo(userId);
if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
synchronized(Vpn.this) {
final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
@@ -1547,7 +1562,7 @@
*/
public void onUserRemoved(int userId) {
// clean up if restricted
- UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+ UserInfo user = mUserManager.getUserInfo(userId);
if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
synchronized(Vpn.this) {
final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
@@ -1730,6 +1745,7 @@
private void cleanupVpnStateLocked() {
mStatusIntent = null;
mNetworkCapabilities.setUids(null);
+ mNetworkCapabilities.setTransportInfo(null);
mConfig = null;
mInterface = null;
@@ -1840,22 +1856,18 @@
}
/**
- * 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) {
@@ -1972,8 +1984,7 @@
private void enforceNotRestrictedUser() {
Binder.withCleanCallingIdentity(() -> {
- final UserManager mgr = UserManager.get(mContext);
- final UserInfo user = mgr.getUserInfo(mUserId);
+ final UserInfo user = mUserManager.getUserInfo(mUserId);
if (user.isRestricted()) {
throw new SecurityException("Restricted users cannot configure VPNs");
@@ -2008,9 +2019,8 @@
*/
public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
@Nullable Network underlying, @NonNull LinkProperties egress) {
- UserManager mgr = UserManager.get(mContext);
- UserInfo user = mgr.getUserInfo(mUserId);
- if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
+ UserInfo user = mUserManager.getUserInfo(mUserId);
+ if (user.isRestricted() || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
new UserHandle(mUserId))) {
throw new SecurityException("Restricted users cannot establish VPNs");
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceState.java b/services/core/java/com/android/server/devicestate/DeviceState.java
index 802472f..e496d77 100644
--- a/services/core/java/com/android/server/devicestate/DeviceState.java
+++ b/services/core/java/com/android/server/devicestate/DeviceState.java
@@ -16,8 +16,6 @@
package com.android.server.devicestate;
-import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
-
import android.annotation.IntRange;
import android.annotation.NonNull;
@@ -37,16 +35,16 @@
*/
public final class DeviceState {
/** Unique identifier for the device state. */
- @IntRange(from = INVALID_DEVICE_STATE)
+ @IntRange(from = 0)
private final int mIdentifier;
/** String description of the device state. */
@NonNull
private final String mName;
- public DeviceState(@IntRange(from = INVALID_DEVICE_STATE) int identifier,
+ public DeviceState(@IntRange(from = 0) int identifier,
@NonNull String name) {
- if (identifier != INVALID_DEVICE_STATE && identifier < 0) {
+ if (identifier < 0) {
throw new IllegalArgumentException("Identifier must be greater than or equal to zero.");
}
mIdentifier = identifier;
@@ -54,7 +52,7 @@
}
/** Returns the unique identifier for the device state. */
- @IntRange(from = INVALID_DEVICE_STATE)
+ @IntRange(from = 0)
public int getIdentifier() {
return mIdentifier;
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 375ec3a..984a176 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -17,13 +17,13 @@
package com.android.server.devicestate;
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.pm.PackageManager;
+import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.IDeviceStateManager;
import android.hardware.devicestate.IDeviceStateManagerCallback;
import android.os.Binder;
@@ -31,6 +31,8 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
@@ -62,8 +64,12 @@
* the {@link DeviceStateProvider} to modify the current device state and communicating with the
* {@link DeviceStatePolicy policy} to ensure the system is configured to match the requested state.
* </p>
+ * The service also provides the {@link DeviceStateManager} API allowing clients to listen for
+ * changes in device state and submit requests to override the device state provided by the
+ * {@link DeviceStateProvider}.
*
* @see DeviceStatePolicy
+ * @see DeviceStateManager
*/
public final class DeviceStateManagerService extends SystemService {
private static final String TAG = "DeviceStateManagerService";
@@ -79,11 +85,11 @@
@GuardedBy("mLock")
private SparseArray<DeviceState> mDeviceStates = new SparseArray<>();
- // The current committed device state. The default of INVALID_DEVICE_STATE will be replaced by
+ // The current committed device state. The default of UNSET will be replaced by
// the current state after the initial callback from the DeviceStateProvider.
@GuardedBy("mLock")
@NonNull
- private DeviceState mCommittedState = new DeviceState(INVALID_DEVICE_STATE, "INVALID");
+ private DeviceState mCommittedState = new DeviceState(0, "UNSET");
// The device state that is currently awaiting callback from the policy to be committed.
@GuardedBy("mLock")
@NonNull
@@ -91,19 +97,23 @@
// Whether or not the policy is currently waiting to be notified of the current pending state.
@GuardedBy("mLock")
private boolean mIsPolicyWaitingForState = false;
- // The device state that is currently requested and is next to be configured and committed.
- // Can be overwritten by an override state value if requested.
- @GuardedBy("mLock")
- @NonNull
- private Optional<DeviceState> mRequestedState = Optional.empty();
- // The most recently requested override state, or empty if no override is requested.
- @GuardedBy("mLock")
- @NonNull
- private Optional<DeviceState> mRequestedOverrideState = Optional.empty();
- // List of registered callbacks indexed by process id.
+ // The device state that is set by the device state provider.
@GuardedBy("mLock")
- private final SparseArray<CallbackRecord> mCallbacks = new SparseArray<>();
+ @NonNull
+ private Optional<DeviceState> mBaseState = Optional.empty();
+
+ // List of processes registered to receive notifications about changes to device state and
+ // request status indexed by process id.
+ @GuardedBy("mLock")
+ private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>();
+ // List of override requests with the highest precedence request at the end.
+ @GuardedBy("mLock")
+ private final ArrayList<OverrideRequestRecord> mRequestRecords = new ArrayList<>();
+ // Set of override requests that are pending a call to notifyStatusIfNeeded() to be notified
+ // of a change in status.
+ @GuardedBy("mLock")
+ private final ArraySet<OverrideRequestRecord> mRequestsPendingStatusChange = new ArraySet<>();
public DeviceStateManagerService(@NonNull Context context) {
this(context, new DeviceStatePolicyImpl(context));
@@ -148,55 +158,32 @@
}
/**
- * Returns the requested state. The service will configure the device to match the requested
- * state when possible.
+ * Returns the base state. The service will configure the device to match the base state when
+ * there is no active request to override the base state.
+ *
+ * @see #getOverrideState()
*/
@NonNull
- Optional<DeviceState> getRequestedState() {
+ Optional<DeviceState> getBaseState() {
synchronized (mLock) {
- return mRequestedState;
+ return mBaseState;
}
}
/**
- * Overrides the current device state with the provided state.
- *
- * @return {@code true} if the override state is valid and supported, {@code false} otherwise.
- */
- boolean setOverrideState(int overrideState) {
- if (getContext().checkCallingOrSelfPermission(CONTROL_DEVICE_STATE)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Must hold permission " + CONTROL_DEVICE_STATE);
- }
-
- synchronized (mLock) {
- if (overrideState != INVALID_DEVICE_STATE && !isSupportedStateLocked(overrideState)) {
- return false;
- }
-
- mRequestedOverrideState = getStateLocked(overrideState);
- updatePendingStateLocked();
- }
-
- notifyPolicyIfNeeded();
- return true;
- }
-
- /**
- * Clears an override state set with {@link #setOverrideState(int)}.
- */
- void clearOverrideState() {
- setOverrideState(INVALID_DEVICE_STATE);
- }
-
- /**
- * Returns the current requested override state, or {@link Optional#empty()} if no override
- * state is requested.
+ * Returns the current override state, or {@link Optional#empty()} if no override state is
+ * requested. If an override states is present, the returned state will take precedence over
+ * the base state returned from {@link #getBaseState()}.
*/
@NonNull
Optional<DeviceState> getOverrideState() {
synchronized (mLock) {
- return mRequestedOverrideState;
+ if (mRequestRecords.isEmpty()) {
+ return Optional.empty();
+ }
+
+ OverrideRequestRecord topRequest = mRequestRecords.get(mRequestRecords.size() - 1);
+ return Optional.of(topRequest.mRequestedState);
}
}
@@ -211,6 +198,17 @@
}
}
+ /** Returns the list of currently supported device state identifiers. */
+ private int[] getSupportedStateIdentifiers() {
+ synchronized (mLock) {
+ int[] supportedStates = new int[mDeviceStates.size()];
+ for (int i = 0; i < supportedStates.length; i++) {
+ supportedStates[i] = mDeviceStates.valueAt(i).getIdentifier();
+ }
+ return supportedStates;
+ }
+ }
+
@VisibleForTesting
IDeviceStateManager getBinderService() {
return mBinderService;
@@ -224,22 +222,26 @@
mDeviceStates.put(state.getIdentifier(), state);
}
- if (mRequestedState.isPresent()
- && !isSupportedStateLocked(mRequestedState.get().getIdentifier())) {
- // The current requested state is no longer valid. We'll clear it here, though
+ if (mBaseState.isPresent()
+ && !isSupportedStateLocked(mBaseState.get().getIdentifier())) {
+ // The current base state is no longer valid. We'll clear it here, though
// we won't actually update the current state until a callback comes from the
// provider with the most recent state.
- mRequestedState = Optional.empty();
+ mBaseState = Optional.empty();
}
- if (mRequestedOverrideState.isPresent()
- && !isSupportedStateLocked(mRequestedOverrideState.get().getIdentifier())) {
- // The current override state is no longer valid. We'll clear it here and update
- // the committed state if necessary.
- mRequestedOverrideState = Optional.empty();
+
+ final int requestSize = mRequestRecords.size();
+ for (int i = 0; i < requestSize; i++) {
+ OverrideRequestRecord request = mRequestRecords.get(i);
+ if (!isSupportedStateLocked(request.mRequestedState.getIdentifier())) {
+ request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
+ }
}
+
updatePendingStateLocked();
}
+ notifyRequestsOfStatusChangeIfNeeded();
notifyPolicyIfNeeded();
}
@@ -261,20 +263,37 @@
}
/**
- * Requests that the system enter the provided {@code state}. The request may not be honored
- * under certain conditions, for example if the provided state is not supported.
+ * Requests to set the base state. The request may not be honored under certain conditions, for
+ * example if the provided state is not supported.
*
* @see #isSupportedStateLocked(int)
*/
- private void requestState(int identifier) {
+ private void setBaseState(int identifier) {
synchronized (mLock) {
- final Optional<DeviceState> requestedState = getStateLocked(identifier);
- if (requestedState.isPresent()) {
- mRequestedState = requestedState;
+ if (mBaseState.isPresent() && mBaseState.get().getIdentifier() == identifier) {
+ // Base state hasn't changed. Nothing to do.
+ return;
}
+
+ final Optional<DeviceState> baseState = getStateLocked(identifier);
+ if (!baseState.isPresent()) {
+ throw new IllegalArgumentException("Base state is not supported");
+ }
+
+ mBaseState = baseState;
+
+ final int requestSize = mRequestRecords.size();
+ for (int i = 0; i < requestSize; i++) {
+ OverrideRequestRecord request = mRequestRecords.get(i);
+ if ((request.mFlags & FLAG_CANCEL_WHEN_BASE_CHANGES) > 0) {
+ request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
+ }
+ }
+
updatePendingStateLocked();
}
+ notifyRequestsOfStatusChangeIfNeeded();
notifyPolicyIfNeeded();
}
@@ -290,10 +309,10 @@
}
final DeviceState stateToConfigure;
- if (mRequestedOverrideState.isPresent()) {
- stateToConfigure = mRequestedOverrideState.get();
+ if (!mRequestRecords.isEmpty()) {
+ stateToConfigure = mRequestRecords.get(mRequestRecords.size() - 1).mRequestedState;
} else {
- stateToConfigure = mRequestedState.orElse(null);
+ stateToConfigure = mBaseState.orElse(null);
}
if (stateToConfigure == null) {
@@ -360,6 +379,13 @@
}
mCommittedState = mPendingState.get();
newState = mCommittedState.getIdentifier();
+
+ if (!mRequestRecords.isEmpty()) {
+ final OverrideRequestRecord topRequest =
+ mRequestRecords.get(mRequestRecords.size() - 1);
+ topRequest.setStatusLocked(OverrideRequestRecord.STATUS_ACTIVE);
+ }
+
mPendingState = Optional.empty();
updatePendingStateLocked();
}
@@ -367,6 +393,9 @@
// Notify callbacks of a change.
notifyDeviceStateChanged(newState);
+ // Notify the top request that it's active.
+ notifyRequestsOfStatusChangeIfNeeded();
+
// Try to configure the next state if needed.
notifyPolicyIfNeeded();
}
@@ -377,43 +406,69 @@
"Attempting to notify callbacks with service lock held.");
}
- // Grab the lock and copy the callbacks.
- ArrayList<CallbackRecord> callbacks;
+ // Grab the lock and copy the process records.
+ ArrayList<ProcessRecord> registeredProcesses;
synchronized (mLock) {
- if (mCallbacks.size() == 0) {
+ if (mProcessRecords.size() == 0) {
return;
}
- callbacks = new ArrayList<>();
- for (int i = 0; i < mCallbacks.size(); i++) {
- callbacks.add(mCallbacks.valueAt(i));
+ registeredProcesses = new ArrayList<>();
+ for (int i = 0; i < mProcessRecords.size(); i++) {
+ registeredProcesses.add(mProcessRecords.valueAt(i));
}
}
// After releasing the lock, send the notifications out.
- for (int i = 0; i < callbacks.size(); i++) {
- callbacks.get(i).notifyDeviceStateAsync(deviceState);
+ for (int i = 0; i < registeredProcesses.size(); i++) {
+ registeredProcesses.get(i).notifyDeviceStateAsync(deviceState);
}
}
- private void registerCallbackInternal(IDeviceStateManagerCallback callback, int callingPid) {
+ /**
+ * Notifies all dirty requests (requests that have a change in status, but have not yet been
+ * notified) that their status has changed.
+ */
+ private void notifyRequestsOfStatusChangeIfNeeded() {
+ if (Thread.holdsLock(mLock)) {
+ throw new IllegalStateException(
+ "Attempting to notify requests with service lock held.");
+ }
+
+ ArraySet<OverrideRequestRecord> dirtyRequests;
+ synchronized (mLock) {
+ if (mRequestsPendingStatusChange.isEmpty()) {
+ return;
+ }
+
+ dirtyRequests = new ArraySet<>(mRequestsPendingStatusChange);
+ mRequestsPendingStatusChange.clear();
+ }
+
+ // After releasing the lock, send the notifications out.
+ for (int i = 0; i < dirtyRequests.size(); i++) {
+ dirtyRequests.valueAt(i).notifyStatusIfNeeded();
+ }
+ }
+
+ private void registerProcess(int pid, IDeviceStateManagerCallback callback) {
int currentState;
- CallbackRecord record;
+ ProcessRecord record;
// Grab the lock to register the callback and get the current state.
synchronized (mLock) {
- if (mCallbacks.contains(callingPid)) {
+ if (mProcessRecords.contains(pid)) {
throw new SecurityException("The calling process has already registered an"
+ " IDeviceStateManagerCallback.");
}
- record = new CallbackRecord(callback, callingPid);
+ record = new ProcessRecord(callback, pid);
try {
callback.asBinder().linkToDeath(record, 0);
} catch (RemoteException ex) {
throw new RuntimeException(ex);
}
- mCallbacks.put(callingPid, record);
+ mProcessRecords.put(pid, record);
currentState = mCommittedState.getIdentifier();
}
@@ -421,10 +476,86 @@
record.notifyDeviceStateAsync(currentState);
}
- private void unregisterCallbackInternal(CallbackRecord record) {
+ private void handleProcessDied(ProcessRecord processRecord) {
synchronized (mLock) {
- mCallbacks.remove(record.mPid);
+ // Cancel all requests from this process.
+ final int requestCount = processRecord.mRequestRecords.size();
+ for (int i = 0; i < requestCount; i++) {
+ final OverrideRequestRecord request = processRecord.mRequestRecords.valueAt(i);
+ // Cancel the request but don't mark it as dirty since there's no need to send
+ // notifications if the process has died.
+ request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED,
+ false /* markDirty */);
+ }
+
+ mProcessRecords.remove(processRecord.mPid);
+
+ updatePendingStateLocked();
}
+
+ notifyPolicyIfNeeded();
+ }
+
+ private void requestStateInternal(int state, int flags, int callingPid,
+ @NonNull IBinder token) {
+ synchronized (mLock) {
+ final ProcessRecord processRecord = mProcessRecords.get(callingPid);
+ if (processRecord == null) {
+ throw new IllegalStateException("Process " + callingPid
+ + " has no registered callback.");
+ }
+
+ if (processRecord.mRequestRecords.get(token) != null) {
+ throw new IllegalStateException("Request has already been made for the supplied"
+ + " token: " + token);
+ }
+
+ final Optional<DeviceState> deviceState = getStateLocked(state);
+ if (!deviceState.isPresent()) {
+ throw new IllegalArgumentException("Requested state: " + state
+ + " is not supported.");
+ }
+
+ OverrideRequestRecord topRecord = mRequestRecords.isEmpty()
+ ? null : mRequestRecords.get(mRequestRecords.size() - 1);
+ if (topRecord != null) {
+ topRecord.setStatusLocked(OverrideRequestRecord.STATUS_SUSPENDED);
+ }
+
+ final OverrideRequestRecord request =
+ new OverrideRequestRecord(processRecord, token, deviceState.get(), flags);
+ mRequestRecords.add(request);
+ processRecord.mRequestRecords.put(request.mToken, request);
+ // We don't set the status of the new request to ACTIVE here as it will be set in
+ // commitPendingState().
+
+ updatePendingStateLocked();
+ }
+
+ notifyRequestsOfStatusChangeIfNeeded();
+ notifyPolicyIfNeeded();
+ }
+
+ private void cancelRequestInternal(int callingPid, @NonNull IBinder token) {
+ synchronized (mLock) {
+ final ProcessRecord processRecord = mProcessRecords.get(callingPid);
+ if (processRecord == null) {
+ throw new IllegalStateException("Process " + callingPid
+ + " has no registered callback.");
+ }
+
+ OverrideRequestRecord request = processRecord.mRequestRecords.get(token);
+ if (request == null) {
+ throw new IllegalStateException("No known request for the given token");
+ }
+
+ request.setStatusLocked(OverrideRequestRecord.STATUS_CANCELED);
+
+ updatePendingStateLocked();
+ }
+
+ notifyRequestsOfStatusChangeIfNeeded();
+ notifyPolicyIfNeeded();
}
private void dumpInternal(PrintWriter pw) {
@@ -433,15 +564,26 @@
synchronized (mLock) {
pw.println(" mCommittedState=" + mCommittedState);
pw.println(" mPendingState=" + mPendingState);
- pw.println(" mRequestedState=" + mRequestedState);
- pw.println(" mRequestedOverrideState=" + mRequestedOverrideState);
+ pw.println(" mBaseState=" + mBaseState);
+ pw.println(" mOverrideState=" + getOverrideState());
- final int callbackCount = mCallbacks.size();
+ final int processCount = mProcessRecords.size();
pw.println();
- pw.println("Callbacks: size=" + callbackCount);
- for (int i = 0; i < callbackCount; i++) {
- CallbackRecord callback = mCallbacks.valueAt(i);
- pw.println(" " + i + ": mPid=" + callback.mPid);
+ pw.println("Registered processes: size=" + processCount);
+ for (int i = 0; i < processCount; i++) {
+ ProcessRecord processRecord = mProcessRecords.valueAt(i);
+ pw.println(" " + i + ": mPid=" + processRecord.mPid);
+ }
+
+ final int requestCount = mRequestRecords.size();
+ pw.println();
+ pw.println("Override requests: size=" + requestCount);
+ for (int i = 0; i < requestCount; i++) {
+ OverrideRequestRecord requestRecord = mRequestRecords.get(i);
+ pw.println(" " + i + ": mPid=" + requestRecord.mProcessRecord.mPid
+ + ", mRequestedState=" + requestRecord.mRequestedState
+ + ", mFlags=" + requestRecord.mFlags
+ + ", mStatus=" + requestRecord.statusToString(requestRecord.mStatus));
}
}
}
@@ -452,12 +594,6 @@
if (newDeviceStates.length == 0) {
throw new IllegalArgumentException("Supported device states must not be empty");
}
- for (int i = 0; i < newDeviceStates.length; i++) {
- if (newDeviceStates[i].getIdentifier() == INVALID_DEVICE_STATE) {
- throw new IllegalArgumentException(
- "Supported device states includes INVALID_DEVICE_STATE identifier");
- }
- }
updateSupportedStates(newDeviceStates);
}
@@ -467,22 +603,24 @@
throw new IllegalArgumentException("Invalid identifier: " + identifier);
}
- requestState(identifier);
+ setBaseState(identifier);
}
}
- private final class CallbackRecord implements IBinder.DeathRecipient {
+ private final class ProcessRecord implements IBinder.DeathRecipient {
private final IDeviceStateManagerCallback mCallback;
private final int mPid;
- CallbackRecord(IDeviceStateManagerCallback callback, int pid) {
+ private final ArrayMap<IBinder, OverrideRequestRecord> mRequestRecords = new ArrayMap<>();
+
+ ProcessRecord(IDeviceStateManagerCallback callback, int pid) {
mCallback = callback;
mPid = pid;
}
@Override
public void binderDied() {
- unregisterCallbackInternal(this);
+ handleProcessDied(this);
}
public void notifyDeviceStateAsync(int devicestate) {
@@ -493,6 +631,119 @@
ex);
}
}
+
+ public void notifyRequestActiveAsync(OverrideRequestRecord request) {
+ try {
+ mCallback.onRequestActive(request.mToken);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
+ ex);
+ }
+ }
+
+ public void notifyRequestSuspendedAsync(OverrideRequestRecord request) {
+ try {
+ mCallback.onRequestSuspended(request.mToken);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
+ ex);
+ }
+ }
+
+ public void notifyRequestCanceledAsync(OverrideRequestRecord request) {
+ try {
+ mCallback.onRequestCanceled(request.mToken);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to notify process " + mPid + " that request state changed.",
+ ex);
+ }
+ }
+ }
+
+ /** A record describing a request to override the state of the device. */
+ private final class OverrideRequestRecord {
+ public static final int STATUS_UNKNOWN = 0;
+ public static final int STATUS_ACTIVE = 1;
+ public static final int STATUS_SUSPENDED = 2;
+ public static final int STATUS_CANCELED = 3;
+
+ @Nullable
+ public String statusToString(int status) {
+ switch (status) {
+ case STATUS_ACTIVE:
+ return "ACTIVE";
+ case STATUS_SUSPENDED:
+ return "SUSPENDED";
+ case STATUS_CANCELED:
+ return "CANCELED";
+ case STATUS_UNKNOWN:
+ return "UNKNOWN";
+ default:
+ return null;
+ }
+ }
+
+ private final ProcessRecord mProcessRecord;
+ @NonNull
+ private final IBinder mToken;
+ @NonNull
+ private final DeviceState mRequestedState;
+ private final int mFlags;
+
+ private int mStatus = STATUS_UNKNOWN;
+ private int mLastNotifiedStatus = STATUS_UNKNOWN;
+
+ OverrideRequestRecord(@NonNull ProcessRecord processRecord, @NonNull IBinder token,
+ @NonNull DeviceState requestedState, int flags) {
+ mProcessRecord = processRecord;
+ mToken = token;
+ mRequestedState = requestedState;
+ mFlags = flags;
+ }
+
+ public void setStatusLocked(int status) {
+ setStatusLocked(status, true /* markDirty */);
+ }
+
+ public void setStatusLocked(int status, boolean markDirty) {
+ if (mStatus != status) {
+ if (mStatus == STATUS_CANCELED) {
+ throw new IllegalStateException(
+ "Can not alter the status of a request after set to CANCELED.");
+ }
+
+ mStatus = status;
+
+ if (mStatus == STATUS_CANCELED) {
+ mRequestRecords.remove(this);
+ mProcessRecord.mRequestRecords.remove(mToken);
+ }
+
+ if (markDirty) {
+ mRequestsPendingStatusChange.add(this);
+ }
+ }
+ }
+
+ public void notifyStatusIfNeeded() {
+ int stateToReport;
+ synchronized (mLock) {
+ if (mLastNotifiedStatus == mStatus) {
+ return;
+ }
+
+ stateToReport = mStatus;
+ mLastNotifiedStatus = mStatus;
+ }
+
+ if (stateToReport == STATUS_ACTIVE) {
+ mProcessRecord.notifyRequestActiveAsync(this);
+ } else if (stateToReport == STATUS_SUSPENDED) {
+ mProcessRecord.notifyRequestSuspendedAsync(this);
+ } else if (stateToReport == STATUS_CANCELED) {
+ mProcessRecord.notifyRequestCanceledAsync(this);
+ }
+ }
}
/** Implementation of {@link IDeviceStateManager} published as a binder service. */
@@ -506,13 +757,59 @@
final int callingPid = Binder.getCallingPid();
final long token = Binder.clearCallingIdentity();
try {
- registerCallbackInternal(callback, callingPid);
+ registerProcess(callingPid, callback);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
+ public int[] getSupportedDeviceStates() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getSupportedStateIdentifiers();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
+ public void requestState(IBinder token, int state, int flags) {
+ getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
+ "Permission required to request device state.");
+
+ if (token == null) {
+ throw new IllegalArgumentException("Request token must not be null.");
+ }
+
+ final int callingPid = Binder.getCallingPid();
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ requestStateInternal(state, flags, callingPid, token);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override // Binder call
+ public void cancelRequest(IBinder token) {
+ getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
+ "Permission required to clear requested device state.");
+
+ if (token == null) {
+ throw new IllegalArgumentException("Request token must not be null.");
+ }
+
+ final int callingPid = Binder.getCallingPid();
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ cancelRequestInternal(callingPid, token);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override // Binder call
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver result) {
new DeviceStateManagerShellCommand(DeviceStateManagerService.this)
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
index 7914531..6cc55a6 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
@@ -16,6 +16,13 @@
package com.android.server.devicestate;
+import static android.Manifest.permission.CONTROL_DEVICE_STATE;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateRequest;
+import android.os.Binder;
import android.os.ShellCommand;
import java.io.PrintWriter;
@@ -27,10 +34,15 @@
* Use with {@code adb shell cmd device_state ...}.
*/
public class DeviceStateManagerShellCommand extends ShellCommand {
- private final DeviceStateManagerService mInternal;
+ @Nullable
+ private static DeviceStateRequest sLastRequest;
+
+ private final DeviceStateManagerService mService;
+ private final DeviceStateManager mClient;
public DeviceStateManagerShellCommand(DeviceStateManagerService service) {
- mInternal = service;
+ mService = service;
+ mClient = service.getContext().getSystemService(DeviceStateManager.class);
}
@Override
@@ -51,15 +63,15 @@
}
private void printState(PrintWriter pw) {
- DeviceState committedState = mInternal.getCommittedState();
- Optional<DeviceState> requestedState = mInternal.getRequestedState();
- Optional<DeviceState> requestedOverrideState = mInternal.getOverrideState();
+ DeviceState committedState = mService.getCommittedState();
+ Optional<DeviceState> baseState = mService.getBaseState();
+ Optional<DeviceState> overrideState = mService.getOverrideState();
pw.println("Committed state: " + committedState);
- if (requestedOverrideState.isPresent()) {
+ if (overrideState.isPresent()) {
pw.println("----------------------");
- pw.println("Base state: " + requestedState.orElse(null));
- pw.println("Override state: " + requestedOverrideState.get());
+ pw.println("Base state: " + baseState.orElse(null));
+ pw.println("Override state: " + overrideState.get());
}
}
@@ -67,32 +79,51 @@
final String nextArg = getNextArg();
if (nextArg == null) {
printState(pw);
- } else if ("reset".equals(nextArg)) {
- mInternal.clearOverrideState();
- } else {
- int requestedState;
- try {
- requestedState = Integer.parseInt(nextArg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: requested state should be an integer");
- return -1;
- }
-
- boolean success = mInternal.setOverrideState(requestedState);
- if (!success) {
- getErrPrintWriter().println("Error: failed to set override state. Run:");
- getErrPrintWriter().println("");
- getErrPrintWriter().println(" print-states");
- getErrPrintWriter().println("");
- getErrPrintWriter().println("to get the list of currently supported device states");
- return -1;
- }
}
+
+ final Context context = mService.getContext();
+ context.enforceCallingOrSelfPermission(
+ CONTROL_DEVICE_STATE,
+ "Permission required to request device state.");
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ if ("reset".equals(nextArg)) {
+ if (sLastRequest != null) {
+ mClient.cancelRequest(sLastRequest);
+ sLastRequest = null;
+ }
+ } else {
+ int requestedState = Integer.parseInt(nextArg);
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(requestedState).build();
+
+ mClient.requestState(request, null /* executor */, null /* callback */);
+ if (sLastRequest != null) {
+ mClient.cancelRequest(sLastRequest);
+ }
+
+ sLastRequest = request;
+ }
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: requested state should be an integer");
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println("Error: " + e.getMessage());
+ getErrPrintWriter().println("-------------------");
+ getErrPrintWriter().println("Run:");
+ getErrPrintWriter().println("");
+ getErrPrintWriter().println(" print-states");
+ getErrPrintWriter().println("");
+ getErrPrintWriter().println("to get the list of currently supported device states");
+ return -1;
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+
return 0;
}
private int runPrintStates(PrintWriter pw) {
- DeviceState[] states = mInternal.getSupportedStates();
+ DeviceState[] states = mService.getSupportedStates();
pw.print("Supported states: [\n");
for (int i = 0; i < states.length; i++) {
pw.print(" " + states[i] + ",\n");
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e5151d8..01fee56 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -514,8 +514,8 @@
DeviceStateManager deviceStateManager =
mContext.getSystemService(DeviceStateManager.class);
- deviceStateManager.registerDeviceStateListener(new DeviceStateListener(),
- new HandlerExecutor(mHandler));
+ deviceStateManager.addDeviceStateListener(new HandlerExecutor(mHandler),
+ new DeviceStateListener());
scheduleTraversalLocked(false);
}
@@ -2579,14 +2579,14 @@
}
@Override // Binder call
- public void setTemporaryBrightness(float brightness) {
+ public void setTemporaryBrightness(int displayId, float brightness) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
"Permission required to set the display's brightness");
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
- mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY)
+ mDisplayPowerControllers.get(displayId)
.setTemporaryBrightness(brightness);
}
} finally {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 7e6a137..73ebb2e 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -105,17 +105,18 @@
Slog.w(TAG, "No valid info found for display device " + physicalDisplayId);
return;
}
- SurfaceControl.DisplayConfig[] configs = SurfaceControl.getDisplayConfigs(displayToken);
- if (configs == null) {
- // There are no valid configs for this device, so we can't use it
- Slog.w(TAG, "No valid configs found for display device " + physicalDisplayId);
+ SurfaceControl.DisplayMode[] displayModes =
+ SurfaceControl.getDisplayModes(displayToken);
+ if (displayModes == null) {
+ // There are no valid modes for this device, so we can't use it
+ Slog.w(TAG, "No valid modes found for display device " + physicalDisplayId);
return;
}
- int activeConfig = SurfaceControl.getActiveConfig(displayToken);
- if (activeConfig < 0) {
- // There is no active config, and for now we don't have the
+ int activeDisplayMode = SurfaceControl.getActiveDisplayMode(displayToken);
+ if (activeDisplayMode < 0) {
+ // There is no active mode, and for now we don't have the
// policy to set one.
- Slog.w(TAG, "No active config found for display device " + physicalDisplayId);
+ Slog.w(TAG, "No active mode found for display device " + physicalDisplayId);
return;
}
int activeColorMode = SurfaceControl.getActiveColorMode(displayToken);
@@ -127,8 +128,8 @@
physicalDisplayId);
activeColorMode = Display.COLOR_MODE_INVALID;
}
- SurfaceControl.DesiredDisplayConfigSpecs configSpecs =
- SurfaceControl.getDesiredDisplayConfigSpecs(displayToken);
+ SurfaceControl.DesiredDisplayModeSpecs modeSpecsSpecs =
+ SurfaceControl.getDesiredDisplayModeSpecs(displayToken);
int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken);
Display.HdrCapabilities hdrCapabilities =
SurfaceControl.getHdrCapabilities(displayToken);
@@ -136,13 +137,13 @@
if (device == null) {
// Display was added.
final boolean isDefaultDisplay = mDevices.size() == 0;
- device = new LocalDisplayDevice(displayToken, physicalDisplayId, info,
- configs, activeConfig, configSpecs, colorModes, activeColorMode,
+ device = new LocalDisplayDevice(displayToken, physicalDisplayId, info, displayModes,
+ activeDisplayMode, modeSpecsSpecs, colorModes, activeColorMode,
hdrCapabilities, isDefaultDisplay);
mDevices.put(physicalDisplayId, device);
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
- } else if (device.updateDisplayPropertiesLocked(info, configs, activeConfig,
- configSpecs, colorModes, activeColorMode, hdrCapabilities)) {
+ } else if (device.updateDisplayPropertiesLocked(info, displayModes, activeDisplayMode,
+ modeSpecsSpecs, colorModes, activeColorMode, hdrCapabilities)) {
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
}
} else {
@@ -189,12 +190,12 @@
// This is only set in the runnable returned from requestDisplayStateLocked.
private float mBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
private int mDefaultModeId;
- private int mDefaultConfigGroup;
+ private int mDefaultModeGroup;
private int mActiveModeId;
private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs =
new DisplayModeDirector.DesiredDisplayModeSpecs();
private boolean mDisplayModeSpecsInvalid;
- private int mActiveConfigId;
+ private int mActiveDisplayModeId;
private int mActiveColorMode;
private Display.HdrCapabilities mHdrCapabilities;
private boolean mAllmSupported;
@@ -204,7 +205,7 @@
private boolean mSidekickActive;
private SidekickInternal mSidekickInternal;
private SurfaceControl.DisplayInfo mDisplayInfo;
- private SurfaceControl.DisplayConfig[] mDisplayConfigs;
+ private SurfaceControl.DisplayMode[] mDisplayModes;
private Spline mSystemBrightnessToNits;
private Spline mNitsToHalBrightness;
private DisplayDeviceConfig mDisplayDeviceConfig;
@@ -213,15 +214,15 @@
new DisplayEventReceiver.FrameRateOverride[0];
LocalDisplayDevice(IBinder displayToken, long physicalDisplayId,
- SurfaceControl.DisplayInfo info, SurfaceControl.DisplayConfig[] configs,
- int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
+ SurfaceControl.DisplayInfo info, SurfaceControl.DisplayMode[] displayModes,
+ int activeDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs,
int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities,
boolean isDefaultDisplay) {
super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId);
mPhysicalDisplayId = physicalDisplayId;
mIsDefaultDisplay = isDefaultDisplay;
- updateDisplayPropertiesLocked(info, configs, activeConfigId, configSpecs, colorModes,
- activeColorMode, hdrCapabilities);
+ updateDisplayPropertiesLocked(info, displayModes, activeDisplayModeId, modeSpecs,
+ colorModes, activeColorMode, hdrCapabilities);
mSidekickInternal = LocalServices.getService(SidekickInternal.class);
mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay);
mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken);
@@ -241,10 +242,11 @@
* Returns true if there is a change.
**/
public boolean updateDisplayPropertiesLocked(SurfaceControl.DisplayInfo info,
- SurfaceControl.DisplayConfig[] configs,
- int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
+ SurfaceControl.DisplayMode[] displayModes,
+ int activeDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs,
int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities) {
- boolean changed = updateDisplayConfigsLocked(configs, activeConfigId, configSpecs);
+ boolean changed = updateDisplayModesLocked(
+ displayModes, activeDisplayModeId, modeSpecs);
changed |= updateDisplayInfo(info);
changed |= updateColorModesLocked(colorModes, activeColorMode);
changed |= updateHdrCapabilitiesLocked(hdrCapabilities);
@@ -255,35 +257,35 @@
return changed;
}
- public boolean updateDisplayConfigsLocked(
- SurfaceControl.DisplayConfig[] configs, int activeConfigId,
- SurfaceControl.DesiredDisplayConfigSpecs configSpecs) {
- mDisplayConfigs = Arrays.copyOf(configs, configs.length);
- mActiveConfigId = activeConfigId;
+ public boolean updateDisplayModesLocked(
+ SurfaceControl.DisplayMode[] displayModes, int activeDisplayModeId,
+ SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
+ mDisplayModes = Arrays.copyOf(displayModes, displayModes.length);
+ mActiveDisplayModeId = activeDisplayModeId;
// Build an updated list of all existing modes.
ArrayList<DisplayModeRecord> records = new ArrayList<>();
boolean modesAdded = false;
- for (int i = 0; i < configs.length; i++) {
- SurfaceControl.DisplayConfig config = configs[i];
+ for (int i = 0; i < displayModes.length; i++) {
+ SurfaceControl.DisplayMode mode = displayModes[i];
List<Float> alternativeRefreshRates = new ArrayList<>();
- for (int j = 0; j < configs.length; j++) {
- SurfaceControl.DisplayConfig other = configs[j];
- boolean isAlternative = j != i && other.width == config.width
- && other.height == config.height
- && other.refreshRate != config.refreshRate
- && other.configGroup == config.configGroup;
+ for (int j = 0; j < displayModes.length; j++) {
+ SurfaceControl.DisplayMode other = displayModes[j];
+ boolean isAlternative = j != i && other.width == mode.width
+ && other.height == mode.height
+ && other.refreshRate != mode.refreshRate
+ && other.group == mode.group;
if (isAlternative) {
- alternativeRefreshRates.add(configs[j].refreshRate);
+ alternativeRefreshRates.add(displayModes[j].refreshRate);
}
}
Collections.sort(alternativeRefreshRates);
// First, check to see if we've already added a matching mode. Since not all
// configuration options are exposed via Display.Mode, it's possible that we have
- // multiple DisplayConfigs that would generate the same Display.Mode.
+ // multiple DisplayModess that would generate the same Display.Mode.
boolean existingMode = false;
for (DisplayModeRecord record : records) {
- if (record.hasMatchingMode(config)
+ if (record.hasMatchingMode(mode)
&& refreshRatesEquals(alternativeRefreshRates,
record.mMode.getAlternativeRefreshRates())) {
existingMode = true;
@@ -296,13 +298,13 @@
// If we haven't already added a mode for this configuration to the new set of
// supported modes then check to see if we have one in the prior set of supported
// modes to reuse.
- DisplayModeRecord record = findDisplayModeRecord(config, alternativeRefreshRates);
+ DisplayModeRecord record = findDisplayModeRecord(mode, alternativeRefreshRates);
if (record == null) {
float[] alternativeRates = new float[alternativeRefreshRates.size()];
for (int j = 0; j < alternativeRates.length; j++) {
alternativeRates[j] = alternativeRefreshRates.get(j);
}
- record = new DisplayModeRecord(config, alternativeRates);
+ record = new DisplayModeRecord(mode, alternativeRates);
modesAdded = true;
}
records.add(record);
@@ -312,7 +314,7 @@
DisplayModeRecord activeRecord = null;
for (int i = 0; i < records.size(); i++) {
DisplayModeRecord record = records.get(i);
- if (record.hasMatchingMode(configs[activeConfigId])) {
+ if (record.hasMatchingMode(displayModes[activeDisplayModeId])) {
activeRecord = record;
break;
}
@@ -334,20 +336,20 @@
// Check whether surface flinger spontaneously changed display config specs out from
// under us. If so, schedule a traversal to reapply our display config specs.
if (mDisplayModeSpecs.baseModeId != NO_DISPLAY_MODE_ID) {
- int activeBaseMode = findMatchingModeIdLocked(configSpecs.defaultConfig);
- // If we can't map the defaultConfig index to a mode, then the physical display
- // configs must have changed, and the code below for handling changes to the
- // list of available modes will take care of updating display config specs.
+ int activeBaseMode = findMatchingModeIdLocked(modeSpecs.defaultMode);
+ // If we can't map the defaultMode index to a mode, then the physical display
+ // modes must have changed, and the code below for handling changes to the
+ // list of available modes will take care of updating display mode specs.
if (activeBaseMode != NO_DISPLAY_MODE_ID) {
if (mDisplayModeSpecs.baseModeId != activeBaseMode
|| mDisplayModeSpecs.primaryRefreshRateRange.min
- != configSpecs.primaryRefreshRateMin
+ != modeSpecs.primaryRefreshRateMin
|| mDisplayModeSpecs.primaryRefreshRateRange.max
- != configSpecs.primaryRefreshRateMax
+ != modeSpecs.primaryRefreshRateMax
|| mDisplayModeSpecs.appRequestRefreshRateRange.min
- != configSpecs.appRequestRefreshRateMin
+ != modeSpecs.appRequestRefreshRateMin
|| mDisplayModeSpecs.appRequestRefreshRateRange.max
- != configSpecs.appRequestRefreshRateMax) {
+ != modeSpecs.appRequestRefreshRateMax) {
mDisplayModeSpecsInvalid = true;
sendTraversalRequestLocked();
}
@@ -368,17 +370,17 @@
// For a new display, we need to initialize the default mode ID.
if (mDefaultModeId == NO_DISPLAY_MODE_ID) {
mDefaultModeId = activeRecord.mMode.getModeId();
- mDefaultConfigGroup = configs[activeConfigId].configGroup;
+ mDefaultModeGroup = displayModes[activeDisplayModeId].group;
} else if (modesAdded && activeModeChanged) {
Slog.d(TAG, "New display modes are added and the active mode has changed, "
+ "use active mode as default mode.");
mDefaultModeId = activeRecord.mMode.getModeId();
- mDefaultConfigGroup = configs[activeConfigId].configGroup;
- } else if (findDisplayConfigIdLocked(mDefaultModeId, mDefaultConfigGroup) < 0) {
+ mDefaultModeGroup = displayModes[activeDisplayModeId].group;
+ } else if (findDisplayModeIdLocked(mDefaultModeId, mDefaultModeGroup) < 0) {
Slog.w(TAG, "Default display mode no longer available, using currently"
+ " active mode as default.");
mDefaultModeId = activeRecord.mMode.getModeId();
- mDefaultConfigGroup = configs[activeConfigId].configGroup;
+ mDefaultModeGroup = displayModes[activeDisplayModeId].group;
}
// Determine whether the display mode specs' base mode is still there.
@@ -518,11 +520,11 @@
return true;
}
- private DisplayModeRecord findDisplayModeRecord(SurfaceControl.DisplayConfig config,
+ private DisplayModeRecord findDisplayModeRecord(SurfaceControl.DisplayMode mode,
List<Float> alternativeRefreshRates) {
for (int i = 0; i < mSupportedModes.size(); i++) {
DisplayModeRecord record = mSupportedModes.valueAt(i);
- if (record.hasMatchingMode(config)
+ if (record.hasMatchingMode(mode)
&& refreshRatesEquals(alternativeRefreshRates,
record.mMode.getAlternativeRefreshRates())) {
return record;
@@ -554,10 +556,10 @@
@Override
public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
if (mInfo == null) {
- SurfaceControl.DisplayConfig config = mDisplayConfigs[mActiveConfigId];
+ SurfaceControl.DisplayMode mode = mDisplayModes[mActiveDisplayModeId];
mInfo = new DisplayDeviceInfo();
- mInfo.width = config.width;
- mInfo.height = config.height;
+ mInfo.width = mode.width;
+ mInfo.height = mode.height;
mInfo.modeId = mActiveModeId;
mInfo.defaultModeId = mDefaultModeId;
mInfo.supportedModes = getDisplayModes(mSupportedModes);
@@ -570,16 +572,16 @@
mInfo.supportedColorModes[i] = mSupportedColorModes.get(i);
}
mInfo.hdrCapabilities = mHdrCapabilities;
- mInfo.appVsyncOffsetNanos = config.appVsyncOffsetNanos;
- mInfo.presentationDeadlineNanos = config.presentationDeadlineNanos;
+ mInfo.appVsyncOffsetNanos = mode.appVsyncOffsetNanos;
+ mInfo.presentationDeadlineNanos = mode.presentationDeadlineNanos;
mInfo.state = mState;
mInfo.uniqueId = getUniqueId();
final DisplayAddress.Physical physicalAddress =
DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId);
mInfo.address = physicalAddress;
mInfo.densityDpi = (int) (mDisplayInfo.density * 160 + 0.5f);
- mInfo.xDpi = config.xDpi;
- mInfo.yDpi = config.yDpi;
+ mInfo.xDpi = mode.xDpi;
+ mInfo.yDpi = mode.yDpi;
mInfo.deviceProductInfo = mDisplayInfo.deviceProductInfo;
// Assume that all built-in displays that have secure output (eg. HDCP) also
@@ -835,16 +837,16 @@
return;
}
- // Find the config Id based on the desired mode specs. In case there is more than one
- // config matching the mode spec, prefer the one that is in the default config group.
- // For now the default config group is taken from the active config when we got the
+ // Find the mode Id based on the desired mode specs. In case there is more than one
+ // mode matching the mode spec, prefer the one that is in the default mode group.
+ // For now the default config mode is taken from the active mode when we got the
// hotplug event for the display. In the future we might want to change the default
- // config based on vendor requirements.
- // Note: We prefer the default config group over the current one as this is the config
+ // mode based on vendor requirements.
+ // Note: We prefer the default mode group over the current one as this is the mode
// group the vendor prefers.
- int baseConfigId = findDisplayConfigIdLocked(displayModeSpecs.baseModeId,
- mDefaultConfigGroup);
- if (baseConfigId < 0) {
+ int baseModeId = findDisplayModeIdLocked(displayModeSpecs.baseModeId,
+ mDefaultModeGroup);
+ if (baseModeId < 0) {
// When a display is hotplugged, it's possible for a mode to be removed that was
// previously valid. Because of the way display changes are propagated through the
// framework, and the caching of the display mode specs in LogicalDisplay, it's
@@ -862,7 +864,7 @@
getHandler().sendMessage(PooledLambda.obtainMessage(
LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this,
getDisplayTokenLocked(),
- new SurfaceControl.DesiredDisplayConfigSpecs(baseConfigId,
+ new SurfaceControl.DesiredDisplayModeSpecs(baseModeId,
mDisplayModeSpecs.allowGroupSwitching,
mDisplayModeSpecs.primaryRefreshRateRange.min,
mDisplayModeSpecs.primaryRefreshRateRange.max,
@@ -872,13 +874,13 @@
}
private void setDesiredDisplayModeSpecsAsync(IBinder displayToken,
- SurfaceControl.DesiredDisplayConfigSpecs configSpecs) {
+ SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
// Do not lock when calling these SurfaceControl methods because they are sync
// operations that may block for a while when setting display power mode.
- SurfaceControl.setDesiredDisplayConfigSpecs(displayToken, configSpecs);
- final int activePhysIndex = SurfaceControl.getActiveConfig(displayToken);
+ SurfaceControl.setDesiredDisplayModeSpecs(displayToken, modeSpecs);
+ final int activeMode = SurfaceControl.getActiveDisplayMode(displayToken);
synchronized (getSyncRoot()) {
- if (updateActiveModeLocked(activePhysIndex)) {
+ if (updateActiveModeLocked(activeMode)) {
updateDeviceInfoLocked();
}
}
@@ -889,8 +891,8 @@
updateDeviceInfoLocked();
}
- public void onActiveDisplayConfigChangedLocked(int configId) {
- if (updateActiveModeLocked(configId)) {
+ public void onActiveDisplayModeChangedLocked(int modeId) {
+ if (updateActiveModeLocked(modeId)) {
updateDeviceInfoLocked();
}
}
@@ -902,15 +904,15 @@
}
}
- public boolean updateActiveModeLocked(int activeConfigId) {
- if (mActiveConfigId == activeConfigId) {
+ public boolean updateActiveModeLocked(int activeModeId) {
+ if (mActiveDisplayModeId == activeModeId) {
return false;
}
- mActiveConfigId = activeConfigId;
- mActiveModeId = findMatchingModeIdLocked(activeConfigId);
+ mActiveDisplayModeId = activeModeId;
+ mActiveModeId = findMatchingModeIdLocked(activeModeId);
if (mActiveModeId == NO_DISPLAY_MODE_ID) {
- Slog.w(TAG, "In unknown mode after setting allowed configs"
- + ", activeConfigId=" + mActiveConfigId);
+ Slog.w(TAG, "In unknown mode after setting allowed modes"
+ + ", activeModeId=" + mActiveDisplayModeId);
}
return true;
}
@@ -990,7 +992,7 @@
pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId);
pw.println("mDisplayModeSpecs={" + mDisplayModeSpecs + "}");
pw.println("mDisplayModeSpecsInvalid=" + mDisplayModeSpecsInvalid);
- pw.println("mActiveConfigId=" + mActiveConfigId);
+ pw.println("mActiveDisplayModeId=" + mActiveDisplayModeId);
pw.println("mActiveModeId=" + mActiveModeId);
pw.println("mActiveColorMode=" + mActiveColorMode);
pw.println("mDefaultModeId=" + mDefaultModeId);
@@ -1002,9 +1004,9 @@
pw.println("mGameContentTypeSupported=" + mGameContentTypeSupported);
pw.println("mGameContentTypeRequested=" + mGameContentTypeRequested);
pw.println("mDisplayInfo=" + mDisplayInfo);
- pw.println("mDisplayConfigs=");
- for (int i = 0; i < mDisplayConfigs.length; i++) {
- pw.println(" " + mDisplayConfigs[i]);
+ pw.println("mDisplayModes=");
+ for (int i = 0; i < mDisplayModes.length; i++) {
+ pw.println(" " + mDisplayModes[i]);
}
pw.println("mSupportedModes=");
for (int i = 0; i < mSupportedModes.size(); i++) {
@@ -1014,37 +1016,37 @@
pw.println("mDisplayDeviceConfig=" + mDisplayDeviceConfig);
}
- private int findDisplayConfigIdLocked(int modeId, int configGroup) {
- int matchingConfigId = SurfaceControl.DisplayConfig.INVALID_DISPLAY_CONFIG_ID;
+ private int findDisplayModeIdLocked(int modeId, int modeGroup) {
+ int matchingModeId = SurfaceControl.DisplayMode.INVALID_DISPLAY_MODE_ID;
DisplayModeRecord record = mSupportedModes.get(modeId);
if (record != null) {
- for (int i = 0; i < mDisplayConfigs.length; i++) {
- SurfaceControl.DisplayConfig config = mDisplayConfigs[i];
- if (record.hasMatchingMode(config)) {
- if (matchingConfigId
- == SurfaceControl.DisplayConfig.INVALID_DISPLAY_CONFIG_ID) {
- matchingConfigId = i;
+ for (int i = 0; i < mDisplayModes.length; i++) {
+ SurfaceControl.DisplayMode mode = mDisplayModes[i];
+ if (record.hasMatchingMode(mode)) {
+ if (matchingModeId
+ == SurfaceControl.DisplayMode.INVALID_DISPLAY_MODE_ID) {
+ matchingModeId = i;
}
- // Prefer to return a config that matches the configGroup
- if (config.configGroup == configGroup) {
+ // Prefer to return a mode that matches the modeGroup
+ if (mode.group == modeGroup) {
return i;
}
}
}
}
- return matchingConfigId;
+ return matchingModeId;
}
- private int findMatchingModeIdLocked(int configId) {
- if (configId < 0 || configId >= mDisplayConfigs.length) {
- Slog.e(TAG, "Invalid display config index " + configId);
+ private int findMatchingModeIdLocked(int modeId) {
+ if (modeId < 0 || modeId >= mDisplayModes.length) {
+ Slog.e(TAG, "Invalid display config index " + modeId);
return NO_DISPLAY_MODE_ID;
}
- SurfaceControl.DisplayConfig config = mDisplayConfigs[configId];
+ SurfaceControl.DisplayMode mode = mDisplayModes[modeId];
for (int i = 0; i < mSupportedModes.size(); i++) {
DisplayModeRecord record = mSupportedModes.valueAt(i);
- if (record.hasMatchingMode(config)) {
+ if (record.hasMatchingMode(mode)) {
return record.mMode.getModeId();
}
}
@@ -1091,30 +1093,29 @@
}
/**
- * Keeps track of a display configuration.
+ * Keeps track of a display mode.
*/
private static final class DisplayModeRecord {
public final Display.Mode mMode;
- DisplayModeRecord(SurfaceControl.DisplayConfig config,
+ DisplayModeRecord(SurfaceControl.DisplayMode mode,
float[] alternativeRefreshRates) {
- mMode = createMode(config.width, config.height, config.refreshRate,
+ mMode = createMode(mode.width, mode.height, mode.refreshRate,
alternativeRefreshRates);
}
/**
- * Returns whether the mode generated by the given DisplayConfig matches the mode
+ * Returns whether the mode generated by the given DisplayModes matches the mode
* contained by the record modulo mode ID.
*
- * Note that this doesn't necessarily mean that the DisplayConfigs are identical, just
+ * Note that this doesn't necessarily mean that the DisplayModes are identical, just
* that they generate identical modes.
*/
- public boolean hasMatchingMode(SurfaceControl.DisplayConfig config) {
- int modeRefreshRate = Float.floatToIntBits(mMode.getRefreshRate());
- int configRefreshRate = Float.floatToIntBits(config.refreshRate);
- return mMode.getPhysicalWidth() == config.width
- && mMode.getPhysicalHeight() == config.height
- && modeRefreshRate == configRefreshRate;
+ public boolean hasMatchingMode(SurfaceControl.DisplayMode mode) {
+ return mMode.getPhysicalWidth() == mode.width
+ && mMode.getPhysicalHeight() == mode.height
+ && Float.floatToIntBits(mMode.getRefreshRate())
+ == Float.floatToIntBits(mode.refreshRate);
}
public String toString() {
@@ -1131,7 +1132,7 @@
public interface DisplayEventListener {
void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected);
- void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId);
+ void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId);
void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId,
DisplayEventReceiver.FrameRateOverride[] overrides);
@@ -1141,7 +1142,7 @@
private final DisplayEventListener mListener;
ProxyDisplayEventReceiver(Looper looper, DisplayEventListener listener) {
super(looper, VSYNC_SOURCE_APP,
- EVENT_REGISTRATION_CONFIG_CHANGED_FLAG
+ EVENT_REGISTRATION_MODE_CHANGED_FLAG
| EVENT_REGISTRATION_FRAME_RATE_OVERRIDE_FLAG);
mListener = listener;
}
@@ -1152,8 +1153,8 @@
}
@Override
- public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
- mListener.onConfigChanged(timestampNanos, physicalDisplayId, configId);
+ public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId) {
+ mListener.onModeChanged(timestampNanos, physicalDisplayId, modeId);
}
@Override
@@ -1176,23 +1177,23 @@
}
@Override
- public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
+ public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId) {
if (DEBUG) {
- Slog.d(TAG, "onConfigChanged("
+ Slog.d(TAG, "onModeChanged("
+ "timestampNanos=" + timestampNanos
+ ", physicalDisplayId=" + physicalDisplayId
- + ", configId=" + configId + ")");
+ + ", modeId=" + modeId + ")");
}
synchronized (getSyncRoot()) {
LocalDisplayDevice device = mDevices.get(physicalDisplayId);
if (device == null) {
if (DEBUG) {
- Slog.d(TAG, "Received config change for unhandled physical display: "
+ Slog.d(TAG, "Received mode change for unhandled physical display: "
+ "physicalDisplayId=" + physicalDisplayId);
}
return;
}
- device.onActiveDisplayConfigChangedLocked(configId);
+ device.onActiveDisplayModeChangedLocked(modeId);
}
}
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 3e2b5ab..1b27572 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -345,6 +345,7 @@
synchronized (mSerializedFontMapLock) {
mSerializedFontMap = serializeFontMap;
}
+ return;
} catch (IOException | ErrnoException e) {
Slog.w(TAG, "Failed to serialize updatable font map. "
+ "Retrying with system image fonts.", e);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 0ae1994..bdf92ca 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -541,7 +541,7 @@
private void bootCompleted() {
// on boot, if device is interactive, set HDMI CEC state as powered on as well
if (mPowerManager.isInteractive() && isPowerStandbyOrTransient()) {
- onWakeUp(WAKE_UP_BOOT_UP);
+ mPowerStatusController.setPowerStatus(HdmiControlManager.POWER_STATUS_ON);
}
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 2e4200c..4e97411 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -57,6 +57,7 @@
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IVibratorStateListener;
import android.os.InputEventInjectionResult;
import android.os.InputEventInjectionSync;
import android.os.LocaleList;
@@ -64,6 +65,7 @@
import android.os.Message;
import android.os.MessageQueue;
import android.os.Process;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
@@ -77,6 +79,7 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.view.Display;
import android.view.IInputFilter;
import android.view.IInputFilterHost;
@@ -100,6 +103,7 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
@@ -221,13 +225,16 @@
private Map<IBinder, VibratorToken> mVibratorTokens = new ArrayMap<IBinder, VibratorToken>();
private int mNextVibratorTokenValue;
+ // List of currently registered vibrator state changed listeners by device id.
+ @GuardedBy("mVibratorLock")
+ private final SparseArray<RemoteCallbackList<IVibratorStateListener>> mVibratorStateListeners =
+ new SparseArray<RemoteCallbackList<IVibratorStateListener>>();
+ // List of vibrator states by device id.
+ @GuardedBy("mVibratorLock")
+ private final SparseBooleanArray mIsVibrating = new SparseBooleanArray();
+
// State for lid switch
- // Lock for the lid switch state. Held when triggering callbacks to guarantee lid switch events
- // are delivered in order. For ex, when a new lid switch callback is registered the lock is held
- // while the callback is processing the initial lid switch event which guarantees that any
- // events that occur at the same time are delivered after the callback has returned.
private final Object mLidSwitchLock = new Object();
- @GuardedBy("mLidSwitchLock")
private List<LidSwitchCallback> mLidSwitchCallbacks = new ArrayList<>();
// State for the currently installed input filter.
@@ -375,6 +382,9 @@
public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER;
public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE;
+ /** Indicates an open state for the lid switch. */
+ public static final int SW_STATE_LID_OPEN = 0;
+
/** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
final boolean mUseDevInputEventForAudioJack;
@@ -410,18 +420,13 @@
}
void registerLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) {
+ boolean lidOpen;
synchronized (mLidSwitchLock) {
mLidSwitchCallbacks.add(callback);
-
- // Skip triggering the initial callback if the system is not yet ready as the switch
- // state will be reported as KEY_STATE_UNKNOWN. The callback will be triggered in
- // systemRunning().
- if (mSystemReady) {
- boolean lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID)
- == KEY_STATE_UP;
- callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen);
- }
+ lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID)
+ == SW_STATE_LID_OPEN;
}
+ callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen);
}
void unregisterLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) {
@@ -469,18 +474,7 @@
}
mNotificationManager = (NotificationManager)mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
-
- synchronized (mLidSwitchLock) {
- mSystemReady = true;
-
- // Send the initial lid switch state to any callback registered before the system was
- // ready.
- int switchState = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID);
- for (int i = 0; i < mLidSwitchCallbacks.size(); i++) {
- LidSwitchCallback callback = mLidSwitchCallbacks.get(i);
- callback.notifyLidSwitchChanged(0 /* whenNanos */, switchState == KEY_STATE_UP);
- }
- }
+ mSystemReady = true;
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -2008,6 +2002,92 @@
}
}
+ // Native callback.
+ private void notifyVibratorState(int deviceId, boolean isOn) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyVibratorState: deviceId=" + deviceId + " isOn=" + isOn);
+ }
+ synchronized (mVibratorLock) {
+ mIsVibrating.put(deviceId, isOn);
+ notifyVibratorStateListenersLocked(deviceId);
+ }
+ }
+
+ @GuardedBy("mVibratorLock")
+ private void notifyVibratorStateListenersLocked(int deviceId) {
+ if (!mVibratorStateListeners.contains(deviceId)) {
+ if (DEBUG) {
+ Slog.v(TAG, "Device " + deviceId + " doesn't have vibrator state listener.");
+ }
+ return;
+ }
+ RemoteCallbackList<IVibratorStateListener> listeners =
+ mVibratorStateListeners.get(deviceId);
+ final int length = listeners.beginBroadcast();
+ try {
+ for (int i = 0; i < length; i++) {
+ notifyVibratorStateListenerLocked(deviceId, listeners.getBroadcastItem(i));
+ }
+ } finally {
+ listeners.finishBroadcast();
+ }
+ }
+
+ @GuardedBy("mVibratorLock")
+ private void notifyVibratorStateListenerLocked(int deviceId, IVibratorStateListener listener) {
+ try {
+ listener.onVibrating(mIsVibrating.get(deviceId));
+ } catch (RemoteException | RuntimeException e) {
+ Slog.e(TAG, "Vibrator state listener failed to call", e);
+ }
+ }
+
+ @Override // Binder call
+ public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+ Preconditions.checkNotNull(listener, "listener must not be null");
+
+ RemoteCallbackList<IVibratorStateListener> listeners;
+ synchronized (mVibratorLock) {
+ if (!mVibratorStateListeners.contains(deviceId)) {
+ listeners = new RemoteCallbackList<>();
+ mVibratorStateListeners.put(deviceId, listeners);
+ } else {
+ listeners = mVibratorStateListeners.get(deviceId);
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!listeners.register(listener)) {
+ Slog.e(TAG, "Could not register vibrator state listener " + listener);
+ return false;
+ }
+ // Notify its callback after new client registered.
+ notifyVibratorStateListenerLocked(deviceId, listener);
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
+ @Override // Binder call
+ public boolean unregisterVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+ synchronized (mVibratorLock) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (!mVibratorStateListeners.contains(deviceId)) {
+ Slog.w(TAG, "Vibrator state listener " + deviceId + " doesn't exist");
+ return false;
+ }
+ RemoteCallbackList<IVibratorStateListener> listeners =
+ mVibratorStateListeners.get(deviceId);
+ return listeners.unregister(listener);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
// Binder call
@Override
public int getBatteryStatus(int deviceId) {
@@ -2251,13 +2331,14 @@
if ((switchMask & SW_LID_BIT) != 0) {
final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0);
+
+ ArrayList<LidSwitchCallback> callbacksCopy;
synchronized (mLidSwitchLock) {
- if (mSystemReady) {
- for (int i = 0; i < mLidSwitchCallbacks.size(); i++) {
- LidSwitchCallback callbacks = mLidSwitchCallbacks.get(i);
- callbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
- }
- }
+ callbacksCopy = new ArrayList<>(mLidSwitchCallbacks);
+ }
+ for (int i = 0; i < callbacksCopy.size(); i++) {
+ LidSwitchCallback callbacks = callbacksCopy.get(i);
+ callbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e5b5350..1a4c8b7 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -110,7 +110,6 @@
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
@@ -300,45 +299,6 @@
private static final String ACTION_SHOW_INPUT_METHOD_PICKER =
"com.android.server.inputmethod.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER";
- /**
- * Debug flag for overriding runtime {@link SystemProperties}.
- */
- @AnyThread
- private static final class DebugFlag {
- private static final Object LOCK = new Object();
- private final String mKey;
- private final boolean mDefaultValue;
- @GuardedBy("LOCK")
- private boolean mValue;
-
- public DebugFlag(String key, boolean defaultValue) {
- mKey = key;
- mDefaultValue = defaultValue;
- mValue = SystemProperties.getBoolean(key, defaultValue);
- }
-
- void refresh() {
- synchronized (LOCK) {
- mValue = SystemProperties.getBoolean(mKey, mDefaultValue);
- }
- }
-
- boolean value() {
- synchronized (LOCK) {
- return mValue;
- }
- }
- }
-
- /**
- * Debug flags that can be overridden using "adb shell setprop <key>"
- * Note: These flags are cached. To refresh, run "adb shell ime refresh_debug_properties".
- */
- private static final class DebugFlags {
- static final DebugFlag FLAG_OPTIMIZE_START_INPUT =
- new DebugFlag("debug.optimize_startinput", false);
- }
-
@UserIdInt
private int mLastSwitchUserId;
@@ -2586,7 +2546,7 @@
+ mCurTokenDisplayId);
}
mIWindowManager.addWindowToken(mCurToken, LayoutParams.TYPE_INPUT_METHOD,
- mCurTokenDisplayId);
+ mCurTokenDisplayId, null /* options */);
} catch (RemoteException e) {
}
return new InputBindResult(
@@ -3687,12 +3647,9 @@
}
res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
startInputFlags, startInputReason);
- } else if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value()
- || (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) {
+ } else {
res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
startInputFlags, startInputReason);
- } else {
- res = InputBindResult.NO_EDITOR;
}
} else {
res = InputBindResult.NULL_EDITOR_INFO;
@@ -5270,6 +5227,8 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+
boolean asProto = false;
for (int argIndex = 0; argIndex < args.length; argIndex++) {
if (args[argIndex].equals(PROTO_ARG)) {
@@ -5292,8 +5251,6 @@
}
private void doDump(FileDescriptor fd, PrintWriter pw, String[] args, boolean useProto) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-
if (useProto) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
dumpDebug(proto, InputMethodManagerServiceTraceProto.INPUT_METHOD_MANAGER_SERVICE);
@@ -5467,10 +5424,6 @@
@BinderThread
@ShellCommandResult
private int onCommandWithSystemIdentity(@Nullable String cmd) {
- if ("refresh_debug_properties".equals(cmd)) {
- return refreshDebugProperties();
- }
-
if ("get-last-switch-user-id".equals(cmd)) {
return mService.getLastSwitchUserId(this);
}
@@ -5505,13 +5458,6 @@
}
@BinderThread
- @ShellCommandResult
- private int refreshDebugProperties() {
- DebugFlags.FLAG_OPTIMIZE_START_INPUT.refresh();
- return ShellCommandResult.SUCCESS;
- }
-
- @BinderThread
@Override
public void onHelp() {
try (PrintWriter pw = getOutPrintWriter()) {
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 6fec906..1dd3d41 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1309,7 +1309,8 @@
final Binder token = new Binder();
Binder.withCleanCallingIdentity(
PooledLambda.obtainRunnable(WindowManagerInternal::addWindowToken,
- mIWindowManagerInternal, token, TYPE_INPUT_METHOD, displayId));
+ mIWindowManagerInternal, token, TYPE_INPUT_METHOD, displayId,
+ null /* options */));
mPerUserData.mDisplayIdToImeWindowTokenMap.add(new TokenInfo(token, displayId));
return token;
}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 142f64f..28dc516 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -66,6 +66,7 @@
import android.location.LocationProvider;
import android.location.LocationRequest;
import android.location.LocationTime;
+import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
import android.location.util.identity.CallerIdentity;
import android.os.Binder;
@@ -78,6 +79,7 @@
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;
@@ -86,6 +88,7 @@
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;
@@ -97,7 +100,6 @@
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;
@@ -146,9 +148,10 @@
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
@@ -158,7 +161,7 @@
// 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();
}
@@ -220,6 +223,7 @@
private final Context mContext;
private final Injector mInjector;
+ private final LocationEventLog mEventLog;
private final LocalService mLocalService;
private final GeofenceManager mGeofenceManager;
@@ -244,10 +248,10 @@
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);
@@ -255,7 +259,7 @@
// 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
@@ -296,7 +300,7 @@
}
LocationProviderManager manager = new LocationProviderManager(mContext, mInjector,
- providerName, mPassiveManager);
+ mEventLog, providerName, mPassiveManager);
addLocationProviderManager(manager, null);
return manager;
}
@@ -340,7 +344,7 @@
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");
@@ -359,7 +363,7 @@
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");
@@ -374,7 +378,7 @@
mGnssManagerService.onSystemReady();
LocationProviderManager gnssManager = new LocationProviderManager(mContext, mInjector,
- GPS_PROVIDER, mPassiveManager);
+ mEventLog, GPS_PROVIDER, mPassiveManager);
addLocationProviderManager(gnssManager, mGnssManagerService.getGnssLocationProvider());
}
@@ -430,7 +434,7 @@
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)
@@ -882,6 +886,20 @@
}
@Override
+ public void addProviderRequestListener(IProviderRequestListener listener) {
+ for (LocationProviderManager manager : mProviderManagers) {
+ manager.addProviderRequestListener(listener);
+ }
+ }
+
+ @Override
+ public void removeProviderRequestListener(IProviderRequestListener listener) {
+ for (LocationProviderManager manager : mProviderManagers) {
+ manager.removeProviderRequestListener(listener);
+ }
+ }
+
+ @Override
public void injectGnssMeasurementCorrections(GnssMeasurementCorrections corrections) {
if (mGnssManagerService != null) {
mGnssManagerService.injectGnssMeasurementCorrections(corrections);
@@ -1178,9 +1196,27 @@
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:");
@@ -1212,6 +1248,25 @@
}
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();
@@ -1226,7 +1281,7 @@
ipw.println("Event Log:");
ipw.increaseIndent();
- mInjector.getLocationEventLog().iterate(ipw::println);
+ mEventLog.iterate(ipw::println);
ipw.decreaseIndent();
}
@@ -1305,7 +1360,6 @@
private final Context mContext;
private final UserInfoHelper mUserInfoHelper;
- private final LocationEventLog mLocationEventLog;
private final AlarmHelper mAlarmHelper;
private final SystemAppOpsHelper mAppOpsHelper;
private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
@@ -1324,19 +1378,17 @@
@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();
@@ -1415,11 +1467,6 @@
}
@Override
- public LocationEventLog getLocationEventLog() {
- return mLocationEventLog;
- }
-
- @Override
public LocationUsageLogger getLocationUsageLogger() {
return mLocationUsageLogger;
}
diff --git a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
similarity index 77%
rename from services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
rename to services/core/java/com/android/server/location/eventlog/LocalEventLog.java
index fe51d74..12dd3e6 100644
--- a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocalEventLog.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,14 +14,15 @@
* limitations under the License.
*/
-package com.android.server.utils.eventlog;
+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 @@
boolean isFiller();
long getTimeDeltaMs();
String getLogString();
+ boolean filter(@Nullable String filter);
}
private static final class FillerEvent implements Log {
@@ -62,6 +64,11 @@
public String getLogString() {
throw new AssertionError();
}
+
+ @Override
+ public boolean filter(String filter) {
+ return false;
+ }
}
/**
@@ -87,6 +94,11 @@
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 @@
}
}
+ /**
+ * 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 @@
// 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 @@
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 @@
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 @@
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;
+ long timeMs = mCurrentRealtimeMs + log.getTimeDeltaMs() + mSystemTimeDeltaMs;
- // 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());
+ increment();
- 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();
- }
-
- @Override
- public int previousIndex() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void add(String s) {
- throw new UnsupportedOperationException();
+ return getTimePrefix(timeMs) + log.getLogString();
}
@Override
@@ -315,9 +290,16 @@
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/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
new file mode 100644
index 0000000..67060fc
--- /dev/null
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
@@ -0,0 +1,502 @@
+/*
+ * 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.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.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
+/** In memory event log for location events. */
+public class LocationEventLog extends LocalEventLog {
+
+ private static int getLogSize() {
+ if (Build.IS_DEBUGGABLE || D) {
+ return 500;
+ } else {
+ return 200;
+ }
+ }
+
+ private static final int EVENT_LOCATION_ENABLED = 1;
+ private static final int EVENT_PROVIDER_ENABLED = 2;
+ private static final int EVENT_PROVIDER_MOCKED = 3;
+ private static final int EVENT_PROVIDER_REGISTER_CLIENT = 4;
+ private static final int EVENT_PROVIDER_UNREGISTER_CLIENT = 5;
+ private static final int EVENT_PROVIDER_UPDATE_REQUEST = 6;
+ private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 7;
+ 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. */
+ public void logLocationEnabled(int userId, boolean enabled) {
+ addLogEvent(EVENT_LOCATION_POWER_SAVE_MODE_CHANGE, userId, enabled);
+ }
+
+ /** Logs a location provider enabled/disabled event. */
+ public void logProviderEnabled(String provider, int userId, boolean enabled) {
+ addLogEvent(EVENT_PROVIDER_ENABLED, provider, userId, enabled);
+ }
+
+ /** Logs a location provider being replaced/unreplaced by a mock provider. */
+ public void logProviderMocked(String provider, boolean mocked) {
+ addLogEvent(EVENT_PROVIDER_MOCKED, provider, mocked);
+ }
+
+ /** Logs a new client registration for a location provider. */
+ 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) {
+ 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. */
+ public void logProviderUpdateRequest(String provider, ProviderRequest request) {
+ addLogEvent(EVENT_PROVIDER_UPDATE_REQUEST, provider, request);
+ }
+
+ /** Logs a new incoming location for a location provider. */
+ public void logProviderReceivedLocations(String provider, int numLocations) {
+ if (Build.IS_DEBUGGABLE || D) {
+ addLogEvent(EVENT_PROVIDER_RECEIVE_LOCATION, provider, numLocations);
+ }
+ }
+
+ /** Logs a location deliver for a client of a location provider. */
+ public void logProviderDeliveredLocations(String provider, int numLocations,
+ CallerIdentity identity) {
+ if (Build.IS_DEBUGGABLE || D) {
+ addLogEvent(EVENT_PROVIDER_DELIVER_LOCATION, provider, numLocations, identity);
+ }
+ }
+
+ /** Logs that the location power save mode has changed. */
+ public void logLocationPowerSaveMode(
+ @LocationPowerSaveMode int locationPowerSaveMode) {
+ addLogEvent(EVENT_LOCATION_POWER_SAVE_MODE_CHANGE, locationPowerSaveMode);
+ }
+
+ @Override
+ protected LogEvent createLogEvent(long timeDelta, int event, Object... args) {
+ switch (event) {
+ case EVENT_LOCATION_ENABLED:
+ return new LocationEnabledEvent(timeDelta, (Integer) args[1], (Boolean) args[2]);
+ case EVENT_PROVIDER_ENABLED:
+ return new ProviderEnabledEvent(timeDelta, (String) args[0], (Integer) args[1],
+ (Boolean) args[2]);
+ case EVENT_PROVIDER_MOCKED:
+ return new ProviderMockedEvent(timeDelta, (String) args[0], (Boolean) args[1]);
+ case EVENT_PROVIDER_REGISTER_CLIENT:
+ return new ProviderRegisterEvent(timeDelta, (String) args[0], true,
+ (CallerIdentity) args[1], (LocationRequest) args[2]);
+ case EVENT_PROVIDER_UNREGISTER_CLIENT:
+ return new ProviderRegisterEvent(timeDelta, (String) args[0], false,
+ (CallerIdentity) args[1], null);
+ case EVENT_PROVIDER_UPDATE_REQUEST:
+ return new ProviderUpdateEvent(timeDelta, (String) args[0],
+ (ProviderRequest) args[1]);
+ case EVENT_PROVIDER_RECEIVE_LOCATION:
+ return new ProviderReceiveLocationEvent(timeDelta, (String) args[0],
+ (Integer) args[1]);
+ case EVENT_PROVIDER_DELIVER_LOCATION:
+ return new ProviderDeliverLocationEvent(timeDelta, (String) args[0],
+ (Integer) args[1], (CallerIdentity) args[2]);
+ case EVENT_LOCATION_POWER_SAVE_MODE_CHANGE:
+ return new LocationPowerSaveModeEvent(timeDelta, (Integer) args[0]);
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ 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 int mUserId;
+ private final boolean mEnabled;
+
+ protected ProviderEnabledEvent(long timeDelta, String provider, int userId,
+ boolean enabled) {
+ super(timeDelta, provider);
+ mUserId = userId;
+ mEnabled = enabled;
+ }
+
+ @Override
+ public String getLogString() {
+ return mProvider + " provider [u" + mUserId + "] " + (mEnabled ? "enabled"
+ : "disabled");
+ }
+ }
+
+ private static final class ProviderMockedEvent extends ProviderEvent {
+
+ private final boolean mMocked;
+
+ protected ProviderMockedEvent(long timeDelta, String provider, boolean mocked) {
+ super(timeDelta, provider);
+ mMocked = mocked;
+ }
+
+ @Override
+ public String getLogString() {
+ if (mMocked) {
+ return mProvider + " provider added mock provider override";
+ } else {
+ return mProvider + " provider removed mock provider override";
+ }
+ }
+ }
+
+ private static final class ProviderRegisterEvent extends ProviderEvent {
+
+ 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, provider);
+ mRegistered = registered;
+ mIdentity = identity;
+ mLocationRequest = locationRequest;
+ }
+
+ @Override
+ public String getLogString() {
+ if (mRegistered) {
+ return mProvider + " provider " + "+registration " + mIdentity + " -> "
+ + mLocationRequest;
+ } else {
+ return mProvider + " provider " + "-registration " + mIdentity;
+ }
+ }
+ }
+
+ private static final class ProviderUpdateEvent extends ProviderEvent {
+
+ private final ProviderRequest mRequest;
+
+ private ProviderUpdateEvent(long timeDelta, String provider, ProviderRequest request) {
+ super(timeDelta, provider);
+ mRequest = request;
+ }
+
+ @Override
+ public String getLogString() {
+ return mProvider + " provider request = " + mRequest;
+ }
+ }
+
+ private static final class ProviderReceiveLocationEvent extends ProviderEvent {
+
+ private final int mNumLocations;
+
+ private ProviderReceiveLocationEvent(long timeDelta, String provider, int numLocations) {
+ super(timeDelta, provider);
+ mNumLocations = numLocations;
+ }
+
+ @Override
+ public String getLogString() {
+ return mProvider + " provider received location[" + mNumLocations + "]";
+ }
+ }
+
+ private static final class ProviderDeliverLocationEvent extends ProviderEvent {
+
+ private final int mNumLocations;
+ @Nullable private final CallerIdentity mIdentity;
+
+ private ProviderDeliverLocationEvent(long timeDelta, String provider, int numLocations,
+ @Nullable CallerIdentity identity) {
+ super(timeDelta, provider);
+ mNumLocations = numLocations;
+ mIdentity = identity;
+ }
+
+ @Override
+ public String getLogString() {
+ return mProvider + " provider delivered location[" + mNumLocations + "] to "
+ + mIdentity;
+ }
+ }
+
+ private static final class LocationPowerSaveModeEvent extends LogEvent {
+
+ @LocationPowerSaveMode
+ private final int mLocationPowerSaveMode;
+
+ private LocationPowerSaveModeEvent(long timeDelta,
+ @LocationPowerSaveMode int locationPowerSaveMode) {
+ super(timeDelta);
+ mLocationPowerSaveMode = locationPowerSaveMode;
+ }
+
+ @Override
+ public String getLogString() {
+ String mode;
+ switch (mLocationPowerSaveMode) {
+ case LOCATION_MODE_NO_CHANGE:
+ mode = "NO_CHANGE";
+ break;
+ case LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
+ mode = "GPS_DISABLED_WHEN_SCREEN_OFF";
+ break;
+ case LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF:
+ mode = "ALL_DISABLED_WHEN_SCREEN_OFF";
+ break;
+ case LOCATION_MODE_FOREGROUND_ONLY:
+ mode = "FOREGROUND_ONLY";
+ break;
+ case LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF:
+ mode = "THROTTLE_REQUESTS_WHEN_SCREEN_OFF";
+ break;
+ default:
+ mode = "UNKNOWN";
+ break;
+ }
+ return "location power save mode changed to " + mode;
+ }
+ }
+
+ private static final class LocationEnabledEvent extends LogEvent {
+
+ private final int mUserId;
+ private final boolean mEnabled;
+
+ private LocationEnabledEvent(long timeDelta, int userId, boolean enabled) {
+ super(timeDelta);
+ mUserId = userId;
+ mEnabled = enabled;
+ }
+
+ @Override
+ public String getLogString() {
+ 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 03938b2..0e157c2 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 @@
/** Returns a LocationUsageLogger. */
LocationUsageLogger getLocationUsageLogger();
-
- /** Returns a LocationEventLog. */
- LocationEventLog getLocationEventLog();
}
diff --git a/services/core/java/com/android/server/location/injector/LocationEventLog.java b/services/core/java/com/android/server/location/injector/LocationEventLog.java
deleted file mode 100644
index b8b54b3..0000000
--- a/services/core/java/com/android/server/location/injector/LocationEventLog.java
+++ /dev/null
@@ -1,324 +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.server.location.injector;
-
-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 com.android.server.location.LocationManagerService.D;
-
-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 com.android.server.utils.eventlog.LocalEventLog;
-
-/** In memory event log for location events. */
-public class LocationEventLog extends LocalEventLog {
-
- private static int getLogSize() {
- if (Build.IS_DEBUGGABLE || D) {
- return 500;
- } else {
- return 200;
- }
- }
-
- private static final int EVENT_LOCATION_ENABLED = 1;
- private static final int EVENT_PROVIDER_ENABLED = 2;
- private static final int EVENT_PROVIDER_MOCKED = 3;
- private static final int EVENT_PROVIDER_REGISTER_CLIENT = 4;
- private static final int EVENT_PROVIDER_UNREGISTER_CLIENT = 5;
- private static final int EVENT_PROVIDER_UPDATE_REQUEST = 6;
- private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 7;
- private static final int EVENT_PROVIDER_DELIVER_LOCATION = 8;
- private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 9;
-
- public LocationEventLog() {
- super(getLogSize());
- }
-
- /** Logs a location enabled/disabled event. */
- public void logLocationEnabled(int userId, boolean enabled) {
- addLogEvent(EVENT_LOCATION_POWER_SAVE_MODE_CHANGE, userId, enabled);
- }
-
- /** Logs a location provider enabled/disabled event. */
- public void logProviderEnabled(String provider, int userId, boolean enabled) {
- addLogEvent(EVENT_PROVIDER_ENABLED, provider, userId, enabled);
- }
-
- /** Logs a location provider being replaced/unreplaced by a mock provider. */
- public void logProviderMocked(String provider, boolean mocked) {
- addLogEvent(EVENT_PROVIDER_MOCKED, provider, mocked);
- }
-
- /** Logs a new client registration for a location provider. */
- public void logProviderClientRegistered(String provider, CallerIdentity identity,
- LocationRequest request) {
- addLogEvent(EVENT_PROVIDER_REGISTER_CLIENT, provider, identity, request);
- }
-
- /** Logs a client unregistration for a location provider. */
- public void logProviderClientUnregistered(String provider,
- CallerIdentity identity) {
- addLogEvent(EVENT_PROVIDER_UNREGISTER_CLIENT, provider, identity);
- }
-
- /** Logs a change to the provider request for a location provider. */
- public void logProviderUpdateRequest(String provider, ProviderRequest request) {
- addLogEvent(EVENT_PROVIDER_UPDATE_REQUEST, provider, request);
- }
-
- /** Logs a new incoming location for a location provider. */
- public void logProviderReceivedLocations(String provider, int numLocations) {
- if (Build.IS_DEBUGGABLE || D) {
- addLogEvent(EVENT_PROVIDER_RECEIVE_LOCATION, provider, numLocations);
- }
- }
-
- /** Logs a location deliver for a client of a location provider. */
- public void logProviderDeliveredLocations(String provider, int numLocations,
- CallerIdentity identity) {
- if (Build.IS_DEBUGGABLE || D) {
- addLogEvent(EVENT_PROVIDER_DELIVER_LOCATION, provider, numLocations, identity);
- }
- }
-
- /** Logs that the location power save mode has changed. */
- public void logLocationPowerSaveMode(
- @LocationPowerSaveMode int locationPowerSaveMode) {
- addLogEvent(EVENT_LOCATION_POWER_SAVE_MODE_CHANGE, locationPowerSaveMode);
- }
-
- @Override
- protected LogEvent createLogEvent(long timeDelta, int event, Object... args) {
- switch (event) {
- case EVENT_LOCATION_ENABLED:
- return new LocationEnabledEvent(timeDelta, (Integer) args[1], (Boolean) args[2]);
- case EVENT_PROVIDER_ENABLED:
- return new ProviderEnabledEvent(timeDelta, (String) args[0], (Integer) args[1],
- (Boolean) args[2]);
- case EVENT_PROVIDER_MOCKED:
- return new ProviderMockedEvent(timeDelta, (String) args[0], (Boolean) args[1]);
- case EVENT_PROVIDER_REGISTER_CLIENT:
- return new ProviderRegisterEvent(timeDelta, (String) args[0], true,
- (CallerIdentity) args[1], (LocationRequest) args[2]);
- case EVENT_PROVIDER_UNREGISTER_CLIENT:
- return new ProviderRegisterEvent(timeDelta, (String) args[0], false,
- (CallerIdentity) args[1], null);
- case EVENT_PROVIDER_UPDATE_REQUEST:
- return new ProviderUpdateEvent(timeDelta, (String) args[0],
- (ProviderRequest) args[1]);
- case EVENT_PROVIDER_RECEIVE_LOCATION:
- return new ProviderReceiveLocationEvent(timeDelta, (String) args[0],
- (Integer) args[1]);
- case EVENT_PROVIDER_DELIVER_LOCATION:
- return new ProviderDeliverLocationEvent(timeDelta, (String) args[0],
- (Integer) args[1], (CallerIdentity) args[2]);
- case EVENT_LOCATION_POWER_SAVE_MODE_CHANGE:
- return new LocationPowerSaveModeEvent(timeDelta, (Integer) args[0]);
- default:
- throw new AssertionError();
- }
- }
-
- private static class ProviderEnabledEvent extends LogEvent {
-
- 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;
- mUserId = userId;
- mEnabled = enabled;
- }
-
- @Override
- public String getLogString() {
- return mProvider + " provider [u" + mUserId + "] " + (mEnabled ? "enabled"
- : "disabled");
- }
- }
-
- private static class ProviderMockedEvent extends LogEvent {
-
- private final String mProvider;
- private final boolean mMocked;
-
- protected ProviderMockedEvent(long timeDelta, String provider, boolean mocked) {
- super(timeDelta);
- mProvider = provider;
- mMocked = mocked;
- }
-
- @Override
- public String getLogString() {
- if (mMocked) {
- return mProvider + " provider added mock provider override";
- } else {
- return mProvider + " provider removed mock provider override";
- }
- }
- }
-
- private static class ProviderRegisterEvent extends LogEvent {
-
- 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;
- mRegistered = registered;
- mIdentity = identity;
- mLocationRequest = locationRequest;
- }
-
- @Override
- public String getLogString() {
- if (mRegistered) {
- return mProvider + " provider " + "+registration " + mIdentity + " -> "
- + mLocationRequest;
- } else {
- return mProvider + " provider " + "-registration " + mIdentity;
- }
- }
- }
-
- private static class ProviderUpdateEvent extends LogEvent {
-
- private final String mProvider;
- private final ProviderRequest mRequest;
-
- private ProviderUpdateEvent(long timeDelta, String provider, ProviderRequest request) {
- super(timeDelta);
- mProvider = provider;
- mRequest = request;
- }
-
- @Override
- public String getLogString() {
- return mProvider + " provider request = " + mRequest;
- }
- }
-
- private static class ProviderReceiveLocationEvent extends LogEvent {
-
- private final String mProvider;
- private final int mNumLocations;
-
- private ProviderReceiveLocationEvent(long timeDelta, String provider, int numLocations) {
- super(timeDelta);
- mProvider = provider;
- mNumLocations = numLocations;
- }
-
- @Override
- public String getLogString() {
- return mProvider + " provider received location[" + mNumLocations + "]";
- }
- }
-
- private static class ProviderDeliverLocationEvent extends LogEvent {
-
- 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;
- mNumLocations = numLocations;
- mIdentity = identity;
- }
-
- @Override
- public String getLogString() {
- return mProvider + " provider delivered location[" + mNumLocations + "] to "
- + mIdentity;
- }
- }
-
- private static class LocationPowerSaveModeEvent extends LogEvent {
-
- @LocationPowerSaveMode
- private final int mLocationPowerSaveMode;
-
- private LocationPowerSaveModeEvent(long timeDelta,
- @LocationPowerSaveMode int locationPowerSaveMode) {
- super(timeDelta);
- mLocationPowerSaveMode = locationPowerSaveMode;
- }
-
- @Override
- public String getLogString() {
- String mode;
- switch (mLocationPowerSaveMode) {
- case LOCATION_MODE_NO_CHANGE:
- mode = "NO_CHANGE";
- break;
- case LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
- mode = "GPS_DISABLED_WHEN_SCREEN_OFF";
- break;
- case LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF:
- mode = "ALL_DISABLED_WHEN_SCREEN_OFF";
- break;
- case LOCATION_MODE_FOREGROUND_ONLY:
- mode = "FOREGROUND_ONLY";
- break;
- case LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF:
- mode = "THROTTLE_REQUESTS_WHEN_SCREEN_OFF";
- break;
- default:
- mode = "UNKNOWN";
- break;
- }
- return "location power save mode changed to " + mode;
- }
- }
-
- private static class LocationEnabledEvent extends LogEvent {
-
- private final int mUserId;
- private final boolean mEnabled;
-
- private LocationEnabledEvent(long timeDelta, int userId, boolean enabled) {
- super(timeDelta);
- mUserId = userId;
- mEnabled = enabled;
- }
-
- @Override
- public String getLogString() {
- return "[u" + mUserId + "] location setting " + (mEnabled ? "enabled" : "disabled");
- }
- }
-}
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 532826a..cc00d56 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 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 1b74865..c47a64d 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 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 221d4fb..388b5a4 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -55,6 +55,7 @@
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.LocationRequest;
import android.location.LocationResult;
+import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
@@ -88,6 +89,7 @@
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;
@@ -95,7 +97,6 @@
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;
@@ -117,6 +118,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Predicate;
/**
@@ -321,7 +323,7 @@
+ 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,
@@ -331,6 +333,10 @@
mIsUsingHighPower = isUsingHighPower();
onProviderListenerRegister();
+
+ if (mForeground) {
+ mEventLog.logProviderClientForeground(mName, getIdentity());
+ }
}
@GuardedBy("mLock")
@@ -342,7 +348,7 @@
onProviderListenerUnregister();
- mLocationEventLog.logProviderClientUnregistered(mName, getIdentity());
+ mEventLog.logProviderClientUnregistered(mName, getIdentity());
if (D) {
Log.d(TAG, mName + " provider removed registration from " + getIdentity());
@@ -367,6 +373,8 @@
Preconditions.checkState(Thread.holdsLock(mLock));
}
+ mEventLog.logProviderClientActive(mName, getIdentity());
+
if (!getRequest().isHiddenFromAppOps()) {
mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey());
}
@@ -387,6 +395,8 @@
}
onProviderListenerInactive();
+
+ mEventLog.logProviderClientInactive(mName, getIdentity());
}
/**
@@ -522,6 +532,12 @@
mForeground = foreground;
+ if (mForeground) {
+ mEventLog.logProviderClientForeground(mName, getIdentity());
+ } else {
+ mEventLog.logProviderClientBackground(mName, getIdentity());
+ }
+
// note that onProviderLocationRequestChanged() is always called
return onProviderLocationRequestChanged()
|| mLocationPowerSaveModeHelper.getLocationPowerSaveMode()
@@ -853,7 +869,7 @@
listener.deliverOnLocationChanged(deliverLocationResult,
mUseWakeLock ? mWakeLock::release : null);
- mLocationEventLog.logProviderDeliveredLocations(mName, locationResult.size(),
+ mEventLog.logProviderDeliveredLocations(mName, locationResult.size(),
getIdentity());
}
@@ -1152,7 +1168,7 @@
// 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());
}
@@ -1219,6 +1235,9 @@
@GuardedBy("mLock")
private final ArrayList<ProviderEnabledListener> mEnabledListeners;
+ private final CopyOnWriteArrayList<IProviderRequestListener> mProviderRequestListeners;
+
+ protected final LocationEventLog mEventLog;
protected final LocationManagerInternal mLocationManagerInternal;
protected final SettingsHelper mSettingsHelper;
protected final UserInfoHelper mUserHelper;
@@ -1231,7 +1250,6 @@
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 =
@@ -1269,8 +1287,8 @@
@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;
@@ -1279,7 +1297,9 @@
mLastLocations = new SparseArray<>(2);
mEnabledListeners = new ArrayList<>();
+ mProviderRequestListeners = new CopyOnWriteArrayList<>();
+ mEventLog = eventLog;
mLocationManagerInternal = Objects.requireNonNull(
LocalServices.getService(LocationManagerInternal.class));
mSettingsHelper = injector.getSettingsHelper();
@@ -1292,7 +1312,6 @@
mScreenInteractiveHelper = injector.getScreenInteractiveHelper();
mLocationAttributionHelper = injector.getLocationAttributionHelper();
mLocationUsageLogger = injector.getLocationUsageLogger();
- mLocationEventLog = injector.getLocationEventLog();
mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
mProvider = new MockableLocationProvider(mLock);
@@ -1344,6 +1363,7 @@
// if external entities are registering listeners it's their responsibility to
// unregister them before stopManager() is called
Preconditions.checkState(mEnabledListeners.isEmpty());
+ mProviderRequestListeners.clear();
mEnabled.clear();
mLastLocations.clear();
@@ -1404,6 +1424,16 @@
}
}
+ /** Add a {@link IProviderRequestListener}. */
+ public void addProviderRequestListener(IProviderRequestListener listener) {
+ mProviderRequestListeners.add(listener);
+ }
+
+ /** Remove a {@link IProviderRequestListener}. */
+ public void removeProviderRequestListener(IProviderRequestListener listener) {
+ mProviderRequestListeners.remove(listener);
+ }
+
public void setRealProvider(@Nullable AbstractLocationProvider provider) {
synchronized (mLock) {
Preconditions.checkState(mState != STATE_STOPPED);
@@ -1421,7 +1451,7 @@
synchronized (mLock) {
Preconditions.checkState(mState != STATE_STOPPED);
- mLocationEventLog.logProviderMocked(mName, provider != null);
+ mEventLog.logProviderMocked(mName, provider != null);
final long identity = Binder.clearCallingIdentity();
try {
@@ -1873,8 +1903,7 @@
Preconditions.checkState(delayMs >= 0 && delayMs <= newRequest.getIntervalMillis());
if (delayMs < MIN_REQUEST_DELAY_MS) {
- mLocationEventLog.logProviderUpdateRequest(mName, newRequest);
- mProvider.getController().setRequest(newRequest);
+ setProviderRequest(newRequest);
} else {
if (D) {
Log.d(TAG, mName + " provider delaying request update " + newRequest + " by "
@@ -1886,8 +1915,7 @@
public void onAlarm() {
synchronized (mLock) {
if (mDelayedRegister == this) {
- mLocationEventLog.logProviderUpdateRequest(mName, newRequest);
- mProvider.getController().setRequest(newRequest);
+ setProviderRequest(newRequest);
mDelayedRegister = null;
}
}
@@ -1906,8 +1934,23 @@
Preconditions.checkState(Thread.holdsLock(mLock));
}
- mLocationEventLog.logProviderUpdateRequest(mName, ProviderRequest.EMPTY_REQUEST);
- mProvider.getController().setRequest(ProviderRequest.EMPTY_REQUEST);
+ setProviderRequest(ProviderRequest.EMPTY_REQUEST);
+ }
+
+ @GuardedBy("mLock")
+ private void setProviderRequest(ProviderRequest request) {
+ mEventLog.logProviderUpdateRequest(mName, request);
+ mProvider.getController().setRequest(request);
+
+ FgThread.getHandler().post(() -> {
+ for (IProviderRequestListener listener : mProviderRequestListeners) {
+ try {
+ listener.onProviderRequestChanged(mName, request);
+ } catch (RemoteException e) {
+ mProviderRequestListeners.remove(listener);
+ }
+ }
+ });
}
@GuardedBy("mLock")
@@ -2232,7 +2275,7 @@
}
// 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;
@@ -2332,7 +2375,7 @@
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 b35af4f..027f4e9 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.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 @@
*/
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/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index e2e5046..87e170a 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.app.BroadcastOptions;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -58,6 +59,16 @@
private static final String TAG = "PendingIntentHolder";
private static final boolean DEBUG_KEY_EVENT = MediaSessionService.DEBUG_KEY_EVENT;
private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
+ // Filter apps regardless of the phone's locked/unlocked state.
+ private static final int PACKAGE_MANAGER_COMMON_FLAGS =
+ PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+
+ /**
+ * Denotes the duration during which a media button receiver will be exempted from
+ * FGS-from-BG restriction and so will be allowed to start an FGS even if it is in the
+ * background state while it receives a media key event.
+ */
+ private static final long FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS = 10_000;
private final int mUserId;
private final PendingIntent mPendingIntent;
@@ -105,40 +116,22 @@
* @return Can be {@code null} if pending intent was null.
*/
public static MediaButtonReceiverHolder create(Context context, int userId,
- PendingIntent pendingIntent) {
+ PendingIntent pendingIntent, String sessionPackageName) {
if (pendingIntent == null) {
return null;
}
- ComponentName componentName = (pendingIntent != null && pendingIntent.getIntent() != null)
- ? pendingIntent.getIntent().getComponent() : null;
+ int componentType = getComponentType(pendingIntent);
+ ComponentName componentName = getComponentName(pendingIntent, componentType);
if (componentName != null) {
- // Explicit intent, where component name is in the PendingIntent.
return new MediaButtonReceiverHolder(userId, pendingIntent, componentName,
- getComponentType(context, componentName));
- }
-
- // Implicit intent, where component name isn't in the PendingIntent. Try resolve.
- PackageManager pm = context.getPackageManager();
- Intent intent = pendingIntent.getIntent();
- if ((componentName = resolveImplicitServiceIntent(pm, intent)) != null) {
- return new MediaButtonReceiverHolder(
- userId, pendingIntent, componentName, COMPONENT_TYPE_SERVICE);
- } else if ((componentName = resolveManifestDeclaredBroadcastReceiverIntent(pm, intent))
- != null) {
- return new MediaButtonReceiverHolder(
- userId, pendingIntent, componentName, COMPONENT_TYPE_BROADCAST);
- } else if ((componentName = resolveImplicitActivityIntent(pm, intent)) != null) {
- return new MediaButtonReceiverHolder(
- userId, pendingIntent, componentName, COMPONENT_TYPE_ACTIVITY);
+ componentType);
}
// Failed to resolve target component for the pending intent. It's unlikely to be usable.
- // However, the pending intent would be still used, just to follow the legacy behavior.
+ // However, the pending intent would be still used, so setting the package name to the
+ // package name of the session that set this pending intent.
Log.w(TAG, "Unresolvable implicit intent is set, pi=" + pendingIntent);
- String packageName = (pendingIntent != null && pendingIntent.getIntent() != null)
- ? pendingIntent.getIntent().getPackage() : null;
- return new MediaButtonReceiverHolder(userId, pendingIntent,
- packageName != null ? packageName : "");
+ return new MediaButtonReceiverHolder(userId, pendingIntent, sessionPackageName);
}
public static MediaButtonReceiverHolder create(int userId, ComponentName broadcastReceiver) {
@@ -201,6 +194,9 @@
// TODO: Find a way to also send PID/UID in secure way.
mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callingPackageName);
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setTemporaryAppWhitelistDuration(
+ FGS_STARTS_TEMP_ALLOWLIST_DURATION_MS);
if (mPendingIntent != null) {
if (DEBUG_KEY_EVENT) {
Log.d(TAG, "Sending " + keyEvent + " to the last known PendingIntent "
@@ -208,7 +204,8 @@
}
try {
mPendingIntent.send(
- context, resultCode, mediaButtonIntent, onFinishedListener, handler);
+ context, resultCode, mediaButtonIntent, onFinishedListener, handler,
+ /* requiredPermission= */ null, options.toBundle());
} catch (PendingIntent.CanceledException e) {
Log.w(TAG, "Error sending key event to media button receiver " + mPendingIntent, e);
return false;
@@ -231,7 +228,8 @@
break;
default:
// Legacy behavior for other cases.
- context.sendBroadcastAsUser(mediaButtonIntent, userHandle);
+ context.sendBroadcastAsUser(mediaButtonIntent, userHandle,
+ /* receiverPermission= */ null, options.toBundle());
}
} catch (Exception e) {
Log.w(TAG, "Error sending media button to the restored intent "
@@ -269,6 +267,18 @@
String.valueOf(mComponentType));
}
+ @ComponentType
+ private static int getComponentType(PendingIntent pendingIntent) {
+ if (pendingIntent.isBroadcast()) {
+ return COMPONENT_TYPE_BROADCAST;
+ } else if (pendingIntent.isActivity()) {
+ return COMPONENT_TYPE_ACTIVITY;
+ } else if (pendingIntent.isForegroundService() || pendingIntent.isService()) {
+ return COMPONENT_TYPE_SERVICE;
+ }
+ return COMPONENT_TYPE_INVALID;
+ }
+
/**
* Gets the type of the component
*
@@ -284,9 +294,7 @@
PackageManager pm = context.getPackageManager();
try {
ActivityInfo activityInfo = pm.getActivityInfo(componentName,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.GET_ACTIVITIES);
+ PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_ACTIVITIES);
if (activityInfo != null) {
return COMPONENT_TYPE_ACTIVITY;
}
@@ -294,9 +302,7 @@
}
try {
ServiceInfo serviceInfo = pm.getServiceInfo(componentName,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.GET_SERVICES);
+ PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_SERVICES);
if (serviceInfo != null) {
return COMPONENT_TYPE_SERVICE;
}
@@ -306,40 +312,29 @@
return COMPONENT_TYPE_BROADCAST;
}
- private static ComponentName resolveImplicitServiceIntent(PackageManager pm, Intent intent) {
- // Flag explanations.
- // - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE:
- // filter apps regardless of the phone's locked/unlocked state.
- // - GET_SERVICES: Return service
- return createComponentName(pm.resolveService(intent,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.GET_SERVICES));
- }
-
- private static ComponentName resolveManifestDeclaredBroadcastReceiverIntent(
- PackageManager pm, Intent intent) {
- // Flag explanations.
- // - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE:
- // filter apps regardless of the phone's locked/unlocked state.
- List<ResolveInfo> resolveInfos = pm.queryBroadcastReceivers(intent,
- PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
- return (resolveInfos != null && !resolveInfos.isEmpty())
- ? createComponentName(resolveInfos.get(0)) : null;
- }
-
- private static ComponentName resolveImplicitActivityIntent(PackageManager pm, Intent intent) {
- // Flag explanations.
- // - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE:
- // Filter apps regardless of the phone's locked/unlocked state.
- // - MATCH_DEFAULT_ONLY:
- // Implicit intent receiver should be set as default. Only needed for activity.
- // - GET_ACTIVITIES: Return activity
- return createComponentName(pm.resolveActivity(intent,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_DEFAULT_ONLY
- | PackageManager.GET_ACTIVITIES));
+ private static ComponentName getComponentName(PendingIntent pendingIntent, int componentType) {
+ List<ResolveInfo> resolveInfos = null;
+ switch (componentType) {
+ case COMPONENT_TYPE_ACTIVITY:
+ resolveInfos = pendingIntent.queryIntentComponents(
+ PACKAGE_MANAGER_COMMON_FLAGS
+ | PackageManager.MATCH_DEFAULT_ONLY /* Implicit intent receiver
+ should be set as default. Only needed for activity. */
+ | PackageManager.GET_ACTIVITIES);
+ break;
+ case COMPONENT_TYPE_SERVICE:
+ resolveInfos = pendingIntent.queryIntentComponents(
+ PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_SERVICES);
+ break;
+ case COMPONENT_TYPE_BROADCAST:
+ resolveInfos = pendingIntent.queryIntentComponents(
+ PACKAGE_MANAGER_COMMON_FLAGS | PackageManager.GET_RECEIVERS);
+ break;
+ }
+ if (resolveInfos != null && !resolveInfos.isEmpty()) {
+ return createComponentName(resolveInfos.get(0));
+ }
+ return null;
}
private static ComponentName createComponentName(ResolveInfo resolveInfo) {
diff --git a/services/core/java/com/android/server/media/MediaKeyDispatcher.java b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
index 63618ee..7ef4924 100644
--- a/services/core/java/com/android/server/media/MediaKeyDispatcher.java
+++ b/services/core/java/com/android/server/media/MediaKeyDispatcher.java
@@ -100,9 +100,12 @@
/**
* Implement this to customize the logic for which MediaButtonReceiver should consume a
* dispatched key event.
- *
- * Note: This pending intent will have lower priority over the {@link MediaSession.Token}
+ * <p>
+ * This pending intent will have lower priority over the {@link MediaSession.Token}
* returned from {@link #getMediaSession(KeyEvent, int, boolean)}.
+ * <p>
+ * Use a pending intent with an explicit intent; setting a pending intent with an implicit
+ * intent that cannot be resolved to a certain component name will fail.
*
* @return a {@link PendingIntent} instance that should receive the dispatched key event.
*/
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index ae58d4c..74111be 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -843,7 +843,8 @@
}
@Override
- public void setMediaButtonReceiver(PendingIntent pi) throws RemoteException {
+ public void setMediaButtonReceiver(PendingIntent pi, String sessionPackageName)
+ throws RemoteException {
final long token = Binder.clearCallingIdentity();
try {
if ((mPolicies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
@@ -851,7 +852,7 @@
return;
}
mMediaButtonReceiverHolder =
- MediaButtonReceiverHolder.create(mContext, mUserId, pi);
+ MediaButtonReceiverHolder.create(mContext, mUserId, pi, sessionPackageName);
mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
} finally {
Binder.restoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index c0381e4..6c1d399 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -2124,7 +2124,8 @@
uid, asSystemService);
if (pi != null) {
mediaButtonReceiverHolder = MediaButtonReceiverHolder.create(mContext,
- mCurrentFullUserRecord.mFullUserId, pi);
+ mCurrentFullUserRecord.mFullUserId, pi,
+ /* sessionPackageName= */ "");
}
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index b99a552..aa7da54 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1961,14 +1961,13 @@
if (state.network != null) {
mNetIdToSubId.put(state.network.netId, parseSubId(state));
}
- if (state.networkInfo != null && state.networkInfo.isConnected()) {
- // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype
- // in the object created here is never used and its value doesn't matter, so use
- // NETWORK_TYPE_UNKNOWN.
- final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
- true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */);
- identified.put(state, ident);
- }
+
+ // Policies matched by NPMS only match by subscriber ID or by ssid. Thus subtype
+ // in the object created here is never used and its value doesn't matter, so use
+ // NETWORK_TYPE_UNKNOWN.
+ final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
+ true, TelephonyManager.NETWORK_TYPE_UNKNOWN /* subType */);
+ identified.put(state, ident);
}
final ArraySet<String> newMeteredIfaces = new ArraySet<>();
@@ -2043,8 +2042,7 @@
// One final pass to catch any metered ifaces that don't have explicitly
// defined policies; typically Wi-Fi networks.
for (NetworkState state : states) {
- if (state.networkInfo != null && state.networkInfo.isConnected()
- && !state.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+ if (!state.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
matchingIfaces.clear();
collectIfaces(matchingIfaces, state);
for (int j = matchingIfaces.size() - 1; j >= 0; j--) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 0ab35a9..9706bce 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -96,7 +96,6 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkIdentity;
-import android.net.NetworkInfo;
import android.net.NetworkStack;
import android.net.NetworkState;
import android.net.NetworkStats;
@@ -1264,7 +1263,7 @@
/**
* Inspect all current {@link NetworkState} to derive mapping from {@code iface} to {@link
- * NetworkStatsHistory}. When multiple {@link NetworkInfo} are active on a single {@code iface},
+ * NetworkStatsHistory}. When multiple networks are active on a single {@code iface},
* they are combined under a single {@link NetworkIdentitySet}.
*/
@GuardedBy("mStatsLock")
@@ -1294,84 +1293,82 @@
final boolean combineSubtypeEnabled = mSettings.getCombineSubtypeEnabled();
final ArraySet<String> mobileIfaces = new ArraySet<>();
for (NetworkState state : states) {
- if (state.networkInfo.isConnected()) {
- final boolean isMobile = isNetworkTypeMobile(state.networkInfo.getType());
- final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network);
- final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
- : getSubTypeForState(state);
- final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
- isDefault, subType);
+ final boolean isMobile = isNetworkTypeMobile(state.legacyNetworkType);
+ final boolean isDefault = ArrayUtils.contains(mDefaultNetworks, state.network);
+ final int subType = combineSubtypeEnabled ? SUBTYPE_COMBINED
+ : getSubTypeForState(state);
+ final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state,
+ isDefault, subType);
- // Traffic occurring on the base interface is always counted for
- // both total usage and UID details.
- final String baseIface = state.linkProperties.getInterfaceName();
- if (baseIface != null) {
- findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
- findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
+ // Traffic occurring on the base interface is always counted for
+ // both total usage and UID details.
+ final String baseIface = state.linkProperties.getInterfaceName();
+ if (baseIface != null) {
+ findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
+ findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
- // Build a separate virtual interface for VT (Video Telephony) data usage.
- // Only do this when IMS is not metered, but VT is metered.
- // If IMS is metered, then the IMS network usage has already included VT usage.
- // VT is considered always metered in framework's layer. If VT is not metered
- // per carrier's policy, modem will report 0 usage for VT calls.
- if (state.networkCapabilities.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.getMetered()) {
+ // Build a separate virtual interface for VT (Video Telephony) data usage.
+ // Only do this when IMS is not metered, but VT is metered.
+ // If IMS is metered, then the IMS network usage has already included VT usage.
+ // VT is considered always metered in framework's layer. If VT is not metered
+ // per carrier's policy, modem will report 0 usage for VT calls.
+ if (state.networkCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.getMetered()) {
- // Copy the identify from IMS one but mark it as metered.
- NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
- ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
- ident.getRoaming(), true /* metered */,
- true /* onDefaultNetwork */);
- findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent);
- findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent);
- }
-
- if (isMobile) {
- mobileIfaces.add(baseIface);
- }
+ // Copy the identify from IMS one but mark it as metered.
+ NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
+ ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
+ ident.getRoaming(), true /* metered */,
+ true /* onDefaultNetwork */);
+ findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent);
+ findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent);
}
- // Traffic occurring on stacked interfaces is usually clatd.
- //
- // UID stats are always counted on the stacked interface and never on the base
- // interface, because the packets on the base interface do not actually match
- // application sockets (they're not IPv4) and thus the app uid is not known.
- // For receive this is obvious: packets must be translated from IPv6 to IPv4
- // before the application socket can be found.
- // For transmit: either they go through the clat daemon which by virtue of going
- // through userspace strips the original socket association during the IPv4 to
- // IPv6 translation process, or they are offloaded by eBPF, which doesn't:
- // However, on an ebpf device the accounting is done in cgroup ebpf hooks,
- // which don't trigger again post ebpf translation.
- // (as such stats accounted to the clat uid are ignored)
- //
- // Interface stats are more complicated.
- //
- // eBPF offloaded 464xlat'ed packets never hit base interface ip6tables, and thus
- // *all* statistics are collected by iptables on the stacked v4-* interface.
- //
- // Additionally for ingress all packets bound for the clat IPv6 address are dropped
- // in ip6tables raw prerouting and thus even non-offloaded packets are only
- // accounted for on the stacked interface.
- //
- // For egress, packets subject to eBPF offload never appear on the base interface
- // and only appear on the stacked interface. Thus to ensure packets increment
- // interface stats, we must collate data from stacked interfaces. For xt_qtaguid
- // (or non eBPF offloaded) TX they would appear on both, however egress interface
- // accounting is explicitly bypassed for traffic from the clat uid.
- //
- final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
- for (LinkProperties stackedLink : stackedLinks) {
- final String stackedIface = stackedLink.getInterfaceName();
- if (stackedIface != null) {
- findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident);
- findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
- if (isMobile) {
- mobileIfaces.add(stackedIface);
- }
+ if (isMobile) {
+ mobileIfaces.add(baseIface);
+ }
+ }
- mStatsFactory.noteStackedIface(stackedIface, baseIface);
+ // Traffic occurring on stacked interfaces is usually clatd.
+ //
+ // UID stats are always counted on the stacked interface and never on the base
+ // interface, because the packets on the base interface do not actually match
+ // application sockets (they're not IPv4) and thus the app uid is not known.
+ // For receive this is obvious: packets must be translated from IPv6 to IPv4
+ // before the application socket can be found.
+ // For transmit: either they go through the clat daemon which by virtue of going
+ // through userspace strips the original socket association during the IPv4 to
+ // IPv6 translation process, or they are offloaded by eBPF, which doesn't:
+ // However, on an ebpf device the accounting is done in cgroup ebpf hooks,
+ // which don't trigger again post ebpf translation.
+ // (as such stats accounted to the clat uid are ignored)
+ //
+ // Interface stats are more complicated.
+ //
+ // eBPF offloaded 464xlat'ed packets never hit base interface ip6tables, and thus
+ // *all* statistics are collected by iptables on the stacked v4-* interface.
+ //
+ // Additionally for ingress all packets bound for the clat IPv6 address are dropped
+ // in ip6tables raw prerouting and thus even non-offloaded packets are only
+ // accounted for on the stacked interface.
+ //
+ // For egress, packets subject to eBPF offload never appear on the base interface
+ // and only appear on the stacked interface. Thus to ensure packets increment
+ // interface stats, we must collate data from stacked interfaces. For xt_qtaguid
+ // (or non eBPF offloaded) TX they would appear on both, however egress interface
+ // accounting is explicitly bypassed for traffic from the clat uid.
+ //
+ final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
+ for (LinkProperties stackedLink : stackedLinks) {
+ final String stackedIface = stackedLink.getInterfaceName();
+ if (stackedIface != null) {
+ findOrCreateNetworkIdentitySet(mActiveIfaces, stackedIface).add(ident);
+ findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
+ if (isMobile) {
+ mobileIfaces.add(stackedIface);
}
+
+ mStatsFactory.noteStackedIface(stackedIface, baseIface);
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6843733..4c3dfbf 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 @@
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,
@@ -3041,7 +3043,8 @@
}
Binder windowToken = new Binder();
- mWindowManagerInternal.addWindowToken(windowToken, TYPE_TOAST, displayId);
+ mWindowManagerInternal.addWindowToken(windowToken, TYPE_TOAST, displayId,
+ null /* options */);
record = getToastRecord(callingUid, callingPid, pkg, isSystemToast, token,
text, callback, duration, windowToken, displayId, textCallback);
mToastQueue.add(record);
@@ -9047,7 +9050,8 @@
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();
@@ -9149,13 +9153,19 @@
@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));
+ }
}
}
}
@@ -10103,7 +10113,9 @@
}
BackgroundThread.getHandler().post(() -> {
- if (info.isSystem || hasCompanionDevice(info)) {
+ if (info.isSystem
+ || hasCompanionDevice(info)
+ || mAssistants.isServiceTokenValidLocked(info.service)) {
notifyNotificationChannelChanged(
info, pkg, user, channel, modificationType);
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 50fb176..fd2fb1f 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.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 @@
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 @@
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 e60411bb..c547c36 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.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 @@
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/os/NativeTombstoneManager.java b/services/core/java/com/android/server/os/NativeTombstoneManager.java
new file mode 100644
index 0000000..a83edb7
--- /dev/null
+++ b/services/core/java/com/android/server/os/NativeTombstoneManager.java
@@ -0,0 +1,241 @@
+/*
+ * 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.os;
+
+import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+
+import android.annotation.AppIdInt;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.os.FileObserver;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.proto.ProtoInputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.BootReceiver;
+import com.android.server.ServiceThread;
+import com.android.server.os.TombstoneProtos.Tombstone;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Optional;
+
+/**
+ * A class to manage native tombstones.
+ */
+public final class NativeTombstoneManager {
+ private static final String TAG = NativeTombstoneManager.class.getSimpleName();
+
+ private static final File TOMBSTONE_DIR = new File("/data/tombstones");
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final TombstoneWatcher mWatcher;
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final SparseArray<TombstoneFile> mTombstones;
+
+ NativeTombstoneManager(Context context) {
+ mTombstones = new SparseArray<TombstoneFile>();
+ mContext = context;
+
+ final ServiceThread thread = new ServiceThread(TAG + ":tombstoneWatcher",
+ THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
+ thread.start();
+ mHandler = thread.getThreadHandler();
+
+ mWatcher = new TombstoneWatcher();
+ mWatcher.startWatching();
+ }
+
+ void onSystemReady() {
+ // Scan existing tombstones.
+ mHandler.post(() -> {
+ final File[] tombstoneFiles = TOMBSTONE_DIR.listFiles();
+ for (int i = 0; tombstoneFiles != null && i < tombstoneFiles.length; i++) {
+ if (tombstoneFiles[i].isFile()) {
+ handleTombstone(tombstoneFiles[i]);
+ }
+ }
+ });
+ }
+
+ private void handleTombstone(File path) {
+ final String filename = path.getName();
+ if (!filename.startsWith("tombstone_")) {
+ return;
+ }
+
+ if (filename.endsWith(".pb")) {
+ handleProtoTombstone(path);
+ } else {
+ BootReceiver.addTombstoneToDropBox(mContext, path);
+ }
+ }
+
+ private void handleProtoTombstone(File path) {
+ final String filename = path.getName();
+ if (!filename.endsWith(".pb")) {
+ Slog.w(TAG, "unexpected tombstone name: " + path);
+ return;
+ }
+
+ final String suffix = filename.substring("tombstone_".length());
+ final String numberStr = suffix.substring(0, suffix.length() - 3);
+
+ int number;
+ try {
+ number = Integer.parseInt(numberStr);
+ if (number < 0 || number > 99) {
+ Slog.w(TAG, "unexpected tombstone name: " + path);
+ return;
+ }
+ } catch (NumberFormatException ex) {
+ Slog.w(TAG, "unexpected tombstone name: " + path);
+ return;
+ }
+
+ ParcelFileDescriptor pfd;
+ try {
+ pfd = ParcelFileDescriptor.open(path, MODE_READ_WRITE);
+ } catch (FileNotFoundException ex) {
+ Slog.w(TAG, "failed to open " + path, ex);
+ return;
+ }
+
+ final Optional<TombstoneFile> parsedTombstone = TombstoneFile.parse(pfd);
+ if (!parsedTombstone.isPresent()) {
+ IoUtils.closeQuietly(pfd);
+ return;
+ }
+
+ synchronized (mLock) {
+ TombstoneFile previous = mTombstones.get(number);
+ if (previous != null) {
+ previous.dispose();
+ }
+
+ mTombstones.put(number, parsedTombstone.get());
+ }
+ }
+
+ static class TombstoneFile {
+ final ParcelFileDescriptor mPfd;
+
+ final @UserIdInt int mUserId;
+ final @AppIdInt int mAppId;
+
+ boolean mPurged = false;
+
+ TombstoneFile(ParcelFileDescriptor pfd, @UserIdInt int userId, @AppIdInt int appId) {
+ mPfd = pfd;
+ mUserId = userId;
+ mAppId = appId;
+ }
+
+ public boolean matches(Optional<Integer> userId, Optional<Integer> appId) {
+ if (mPurged) {
+ return false;
+ }
+
+ if (userId.isPresent() && userId.get() != mUserId) {
+ return false;
+ }
+
+ if (appId.isPresent() && appId.get() != mAppId) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public void dispose() {
+ IoUtils.closeQuietly(mPfd);
+ }
+
+ static Optional<TombstoneFile> parse(ParcelFileDescriptor pfd) {
+ final FileInputStream is = new FileInputStream(pfd.getFileDescriptor());
+ final ProtoInputStream stream = new ProtoInputStream(is);
+
+ int uid = 0;
+ String selinuxLabel = "";
+
+ try {
+ while (stream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (stream.getFieldNumber()) {
+ case (int) Tombstone.UID:
+ uid = stream.readInt(Tombstone.UID);
+ break;
+
+ case (int) Tombstone.SELINUX_LABEL:
+ selinuxLabel = stream.readString(Tombstone.SELINUX_LABEL);
+ break;
+
+ default:
+ break;
+ }
+ }
+ } catch (IOException ex) {
+ Slog.e(TAG, "Failed to parse tombstone", ex);
+ return Optional.empty();
+ }
+
+ if (!UserHandle.isApp(uid)) {
+ Slog.e(TAG, "Tombstone's UID (" + uid + ") not an app, ignoring");
+ return Optional.empty();
+ }
+
+ final int userId = UserHandle.getUserId(uid);
+ final int appId = UserHandle.getAppId(uid);
+
+ if (!selinuxLabel.startsWith("u:r:untrusted_app")) {
+ Slog.e(TAG, "Tombstone has invalid selinux label (" + selinuxLabel + "), ignoring");
+ return Optional.empty();
+ }
+
+ return Optional.of(new TombstoneFile(pfd, userId, appId));
+ }
+ }
+
+ class TombstoneWatcher extends FileObserver {
+ TombstoneWatcher() {
+ // Tombstones can be created either by linking an O_TMPFILE temporary file (CREATE),
+ // or by moving a named temporary file in the same directory on kernels where O_TMPFILE
+ // isn't supported (MOVED_TO).
+ super(TOMBSTONE_DIR, FileObserver.CREATE | FileObserver.MOVED_TO);
+ }
+
+ @Override
+ public void onEvent(int event, @Nullable String path) {
+ mHandler.post(() -> {
+ handleTombstone(new File(TOMBSTONE_DIR, path));
+ });
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/os/NativeTombstoneManagerService.java b/services/core/java/com/android/server/os/NativeTombstoneManagerService.java
new file mode 100644
index 0000000..cb3c7ff0
--- /dev/null
+++ b/services/core/java/com/android/server/os/NativeTombstoneManagerService.java
@@ -0,0 +1,50 @@
+/*
+ * 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.os;
+
+import android.content.Context;
+
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
+/**
+ * Service that tracks and manages native tombstones.
+ *
+ * @hide
+ */
+public class NativeTombstoneManagerService extends SystemService {
+ private static final String TAG = "NativeTombstoneManagerService";
+
+ private NativeTombstoneManager mManager;
+
+ public NativeTombstoneManagerService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mManager = new NativeTombstoneManager(getContext());
+ LocalServices.addService(NativeTombstoneManager.class, mManager);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ mManager.onSystemReady();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 5373f99..66ea554 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -23,7 +23,6 @@
import static android.content.pm.Checksum.TYPE_WHOLE_SHA1;
import static android.content.pm.Checksum.TYPE_WHOLE_SHA256;
import static android.content.pm.Checksum.TYPE_WHOLE_SHA512;
-import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
import static android.content.pm.parsing.ApkLiteParseUtils.APK_FILE_EXTENSION;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
@@ -32,15 +31,15 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
import android.content.pm.ApkChecksum;
import android.content.pm.Checksum;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
import android.content.pm.Signature;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.os.Handler;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.incremental.IncrementalManager;
import android.os.incremental.IncrementalStorage;
@@ -294,21 +293,21 @@
/**
* Fetch or calculate checksums for the collection of files.
*
- * @param filesToChecksum split name, null for base and File to fetch checksums for
- * @param optional mask to fetch readily available checksums
- * @param required mask to forcefully calculate if not available
- * @param installerPackageName package name of the installer of the packages
- * @param trustedInstallers array of certificate to trust, two specific cases:
- * null - trust anybody,
- * [] - trust nobody.
- * @param statusReceiver to receive the resulting checksums
+ * @param filesToChecksum split name, null for base and File to fetch checksums for
+ * @param optional mask to fetch readily available checksums
+ * @param required mask to forcefully calculate if not available
+ * @param installerPackageName package name of the installer of the packages
+ * @param trustedInstallers array of certificate to trust, two specific cases:
+ * null - trust anybody,
+ * [] - trust nobody.
+ * @param onChecksumsReadyListener to receive the resulting checksums
*/
public static void getChecksums(List<Pair<String, File>> filesToChecksum,
@Checksum.Type int optional,
@Checksum.Type int required,
@Nullable String installerPackageName,
@Nullable Certificate[] trustedInstallers,
- @NonNull IntentSender statusReceiver,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener,
@NonNull Injector injector) {
List<Map<Integer, ApkChecksum>> result = new ArrayList<>(filesToChecksum.size());
for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
@@ -326,14 +325,14 @@
}
long startTime = SystemClock.uptimeMillis();
- processRequiredChecksums(filesToChecksum, result, required, statusReceiver, injector,
- startTime);
+ processRequiredChecksums(filesToChecksum, result, required, onChecksumsReadyListener,
+ injector, startTime);
}
private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum,
List<Map<Integer, ApkChecksum>> result,
@Checksum.Type int required,
- @NonNull IntentSender statusReceiver,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener,
@NonNull Injector injector,
long startTime) {
final boolean timeout =
@@ -350,7 +349,7 @@
// Not ready, come back later.
injector.getHandler().postDelayed(() -> {
processRequiredChecksums(filesToChecksum, result, required,
- statusReceiver, injector, startTime);
+ onChecksumsReadyListener, injector, startTime);
}, PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS);
return;
}
@@ -363,13 +362,9 @@
}
}
- final Intent intent = new Intent();
- intent.putExtra(EXTRA_CHECKSUMS,
- allChecksums.toArray(new ApkChecksum[allChecksums.size()]));
-
try {
- statusReceiver.sendIntent(injector.getContext(), 1, intent, null, null);
- } catch (IntentSender.SendIntentException e) {
+ onChecksumsReadyListener.onChecksumsReady(allChecksums);
+ } catch (RemoteException e) {
Slog.w(TAG, e);
}
}
diff --git a/services/core/java/com/android/server/pm/DefaultAppProvider.java b/services/core/java/com/android/server/pm/DefaultAppProvider.java
index a17967f..c18d0e9 100644
--- a/services/core/java/com/android/server/pm/DefaultAppProvider.java
+++ b/services/core/java/com/android/server/pm/DefaultAppProvider.java
@@ -41,14 +41,18 @@
public class DefaultAppProvider {
@NonNull
private final Supplier<RoleManager> mRoleManagerSupplier;
+ @NonNull
+ private final Supplier<UserManagerInternal> mUserManagerInternalSupplier;
/**
* Create a new instance of this class
*
* @param roleManagerSupplier the supplier for {@link RoleManager}
*/
- public DefaultAppProvider(@NonNull Supplier<RoleManager> roleManagerSupplier) {
+ public DefaultAppProvider(@NonNull Supplier<RoleManager> roleManagerSupplier,
+ @NonNull Supplier<UserManagerInternal> userManagerInternalSupplier) {
mRoleManagerSupplier = roleManagerSupplier;
+ mUserManagerInternalSupplier = userManagerInternalSupplier;
}
/**
@@ -132,7 +136,8 @@
*/
@Nullable
public String getDefaultHome(@NonNull int userId) {
- return getRoleHolder(RoleManager.ROLE_HOME, userId);
+ return getRoleHolder(RoleManager.ROLE_HOME,
+ mUserManagerInternalSupplier.get().getProfileParentId(userId));
}
/**
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
new file mode 100644
index 0000000..a32e107
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
@@ -0,0 +1,118 @@
+/*
+ * 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;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
+import android.content.IntentFilter;
+
+import com.android.internal.annotations.Immutable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Representation of an immutable default cross-profile intent filter.
+ */
+@Immutable
+final class DefaultCrossProfileIntentFilter {
+
+ @IntDef({
+ Direction.TO_PARENT,
+ Direction.TO_PROFILE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Direction {
+ int TO_PARENT = 0;
+ int TO_PROFILE = 1;
+ }
+
+ /** The intent filter that's used */
+ public final IntentFilter filter;
+
+ /**
+ * The flags related to the forwarding, e.g.
+ * {@link android.content.pm.PackageManager#SKIP_CURRENT_PROFILE} or
+ * {@link android.content.pm.PackageManager#ONLY_IF_NO_MATCH_FOUND}.
+ */
+ public final int flags;
+
+ /**
+ * The direction of forwarding, can be either {@link Direction#TO_PARENT} or
+ * {@link Direction#TO_PROFILE}.
+ */
+ public final @Direction int direction;
+
+ /**
+ * Whether this cross profile intent filter would allow personal data to be shared into
+ * the work profile. If this is {@code true}, this intent filter should be only added to
+ * the profile if the admin does not enable
+ * {@link android.os.UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE}.
+ */
+ public final boolean letsPersonalDataIntoProfile;
+
+ private DefaultCrossProfileIntentFilter(IntentFilter filter, int flags,
+ @Direction int direction, boolean letsPersonalDataIntoProfile) {
+ this.filter = requireNonNull(filter);
+ this.flags = flags;
+ this.direction = direction;
+ this.letsPersonalDataIntoProfile = letsPersonalDataIntoProfile;
+ }
+
+ static final class Builder {
+ private IntentFilter mFilter = new IntentFilter();
+ private int mFlags;
+ private @Direction int mDirection;
+ private boolean mLetsPersonalDataIntoProfile;
+
+ Builder(@Direction int direction, int flags, boolean letsPersonalDataIntoProfile) {
+ mDirection = direction;
+ mFlags = flags;
+ mLetsPersonalDataIntoProfile = letsPersonalDataIntoProfile;
+ }
+
+ Builder addAction(String action) {
+ mFilter.addAction(action);
+ return this;
+ }
+
+ Builder addCategory(String category) {
+ mFilter.addCategory(category);
+ return this;
+ }
+
+ Builder addDataType(String type) {
+ try {
+ mFilter.addDataType(type);
+ } catch (IntentFilter.MalformedMimeTypeException e) {
+ // ignore
+ }
+ return this;
+ }
+
+ Builder addDataScheme(String scheme) {
+ mFilter.addDataScheme(scheme);
+ return this;
+ }
+
+ DefaultCrossProfileIntentFilter build() {
+ return new DefaultCrossProfileIntentFilter(mFilter, mFlags, mDirection,
+ mLetsPersonalDataIntoProfile);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
new file mode 100644
index 0000000..3019439
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -0,0 +1,299 @@
+/*
+ * 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;
+
+import static android.content.pm.PackageManager.ONLY_IF_NO_MATCH_FOUND;
+import static android.content.pm.PackageManager.SKIP_CURRENT_PROFILE;
+import static android.speech.RecognizerIntent.ACTION_RECOGNIZE_SPEECH;
+
+import android.content.Intent;
+import android.hardware.usb.UsbManager;
+import android.provider.AlarmClock;
+import android.provider.MediaStore;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Utility Class for {@link DefaultCrossProfileIntentFilter}.
+ */
+public class DefaultCrossProfileIntentFiltersUtils {
+
+ private DefaultCrossProfileIntentFiltersUtils() {
+ }
+
+ // Intents from profile to parent user
+ /** Emergency call intent with mime type is always resolved by primary user. */
+ private static final DefaultCrossProfileIntentFilter
+ EMERGENCY_CALL_MIME =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_CALL_EMERGENCY)
+ .addAction(Intent.ACTION_CALL_PRIVILEGED)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataType("vnd.android.cursor.item/phone")
+ .addDataType("vnd.android.cursor.item/phone_v2")
+ .addDataType("vnd.android.cursor.item/person")
+ .addDataType("vnd.android.cursor.dir/calls")
+ .addDataType("vnd.android.cursor.item/calls")
+ .build();
+
+ /** Emergency call intent with data schemes is always resolved by primary user. */
+ private static final DefaultCrossProfileIntentFilter
+ EMERGENCY_CALL_DATA =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_CALL_EMERGENCY)
+ .addAction(Intent.ACTION_CALL_PRIVILEGED)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataScheme("tel")
+ .addDataScheme("sip")
+ .addDataScheme("voicemail")
+ .build();
+
+ /** Dial intent with mime type can be handled by either managed profile or its parent user. */
+ private static final DefaultCrossProfileIntentFilter DIAL_MIME =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_DIAL)
+ .addAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataType("vnd.android.cursor.item/phone")
+ .addDataType("vnd.android.cursor.item/phone_v2")
+ .addDataType("vnd.android.cursor.item/person")
+ .addDataType("vnd.android.cursor.dir/calls")
+ .addDataType("vnd.android.cursor.item/calls")
+ .build();
+
+ /** Dial intent with data scheme can be handled by either managed profile or its parent user. */
+ private static final DefaultCrossProfileIntentFilter DIAL_DATA =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_DIAL)
+ .addAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataScheme("tel")
+ .addDataScheme("sip")
+ .addDataScheme("voicemail")
+ .build();
+
+ /**
+ * Dial intent with no data scheme or type can be handled by either managed profile or its
+ * parent user.
+ */
+ private static final DefaultCrossProfileIntentFilter DIAL_RAW =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_DIAL)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .build();
+
+ /** Pressing the call button can be handled by either managed profile or its parent user. */
+ private static final DefaultCrossProfileIntentFilter CALL_BUTTON =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ ONLY_IF_NO_MATCH_FOUND,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_CALL_BUTTON)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** SMS and MMS are exclusively handled by the primary user. */
+ private static final DefaultCrossProfileIntentFilter SMS_MMS =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_VIEW)
+ .addAction(Intent.ACTION_SENDTO)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addDataScheme("sms")
+ .addDataScheme("smsto")
+ .addDataScheme("mms")
+ .addDataScheme("mmsto")
+ .build();
+
+ /** Mobile network settings is always shown in the primary user. */
+ private static final DefaultCrossProfileIntentFilter
+ MOBILE_NETWORK_SETTINGS =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(android.provider.Settings.ACTION_DATA_ROAMING_SETTINGS)
+ .addAction(android.provider.Settings.ACTION_NETWORK_OPERATOR_SETTINGS)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** HOME intent is always resolved by the primary user. */
+ static final DefaultCrossProfileIntentFilter HOME =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ SKIP_CURRENT_PROFILE,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_HOME)
+ .build();
+
+ /** Get content can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter GET_CONTENT =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_GET_CONTENT)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .addDataType("*/*")
+ .build();
+
+ /** Open document intent can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter OPEN_DOCUMENT =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_OPEN_DOCUMENT)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .addDataType("*/*")
+ .build();
+
+ /** Pick for any data type can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter ACTION_PICK_DATA =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_PICK)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addDataType("*/*")
+ .build();
+
+ /** Pick without data type can be forwarded to parent user. */
+ private static final DefaultCrossProfileIntentFilter ACTION_PICK_RAW =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_PICK)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** Speech recognition can be performed by primary user. */
+ private static final DefaultCrossProfileIntentFilter RECOGNIZE_SPEECH =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(ACTION_RECOGNIZE_SPEECH)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** Media capture can be performed by primary user. */
+ private static final DefaultCrossProfileIntentFilter MEDIA_CAPTURE =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(MediaStore.ACTION_IMAGE_CAPTURE)
+ .addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE)
+ .addAction(MediaStore.ACTION_VIDEO_CAPTURE)
+ .addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
+ .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
+ .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
+ .addAction(MediaStore.INTENT_ACTION_VIDEO_CAMERA)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ /** Alarm setting can be performed by primary user. */
+ private static final DefaultCrossProfileIntentFilter SET_ALARM =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(AlarmClock.ACTION_SET_ALARM)
+ .addAction(AlarmClock.ACTION_SHOW_ALARMS)
+ .addAction(AlarmClock.ACTION_SET_TIMER)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ // Intents from parent to profile user
+
+ /** ACTION_SEND can be forwarded to the managed profile on user's choice. */
+ private static final DefaultCrossProfileIntentFilter ACTION_SEND =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ true)
+ .addAction(Intent.ACTION_SEND)
+ .addAction(Intent.ACTION_SEND_MULTIPLE)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .addDataType("*/*")
+ .build();
+
+ /** USB devices attached can get forwarded to the profile. */
+ private static final DefaultCrossProfileIntentFilter
+ USB_DEVICE_ATTACHED =
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */ false)
+ .addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED)
+ .addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .build();
+
+ public static List<DefaultCrossProfileIntentFilter> getDefaultManagedProfileFilters() {
+ return Arrays.asList(
+ EMERGENCY_CALL_MIME,
+ EMERGENCY_CALL_DATA,
+ DIAL_MIME,
+ DIAL_DATA,
+ DIAL_RAW,
+ CALL_BUTTON,
+ SMS_MMS,
+ SET_ALARM,
+ MEDIA_CAPTURE,
+ RECOGNIZE_SPEECH,
+ ACTION_PICK_RAW,
+ ACTION_PICK_DATA,
+ OPEN_DOCUMENT,
+ GET_CONTENT,
+ USB_DEVICE_ATTACHED,
+ ACTION_SEND,
+ HOME,
+ MOBILE_NETWORK_SETTINGS);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/IncrementalStates.java b/services/core/java/com/android/server/pm/IncrementalStates.java
index f5ec595..ecafdfd 100644
--- a/services/core/java/com/android/server/pm/IncrementalStates.java
+++ b/services/core/java/com/android/server/pm/IncrementalStates.java
@@ -249,24 +249,6 @@
}
/**
- * @return the current startable state.
- */
- public boolean isStartable() {
- synchronized (mLock) {
- return mStartableState.isStartable();
- }
- }
-
- /**
- * @return Whether the package is still being loaded or has been fully loaded.
- */
- public boolean isLoading() {
- synchronized (mLock) {
- return mLoadingState.isLoading();
- }
- }
-
- /**
* @return all current states in a Parcelable.
*/
public IncrementalStatesInfo getIncrementalStatesInfo() {
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 15e1d52..7bf7042 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.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 @@
private final CookiePersistence mCookiePersistence;
/** State for uninstalled instant apps */
+ @Watched
@GuardedBy("mService.mLock")
private final WatchedSparseArray<List<UninstalledInstantAppState>> mUninstalledInstantApps;
@@ -132,10 +134,12 @@
* 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 @@
mUninstalledInstantApps.registerObserver(mObserver);
mInstantGrants.registerObserver(mObserver);
mInstalledInstantAppUids.registerObserver(mObserver);
+ Watchable.verifyWatchedAttributes(this, mObserver);
}
/**
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index c4a23f9..f240d85 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -1316,10 +1316,6 @@
} finally {
mListeners.finishBroadcast();
}
- PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
- pmi.registerInstalledLoadingProgressCallback(packageName,
- new PackageLoadingProgressCallback(packageName, user),
- user.getIdentifier());
super.onPackageAdded(packageName, uid);
}
@@ -1542,38 +1538,5 @@
checkCallbackCount();
}
}
-
- class PackageLoadingProgressCallback extends
- PackageManagerInternal.InstalledLoadingProgressCallback {
- private String mPackageName;
- private UserHandle mUser;
-
- PackageLoadingProgressCallback(String packageName, UserHandle user) {
- super(mCallbackHandler);
- mPackageName = packageName;
- mUser = user;
- }
-
- @Override
- public void onLoadingProgressChanged(float progress) {
- final int n = mListeners.beginBroadcast();
- try {
- for (int i = 0; i < n; i++) {
- IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
- BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
- if (!isEnabledProfileOf(cookie.user, mUser, "onLoadingProgressChanged")) {
- continue;
- }
- try {
- listener.onPackageLoadingProgressChanged(mUser, mPackageName, progress);
- } catch (RemoteException re) {
- Slog.d(TAG, "Callback failed ", re);
- }
- }
- } finally {
- mListeners.finishBroadcast();
- }
- }
- }
}
}
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 6485c0c..d851e6c 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -401,11 +401,16 @@
String[] abiList = (cpuAbiOverride != null)
? new String[]{cpuAbiOverride} : Build.SUPPORTED_ABIS;
- // Enable gross and lame hacks for apps that are built with old
- // SDK tools. We must scan their APKs for renderscript bitcode and
- // not launch them if it's present. Don't bother checking on devices
- // that don't have 64 bit support.
+ // If an app that contains RenderScript has target API level < 21, it needs to run
+ // with 32-bit ABI, and its APK file will contain a ".bc" file.
+ // If an app that contains RenderScript has target API level >= 21, it can run with
+ // either 32-bit or 64-bit ABI, and its APK file will not contain a ".bc" file.
+ // Therefore, on a device that supports both 32-bit and 64-bit ABIs, we scan the app
+ // APK to see if it has a ".bc" file. If so, we will run it with 32-bit ABI.
+ // However, if the device only supports 64-bit ABI but does not support 32-bit ABI,
+ // we will fail the installation for such an app because it won't be able to run.
boolean needsRenderScriptOverride = false;
+ // No need to check if the device only supports 32-bit
if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null
&& NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
@@ -414,7 +419,8 @@
} else {
throw new PackageManagerException(
INSTALL_FAILED_CPU_ABI_INCOMPATIBLE,
- "Apks with renderscript are not supported on 64-bit only devices");
+ "Apps that contain RenderScript with target API level < 21 are not "
+ + "supported on 64-bit only platforms");
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f444ed6..0c143c9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24,7 +24,6 @@
import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.content.Intent.ACTION_MAIN;
-import static android.content.Intent.CATEGORY_BROWSABLE;
import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.Intent.CATEGORY_HOME;
import static android.content.Intent.EXTRA_LONG_VERSION_CODE;
@@ -68,11 +67,7 @@
import static android.content.pm.PackageManager.INSTALL_REASON_DEVICE_SETUP;
import static android.content.pm.PackageManager.INSTALL_STAGED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.content.pm.PackageManager.MATCH_APEX;
@@ -177,6 +172,7 @@
import android.content.pm.FallbackCategoryProvider;
import android.content.pm.FeatureInfo;
import android.content.pm.IDexModuleRegisterCallback;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageChangeObserver;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
@@ -197,6 +193,7 @@
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;
@@ -238,6 +235,7 @@
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;
@@ -377,11 +375,6 @@
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.pm.dex.ViewCompiler;
-import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
-import com.android.server.pm.verify.domain.DomainVerificationService;
-import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
-import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1;
-import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2;
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.PackageParser2;
@@ -395,6 +388,11 @@
import com.android.server.pm.permission.Permission;
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationService;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1;
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV2;
import com.android.server.rollback.RollbackManagerInternal;
import com.android.server.security.VerityUtils;
import com.android.server.storage.DeviceStorageMonitorInternal;
@@ -404,7 +402,6 @@
import com.android.server.utils.Watched;
import com.android.server.utils.WatchedArrayMap;
import com.android.server.utils.WatchedSparseBooleanArray;
-import com.android.server.utils.WatchedSparseIntArray;
import com.android.server.utils.Watcher;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -466,7 +463,6 @@
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
-import java.util.function.Supplier;
/**
* Keep track of all those APKs everywhere.
@@ -1753,6 +1749,11 @@
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);
+ }
}
/**
@@ -3622,8 +3623,6 @@
final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
final int userId = UserHandle.getUserId(uid);
final int appId = UserHandle.getAppId(uid);
- enforceCrossUserPermission(callingUid, userId,
- /* requireFullPermission */ false, /* checkShell */ false, "getPackagesForUid");
return getPackagesForUidInternalBody(callingUid, userId, appId, isCallerInstantApp);
}
@@ -5523,19 +5522,19 @@
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int optional,
@Checksum.Type int required, @Nullable List trustedInstallers,
- @NonNull IntentSender statusReceiver, int userId) {
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId) {
requestChecksumsInternal(packageName, includeSplits, optional, required, trustedInstallers,
- statusReceiver, userId, mInjector.getBackgroundExecutor(),
+ onChecksumsReadyListener, userId, mInjector.getBackgroundExecutor(),
mInjector.getBackgroundHandler());
}
private void requestChecksumsInternal(@NonNull String packageName, boolean includeSplits,
- @Checksum.Type int optional,
- @Checksum.Type int required, @Nullable List trustedInstallers,
- @NonNull IntentSender statusReceiver, int userId, @NonNull Executor executor,
- @NonNull Handler handler) {
+ @Checksum.Type int optional, @Checksum.Type int required,
+ @Nullable List trustedInstallers,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
+ @NonNull Executor executor, @NonNull Handler handler) {
Objects.requireNonNull(packageName);
- Objects.requireNonNull(statusReceiver);
+ Objects.requireNonNull(onChecksumsReadyListener);
Objects.requireNonNull(executor);
Objects.requireNonNull(handler);
@@ -5571,7 +5570,7 @@
() -> mInjector.getIncrementalManager(),
() -> mPmInternal);
ApkChecksums.getChecksums(filesToChecksum, optional, required, installerPackageName,
- trustedCerts, statusReceiver, injector);
+ trustedCerts, onChecksumsReadyListener, injector);
});
}
@@ -5769,8 +5768,8 @@
(i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()),
(i, pm) -> (IncrementalManager)
i.getContext().getSystemService(Context.INCREMENTAL_SERVICE),
- (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(
- RoleManager.class)),
+ (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(RoleManager.class),
+ () -> LocalServices.getService(UserManagerInternal.class)),
(i, pm) -> new DisplayMetrics(),
(i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore,
i.getDisplayMetrics(), pm.mCacheDir,
@@ -5946,6 +5945,21 @@
}
}
+ // 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.
@@ -6029,15 +6043,7 @@
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;
@@ -6191,15 +6197,6 @@
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
@@ -6208,6 +6205,7 @@
sSnapshotCorked = true;
mLiveComputer = createLiveComputer();
mSnapshotComputer = mLiveComputer;
+ registerObserver();
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
@@ -16164,8 +16162,7 @@
@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
@@ -18127,11 +18124,8 @@
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);
}
}
}
@@ -19168,6 +19162,8 @@
extras, 0 /*flags*/,
null /*targetPackage*/, null /*finishedReceiver*/,
mInstalledUserIds, null /* instantUserIds */, newBroadcastAllowList, null);
+ // Unregister progress listener
+ mIncrementalManager.unregisterLoadingProgressCallbacks(codePath);
// Unregister health listener as it will always be healthy from now
mIncrementalManager.unregisterHealthListener(codePath);
}
@@ -23939,7 +23935,8 @@
writer.println("Domain verification status:");
writer.increaseIndent();
try {
- mDomainVerificationManager.printState(writer, packageName, UserHandle.USER_ALL);
+ mDomainVerificationManager.printState(writer, packageName, UserHandle.USER_ALL,
+ mSettings::getPackageLPr);
} catch (PackageManager.NameNotFoundException e) {
pw.println("Failure printing domain verification information");
Slog.e(TAG, "Failure printing domain verification information", e);
@@ -26619,34 +26616,19 @@
@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) {
@@ -26657,28 +26639,30 @@
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;
}
@@ -27045,47 +27029,6 @@
}
@Override
- public boolean registerInstalledLoadingProgressCallback(String packageName,
- PackageManagerInternal.InstalledLoadingProgressCallback callback, int userId) {
- final PackageSetting ps = getPackageSettingForUser(packageName, Binder.getCallingUid(),
- userId);
- if (ps == null) {
- return false;
- }
- if (!ps.isPackageLoading()) {
- Slog.w(TAG,
- "Failed registering loading progress callback. Package is fully loaded.");
- return false;
- }
- if (mIncrementalManager == null) {
- Slog.w(TAG,
- "Failed registering loading progress callback. Incremental is not enabled");
- return false;
- }
- return mIncrementalManager.registerLoadingProgressCallback(ps.getPathString(),
- (IPackageLoadingProgressCallback) callback.getBinder());
- }
-
- @Override
- public boolean unregisterInstalledLoadingProgressCallback(String packageName,
- PackageManagerInternal.InstalledLoadingProgressCallback callback) {
- final PackageSetting ps;
- synchronized (mLock) {
- ps = mSettings.getPackageLPr(packageName);
- if (ps == null) {
- Slog.w(TAG, "Failed unregistering loading progress callback. Package "
- + packageName + " is not installed");
- return false;
- }
- }
- if (mIncrementalManager == null) {
- return false;
- }
- return mIncrementalManager.unregisterLoadingProgressCallback(ps.getPathString(),
- (IPackageLoadingProgressCallback) callback.getBinder());
- }
-
- @Override
public IncrementalStatesInfo getIncrementalStatesInfo(
@NonNull String packageName, int filterCallingUid, int userId) {
final PackageSetting ps = getPackageSettingForUser(packageName, filterCallingUid,
@@ -27110,12 +27053,14 @@
ps.setStatesOnCrashOrAnr();
}
+ @Override
public void requestChecksums(@NonNull String packageName, boolean includeSplits,
@Checksum.Type int optional, @Checksum.Type int required,
- @Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId,
+ @Nullable List trustedInstallers,
+ @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
@NonNull Executor executor, @NonNull Handler handler) {
requestChecksumsInternal(packageName, includeSplits, optional, required,
- trustedInstallers, statusReceiver, userId, executor, handler);
+ trustedInstallers, onChecksumsReadyListener, userId, executor, handler);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 69e84b5..ca5d2b4 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -344,8 +344,8 @@
installSource.originatingPackageName);
proto.end(sourceToken);
}
- proto.write(PackageProto.StatesProto.IS_STARTABLE, incrementalStates.isStartable());
- proto.write(PackageProto.StatesProto.IS_LOADING, incrementalStates.isLoading());
+ proto.write(PackageProto.StatesProto.IS_STARTABLE, isPackageStartable());
+ proto.write(PackageProto.StatesProto.IS_LOADING, isPackageLoading());
writeUsersInfoToProto(proto, PackageProto.USERS);
writePackageUserPermissionsProto(proto, PackageProto.USER_PERMISSIONS, users, dataProvider);
proto.end(packageToken);
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 8aa553d..5364cbf 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -26,13 +26,12 @@
import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.IncrementalStatesInfo;
-import android.content.pm.IntentFilterVerificationInfo;
-import android.content.pm.PackageManager;
import android.content.pm.PackageManager.UninstallReason;
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;
@@ -42,13 +41,10 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
-import com.android.server.pm.verify.domain.DomainVerificationService;
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;
@@ -331,21 +327,20 @@
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();
}
@@ -731,14 +726,14 @@
* @return True if package is startable, false otherwise.
*/
public boolean isPackageStartable() {
- return incrementalStates.isStartable();
+ return getIncrementalStates().isStartable();
}
/**
* @return True if package is still being loaded, false if the package is fully loaded.
*/
public boolean isPackageLoading() {
- return incrementalStates.isLoading();
+ return getIncrementalStates().isLoading();
}
/**
diff --git a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
index ce77c91..8c5084a 100644
--- a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
+++ b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
@@ -16,22 +16,16 @@
package com.android.server.pm;
-import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
-
import android.app.admin.SecurityLog;
import android.content.Context;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentSender;
import android.content.pm.ApkChecksum;
import android.content.pm.Checksum;
+import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.PackageManagerInternal;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerExecutor;
-import android.os.IBinder;
-import android.os.Parcelable;
+import android.os.RemoteException;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
@@ -39,7 +33,6 @@
import com.android.internal.os.BackgroundThread;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
@@ -109,26 +102,23 @@
// Capturing local loggingInfo to still log even if hash was invalidated.
try {
pmi.requestChecksums(packageName, false, 0, CHECKSUM_TYPE, null,
- new IntentSender((IIntentSender) new IIntentSender.Stub() {
+ new IOnChecksumsReadyListener.Stub() {
@Override
- public void send(int code, Intent intent, String resolvedType,
- IBinder allowlistToken, IIntentReceiver finishedReceiver,
- String requiredPermission, Bundle options) {
- processChecksums(loggingInfo, intent);
+ public void onChecksumsReady(List<ApkChecksum> checksums)
+ throws RemoteException {
+ processChecksums(loggingInfo, checksums);
}
- }), context.getUserId(), mExecutor, this);
+ }, context.getUserId(),
+ mExecutor, this);
} catch (Throwable t) {
Slog.e(TAG, "requestChecksums() failed", t);
enqueueProcessChecksum(loggingInfo, null);
}
}
- void processChecksums(final LoggingInfo loggingInfo, Intent intent) {
- Parcelable[] parcelables = intent.getParcelableArrayExtra(EXTRA_CHECKSUMS);
- ApkChecksum[] checksums = Arrays.copyOf(parcelables, parcelables.length,
- ApkChecksum[].class);
-
- for (ApkChecksum checksum : checksums) {
+ void processChecksums(final LoggingInfo loggingInfo, List<ApkChecksum> checksums) {
+ for (int i = 0, size = checksums.size(); i < size; ++i) {
+ ApkChecksum checksum = checksums.get(i);
if (checksum.getType() == CHECKSUM_TYPE) {
processChecksum(loggingInfo, checksum.getValue());
return;
diff --git a/services/core/java/com/android/server/pm/RestrictionsSet.java b/services/core/java/com/android/server/pm/RestrictionsSet.java
index ea61ca4..e5a70c3 100644
--- a/services/core/java/com/android/server/pm/RestrictionsSet.java
+++ b/services/core/java/com/android/server/pm/RestrictionsSet.java
@@ -26,6 +26,7 @@
import android.util.TypedXmlSerializer;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.BundleUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -78,7 +79,7 @@
if (!changed) {
return false;
}
- if (!UserRestrictionsUtils.isEmpty(restrictions)) {
+ if (!BundleUtils.isEmpty(restrictions)) {
mUserRestrictions.put(userId, restrictions);
} else {
mUserRestrictions.delete(userId);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index fb033e6..a8a6bce 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.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.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.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.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 @@
// 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 @@
mAppIds.registerObserver(mObserver);
mOtherAppIds.registerObserver(mObserver);
mRenamedPackages.registerObserver(mObserver);
+ mNextAppLinkGeneration.registerObserver(mObserver);
mDefaultBrowserApp.registerObserver(mObserver);
Watchable.verifyWatchedAttributes(this, mObserver);
@@ -602,6 +605,7 @@
mAppIds.registerObserver(mObserver);
mOtherAppIds.registerObserver(mObserver);
mRenamedPackages.registerObserver(mObserver);
+ mNextAppLinkGeneration.registerObserver(mObserver);
mDefaultBrowserApp.registerObserver(mObserver);
Watchable.verifyWatchedAttributes(this, mObserver);
@@ -649,6 +653,7 @@
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 @@
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 @@
}
}
- 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/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 89729b5..9b092c0 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -1535,6 +1535,27 @@
pw.println(")");
}
+ public void dumpShortcuts(@NonNull PrintWriter pw, int matchFlags) {
+ final boolean matchDynamic = (matchFlags & ShortcutManager.FLAG_MATCH_DYNAMIC) != 0;
+ final boolean matchPinned = (matchFlags & ShortcutManager.FLAG_MATCH_PINNED) != 0;
+ final boolean matchManifest = (matchFlags & ShortcutManager.FLAG_MATCH_MANIFEST) != 0;
+ final boolean matchCached = (matchFlags & ShortcutManager.FLAG_MATCH_CACHED) != 0;
+
+ final int shortcutFlags = (matchDynamic ? ShortcutInfo.FLAG_DYNAMIC : 0)
+ | (matchPinned ? ShortcutInfo.FLAG_PINNED : 0)
+ | (matchManifest ? ShortcutInfo.FLAG_MANIFEST : 0)
+ | (matchCached ? ShortcutInfo.FLAG_CACHED_ALL : 0);
+
+ final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts;
+ final int size = shortcuts.size();
+ for (int i = 0; i < size; i++) {
+ final ShortcutInfo si = shortcuts.valueAt(i);
+ if ((si.getFlags() & shortcutFlags) != 0) {
+ pw.println(si.toDumpString(""));
+ }
+ }
+ }
+
@Override
public JSONObject dumpCheckin(boolean clear) throws JSONException {
final JSONObject result = super.dumpCheckin(clear);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 3c4457d..863e3fe5 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -40,6 +40,7 @@
import android.content.LocusId;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
import android.content.pm.IPackageManager;
import android.content.pm.IShortcutService;
import android.content.pm.LauncherApps;
@@ -4556,6 +4557,10 @@
private int mUserId = UserHandle.USER_SYSTEM;
+ private int mShortcutMatchFlags = ShortcutManager.FLAG_MATCH_CACHED
+ | ShortcutManager.FLAG_MATCH_DYNAMIC | ShortcutManager.FLAG_MATCH_MANIFEST
+ | ShortcutManager.FLAG_MATCH_PINNED;
+
private void parseOptionsLocked(boolean takeUser)
throws CommandException {
String opt;
@@ -4571,6 +4576,9 @@
break;
}
// fallthrough
+ case "--flags":
+ mShortcutMatchFlags = Integer.parseInt(getNextArgRequired());
+ break;
default:
throw new CommandException("Unknown option: " + opt);
}
@@ -4606,9 +4614,15 @@
case "clear-shortcuts":
handleClearShortcuts();
break;
+ case "get-shortcuts":
+ handleGetShortcuts();
+ break;
case "verify-states": // hidden command to verify various internal states.
handleVerifyStates();
break;
+ case "has-shortcut-access":
+ handleHasShortcutAccess();
+ break;
default:
return handleDefaultCommands(cmd);
}
@@ -4640,7 +4654,7 @@
pw.println("[Deprecated] cmd shortcut get-default-launcher [--user USER_ID]");
pw.println(" Show the default launcher");
pw.println(" Note: This command is deprecated. Callers should query the default"
- + " launcher directly from RoleManager instead.");
+ + " launcher from RoleManager instead.");
pw.println();
pw.println("cmd shortcut unload-user [--user USER_ID]");
pw.println(" Unload a user from the memory");
@@ -4649,6 +4663,13 @@
pw.println("cmd shortcut clear-shortcuts [--user USER_ID] PACKAGE");
pw.println(" Remove all shortcuts from a package, including pinned shortcuts");
pw.println();
+ pw.println("cmd shortcut get-shortcuts [--user USER_ID] [--flags FLAGS] PACKAGE");
+ pw.println(" Show the shortcuts for a package that match the given flags");
+ pw.println();
+ pw.println("cmd shortcut has-shortcut-access [--user USER_ID] PACKAGE");
+ pw.println(" Prints \"true\" if the package can access shortcuts,"
+ + " \"false\" otherwise");
+ pw.println();
}
private void handleResetThrottling() throws CommandException {
@@ -4693,11 +4714,24 @@
private void handleGetDefaultLauncher() throws CommandException {
synchronized (mLock) {
parseOptionsLocked(/* takeUser =*/ true);
+
+ final String defaultLauncher = getDefaultLauncher(mUserId);
+ if (defaultLauncher == null) {
+ throw new CommandException(
+ "Failed to get the default launcher for user " + mUserId);
+ }
+
+ // Get the class name of the component from PM to keep the old behaviour.
final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
- // Default launcher from package manager.
- final ComponentName defaultLauncher = mPackageManagerInternal
- .getHomeActivitiesAsUser(allHomeCandidates, getParentOrSelfUserId(mUserId));
- getOutPrintWriter().println("Launcher: " + defaultLauncher);
+ mPackageManagerInternal.getHomeActivitiesAsUser(allHomeCandidates,
+ getParentOrSelfUserId(mUserId));
+ for (ResolveInfo ri : allHomeCandidates) {
+ final ComponentInfo ci = ri.getComponentInfo();
+ if (ci.packageName.equals(defaultLauncher)) {
+ getOutPrintWriter().println("Launcher: " + ci.getComponentName());
+ break;
+ }
+ }
}
}
@@ -4723,6 +4757,24 @@
}
}
+ private void handleGetShortcuts() throws CommandException {
+ synchronized (mLock) {
+ parseOptionsLocked(/* takeUser =*/ true);
+ final String packageName = getNextArgRequired();
+
+ Slog.i(TAG, "cmd: handleGetShortcuts: user=" + mUserId + ", flags="
+ + mShortcutMatchFlags + ", package=" + packageName);
+
+ final ShortcutUser user = ShortcutService.this.getUserShortcutsLocked(mUserId);
+ final ShortcutPackage p = user.getPackageShortcutsIfExists(packageName);
+ if (p == null) {
+ return;
+ }
+
+ p.dumpShortcuts(getOutPrintWriter(), mShortcutMatchFlags);
+ }
+ }
+
private void handleVerifyStates() throws CommandException {
try {
verifyStatesForce(); // This will throw when there's an issue.
@@ -4730,6 +4782,16 @@
throw new CommandException(th.getMessage() + "\n" + Log.getStackTraceString(th));
}
}
+
+ private void handleHasShortcutAccess() throws CommandException {
+ synchronized (mLock) {
+ parseOptionsLocked(/* takeUser =*/ true);
+ final String packageName = getNextArgRequired();
+
+ boolean shortcutAccess = hasShortcutHostPermissionInner(packageName, mUserId);
+ getOutPrintWriter().println(Boolean.toString(shortcutAccess));
+ }
+ }
}
// === Unit test support ===
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index c182d68..c17d2e4 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -298,4 +298,11 @@
* Gets all {@link UserInfo UserInfos}.
*/
public abstract @NonNull UserInfo[] getUserInfos();
+
+ /**
+ * Sets all default cross profile intent filters between {@code parentUserId} and
+ * {@code profileUserId}.
+ */
+ public abstract void setDefaultCrossProfileIntentFilters(
+ @UserIdInt int parentUserId, @UserIdInt int profileUserId);
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a807777..753f22d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -80,6 +80,7 @@
import android.os.UserManager.EnforcingUser;
import android.os.UserManager.QuietModeFlag;
import android.os.storage.StorageManager;
+import android.provider.Settings;
import android.security.GateKeeper;
import android.service.gatekeeper.IGateKeeperService;
import android.stats.devicepolicy.DevicePolicyEnums;
@@ -108,6 +109,7 @@
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
+import com.android.server.BundleUtils;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.SystemService;
@@ -1960,11 +1962,11 @@
final Bundle global = mDevicePolicyGlobalUserRestrictions.mergeAll();
final RestrictionsSet local = getDevicePolicyLocalRestrictionsForTargetUserLR(userId);
- if (UserRestrictionsUtils.isEmpty(global) && local.isEmpty()) {
+ if (BundleUtils.isEmpty(global) && local.isEmpty()) {
// Common case first.
return baseRestrictions;
}
- final Bundle effective = UserRestrictionsUtils.clone(baseRestrictions);
+ final Bundle effective = BundleUtils.clone(baseRestrictions);
UserRestrictionsUtils.merge(effective, global);
UserRestrictionsUtils.merge(effective, local.mergeAll());
@@ -2105,7 +2107,7 @@
@Override
public Bundle getUserRestrictions(@UserIdInt int userId) {
checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserRestrictions");
- return UserRestrictionsUtils.clone(getEffectiveUserRestrictions(userId));
+ return BundleUtils.clone(getEffectiveUserRestrictions(userId));
}
@Override
@@ -2129,7 +2131,7 @@
synchronized (mRestrictionsLock) {
// Note we can't modify Bundles stored in mBaseUserRestrictions directly, so create
// a copy.
- final Bundle newRestrictions = UserRestrictionsUtils.clone(
+ final Bundle newRestrictions = BundleUtils.clone(
mBaseUserRestrictions.getRestrictions(userId));
newRestrictions.putBoolean(key, value);
@@ -2727,7 +2729,7 @@
if (userVersion < 7) {
// Previously only one user could enforce global restrictions, now it is per-user.
synchronized (mRestrictionsLock) {
- if (!UserRestrictionsUtils.isEmpty(oldGlobalUserRestrictions)
+ if (!BundleUtils.isEmpty(oldGlobalUserRestrictions)
&& mDeviceOwnerUserId != UserHandle.USER_NULL) {
mDevicePolicyGlobalUserRestrictions.updateRestrictions(
mDeviceOwnerUserId, oldGlobalUserRestrictions);
@@ -3562,6 +3564,9 @@
t.traceBegin("PM.onNewUserCreated-" + userId);
mPm.onNewUserCreated(userId, /* convertedFromPreCreated= */ false);
t.traceEnd();
+ applyDefaultUserSettings(userTypeDetails, userId);
+ setDefaultCrossProfileIntentFilters(userId, userTypeDetails, restrictions, parentId);
+
if (preCreate) {
// Must start user (which will be stopped right away, through
// UserController.finishUserUnlockedCompleted) so services can properly
@@ -3597,6 +3602,78 @@
return userInfo;
}
+ private void applyDefaultUserSettings(UserTypeDetails userTypeDetails, @UserIdInt int userId) {
+ final Bundle systemSettings = userTypeDetails.getDefaultSystemSettings();
+ final Bundle secureSettings = userTypeDetails.getDefaultSecureSettings();
+ if (systemSettings.isEmpty() && secureSettings.isEmpty()) {
+ return;
+ }
+
+ final int systemSettingsSize = systemSettings.size();
+ final String[] systemSettingsArray = systemSettings.keySet().toArray(
+ new String[systemSettingsSize]);
+ for (int i = 0; i < systemSettingsSize; i++) {
+ final String setting = systemSettingsArray[i];
+ if (!Settings.System.putStringForUser(
+ mContext.getContentResolver(), setting, systemSettings.getString(setting),
+ userId)) {
+ Slog.e(LOG_TAG, "Failed to insert default system setting: " + setting);
+ }
+ }
+
+ final int secureSettingsSize = secureSettings.size();
+ final String[] secureSettingsArray = secureSettings.keySet().toArray(
+ new String[secureSettingsSize]);
+ for (int i = 0; i < secureSettingsSize; i++) {
+ final String setting = secureSettingsArray[i];
+ if (!Settings.Secure.putStringForUser(
+ mContext.getContentResolver(), setting, secureSettings.getString(setting),
+ userId)) {
+ Slog.e(LOG_TAG, "Failed to insert default secure setting: " + setting);
+ }
+ }
+ }
+
+ /**
+ * Sets all default cross profile intent filters between {@code parentUserId} and
+ * {@code profileUserId}, does nothing if {@code userType} is not a profile.
+ */
+ private void setDefaultCrossProfileIntentFilters(
+ @UserIdInt int profileUserId, UserTypeDetails profileDetails,
+ Bundle profileRestrictions, @UserIdInt int parentUserId) {
+ if (profileDetails == null || !profileDetails.isProfile()) {
+ return;
+ }
+ final List<DefaultCrossProfileIntentFilter> filters =
+ profileDetails.getDefaultCrossProfileIntentFilters();
+ if (filters.isEmpty()) {
+ return;
+ }
+
+ // Skip filters that allow data to be shared into the profile, if admin has disabled it.
+ final boolean disallowSharingIntoProfile =
+ profileRestrictions.getBoolean(
+ UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,
+ /* defaultValue = */ false);
+ final int size = profileDetails.getDefaultCrossProfileIntentFilters().size();
+ for (int i = 0; i < size; i++) {
+ final DefaultCrossProfileIntentFilter filter =
+ profileDetails.getDefaultCrossProfileIntentFilters().get(i);
+ if (disallowSharingIntoProfile && filter.letsPersonalDataIntoProfile) {
+ continue;
+ }
+ if (filter.direction == DefaultCrossProfileIntentFilter.Direction.TO_PARENT) {
+ mPm.addCrossProfileIntentFilter(
+ filter.filter, mContext.getOpPackageName(), profileUserId, parentUserId,
+ filter.flags);
+ } else {
+ mPm.addCrossProfileIntentFilter(
+ filter.filter, mContext.getOpPackageName(), parentUserId, profileUserId,
+ filter.flags);
+ }
+ }
+ }
+
/**
* Finds and converts a previously pre-created user into a regular user, if possible.
*
@@ -5462,6 +5539,15 @@
return allInfos;
}
}
+
+ @Override
+ public void setDefaultCrossProfileIntentFilters(
+ @UserIdInt int parentUserId, @UserIdInt int profileUserId) {
+ final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(profileUserId);
+ final Bundle restrictions = getEffectiveUserRestrictions(profileUserId);
+ UserManagerService.this.setDefaultCrossProfileIntentFilters(
+ profileUserId, userTypeDetails, restrictions, parentUserId);
+ }
}
/**
@@ -5726,8 +5812,8 @@
// merge existing base restrictions with the new type's default restrictions
synchronized (mRestrictionsLock) {
- if (!UserRestrictionsUtils.isEmpty(newUserType.getDefaultRestrictions())) {
- final Bundle newRestrictions = UserRestrictionsUtils.clone(
+ if (!BundleUtils.isEmpty(newUserType.getDefaultRestrictions())) {
+ final Bundle newRestrictions = BundleUtils.clone(
mBaseUserRestrictions.getRestrictions(userInfo.id));
UserRestrictionsUtils.merge(newRestrictions,
newUserType.getDefaultRestrictions());
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 51dff40..ff90043 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -44,6 +44,7 @@
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
+import com.android.server.BundleUtils;
import com.android.server.LocalServices;
import com.google.android.collect.Sets;
@@ -384,10 +385,6 @@
return in != null ? in : new Bundle();
}
- public static boolean isEmpty(@Nullable Bundle in) {
- return (in == null) || (in.size() == 0);
- }
-
/**
* Returns {@code true} if given bundle is not null and contains {@code true} for a given
* restriction.
@@ -396,17 +393,6 @@
return in != null && in.getBoolean(restriction);
}
- /**
- * Creates a copy of the {@code in} Bundle. If {@code in} is null, it'll return an empty
- * bundle.
- *
- * <p>The resulting {@link Bundle} is always writable. (i.e. it won't return
- * {@link Bundle#EMPTY})
- */
- public static @NonNull Bundle clone(@Nullable Bundle in) {
- return (in != null) ? new Bundle(in) : new Bundle();
- }
-
public static void merge(@NonNull Bundle dest, @Nullable Bundle in) {
Objects.requireNonNull(dest);
Preconditions.checkArgument(dest != in);
@@ -483,10 +469,10 @@
if (a == b) {
return true;
}
- if (isEmpty(a)) {
- return isEmpty(b);
+ if (BundleUtils.isEmpty(a)) {
+ return BundleUtils.isEmpty(b);
}
- if (isEmpty(b)) {
+ if (BundleUtils.isEmpty(b)) {
return false;
}
for (String key : a.keySet()) {
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 5fa46b9..17ce386 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -28,8 +28,12 @@
import android.os.UserManager;
import com.android.internal.util.Preconditions;
+import com.android.server.BundleUtils;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* Contains the details about a multiuser "user type", such as a
@@ -82,6 +86,24 @@
*/
private final @Nullable Bundle mDefaultRestrictions;
+ /**
+ * List of {@link android.provider.Settings.System} to apply by default to newly created users
+ * of this type.
+ */
+ private final @Nullable Bundle mDefaultSystemSettings;
+
+ /**
+ * List of {@link android.provider.Settings.Secure} to apply by default to newly created users
+ * of this type.
+ */
+ private final @Nullable Bundle mDefaultSecureSettings;
+
+ /**
+ * List of {@link DefaultCrossProfileIntentFilter} to allow by default for newly created
+ * profiles.
+ */
+ private final @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters;
+
// Fields for profiles only, controlling the nature of their badges.
// All badge information should be set if {@link #hasBadge()} is true.
@@ -133,7 +155,10 @@
int iconBadge, int badgePlain, int badgeNoBackground,
@Nullable int[] badgeLabels, @Nullable int[] badgeColors,
@Nullable int[] darkThemeBadgeColors,
- @Nullable Bundle defaultRestrictions) {
+ @Nullable Bundle defaultRestrictions,
+ @Nullable Bundle defaultSystemSettings,
+ @Nullable Bundle defaultSecureSettings,
+ @Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters) {
this.mName = name;
this.mEnabled = enabled;
this.mMaxAllowed = maxAllowed;
@@ -141,6 +166,9 @@
this.mBaseType = baseType;
this.mDefaultUserInfoPropertyFlags = defaultUserInfoPropertyFlags;
this.mDefaultRestrictions = defaultRestrictions;
+ this.mDefaultSystemSettings = defaultSystemSettings;
+ this.mDefaultSecureSettings = defaultSecureSettings;
+ this.mDefaultCrossProfileIntentFilters = defaultCrossProfileIntentFilters;
this.mIconBadge = iconBadge;
this.mBadgePlain = badgePlain;
@@ -263,9 +291,9 @@
return (mBaseType & UserInfo.FLAG_SYSTEM) != 0;
}
- /** Returns a Bundle representing the default user restrictions. */
+ /** Returns a {@link Bundle} representing the default user restrictions. */
@NonNull Bundle getDefaultRestrictions() {
- return UserRestrictionsUtils.clone(mDefaultRestrictions);
+ return BundleUtils.clone(mDefaultRestrictions);
}
/** Adds the default user restrictions to the given bundle of restrictions. */
@@ -273,6 +301,24 @@
UserRestrictionsUtils.merge(currentRestrictions, mDefaultRestrictions);
}
+ /** Returns a {@link Bundle} representing the default system settings. */
+ @NonNull Bundle getDefaultSystemSettings() {
+ return BundleUtils.clone(mDefaultSystemSettings);
+ }
+
+ /** Returns a {@link Bundle} representing the default secure settings. */
+ @NonNull Bundle getDefaultSecureSettings() {
+ return BundleUtils.clone(mDefaultSecureSettings);
+ }
+
+ /** Returns a list of default cross profile intent filters. */
+ @NonNull List<DefaultCrossProfileIntentFilter> getDefaultCrossProfileIntentFilters() {
+ return mDefaultCrossProfileIntentFilters != null
+ ? new ArrayList<>(mDefaultCrossProfileIntentFilters)
+ : Collections.emptyList();
+ }
+
+
/** Dumps details of the UserTypeDetails. Do not parse this. */
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mName: "); pw.println(mName);
@@ -325,6 +371,10 @@
private int mMaxAllowedPerParent = UNLIMITED_NUMBER_OF_USERS;
private int mDefaultUserInfoPropertyFlags = 0;
private @Nullable Bundle mDefaultRestrictions = null;
+ private @Nullable Bundle mDefaultSystemSettings = null;
+ private @Nullable Bundle mDefaultSecureSettings = null;
+ private @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters =
+ null;
private boolean mEnabled = true;
private int mLabel = Resources.ID_NULL;
private @Nullable int[] mBadgeLabels = null;
@@ -407,6 +457,22 @@
return this;
}
+ public Builder setDefaultSystemSettings(@Nullable Bundle settings) {
+ mDefaultSystemSettings = settings;
+ return this;
+ }
+
+ public Builder setDefaultSecureSettings(@Nullable Bundle settings) {
+ mDefaultSecureSettings = settings;
+ return this;
+ }
+
+ public Builder setDefaultCrossProfileIntentFilters(
+ @Nullable List<DefaultCrossProfileIntentFilter> intentFilters) {
+ mDefaultCrossProfileIntentFilters = intentFilters;
+ return this;
+ }
+
@UserInfoFlag int getBaseType() {
return mBaseType;
}
@@ -425,17 +491,28 @@
Preconditions.checkArgument(mBadgeColors != null && mBadgeColors.length != 0,
"UserTypeDetails " + mName + " has badge but no badgeColors.");
}
+ if (!isProfile()) {
+ Preconditions.checkArgument(mDefaultCrossProfileIntentFilters == null
+ || mDefaultCrossProfileIntentFilters.isEmpty(),
+ "UserTypeDetails %s has a non empty "
+ + "defaultCrossProfileIntentFilters", mName);
+ }
return new UserTypeDetails(mName, mEnabled, mMaxAllowed, mBaseType,
mDefaultUserInfoPropertyFlags, mLabel, mMaxAllowedPerParent,
mIconBadge, mBadgePlain, mBadgeNoBackground, mBadgeLabels, mBadgeColors,
mDarkThemeBadgeColors == null ? mBadgeColors : mDarkThemeBadgeColors,
- mDefaultRestrictions);
+ mDefaultRestrictions, mDefaultSystemSettings, mDefaultSecureSettings,
+ mDefaultCrossProfileIntentFilters);
}
private boolean hasBadge() {
return mIconBadge != Resources.ID_NULL;
}
+ private boolean isProfile() {
+ return (mBaseType & UserInfo.FLAG_PROFILE) != 0;
+ }
+
// TODO(b/143784345): Refactor this when we clean up UserInfo.
private boolean hasValidBaseType() {
return mBaseType == UserInfo.FLAG_FULL
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 1ffbf60..6aac0b2 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -133,7 +133,9 @@
com.android.internal.R.color.profile_badge_1_dark,
com.android.internal.R.color.profile_badge_2_dark,
com.android.internal.R.color.profile_badge_3_dark)
- .setDefaultRestrictions(null);
+ .setDefaultRestrictions(getDefaultManagedProfileRestrictions())
+ .setDefaultSecureSettings(getDefaultManagedProfileSecureSettings())
+ .setDefaultCrossProfileIntentFilters(getDefaultManagedCrossProfileIntentFilter());
}
/**
@@ -256,12 +258,35 @@
return restrictions;
}
+ private static Bundle getDefaultManagedProfileRestrictions() {
+ final Bundle restrictions = new Bundle();
+ restrictions.putBoolean(UserManager.DISALLOW_WALLPAPER, true);
+ return restrictions;
+ }
+
+ private static Bundle getDefaultManagedProfileSecureSettings() {
+ // Only add String values to the bundle, settings are written as Strings eventually
+ final Bundle settings = new Bundle();
+ settings.putString(
+ android.provider.Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, "1");
+ settings.putString(
+ android.provider.Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, "1");
+ return settings;
+ }
+
+ private static List<DefaultCrossProfileIntentFilter>
+ getDefaultManagedCrossProfileIntentFilter() {
+ return DefaultCrossProfileIntentFiltersUtils.getDefaultManagedProfileFilters();
+ }
+
/**
* Reads the given xml parser to obtain device user-type customization, and updates the given
* map of {@link UserTypeDetails.Builder}s accordingly.
* <p>
* The xml file can specify the attributes according to the set... methods below.
*/
+ // TODO(b/176973369): Add parsing logic to support custom settings/filters
+ // in config_user_types.xml
@VisibleForTesting
static void customizeBuilders(ArrayMap<String, UserTypeDetails.Builder> builders,
XmlResourceParser parser) {
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 0000000..6cdd4df
--- /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 1405a7d..8a8a302 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 @@
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/DomainVerificationDebug.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
index af9978b..1925590 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationDebug.java
@@ -32,15 +32,30 @@
import com.android.internal.util.CollectionUtils;
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.parsing.pkg.AndroidPackage;
import java.util.Arrays;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Function;
+@SuppressWarnings("PointlessBooleanExpression")
public class DomainVerificationDebug {
+ // Disable to turn off all logging. This is used to allow a "basic" set of debug flags to be
+ // enabled and checked in, without having everything be on or off.
+ public static final boolean DEBUG_ANY = false;
+
+ // Enable to turn on all logging. Requires enabling DEBUG_ANY.
+ public static final boolean DEBUG_ALL = false;
+
+ public static final boolean DEBUG_APPROVAL = DEBUG_ANY && (DEBUG_ALL || true);
+ public static final boolean DEBUG_BROADCASTS = DEBUG_ANY && (DEBUG_ALL || false);
+ public static final boolean DEBUG_PROXIES = DEBUG_ANY && (DEBUG_ALL || false);
+
@NonNull
private final DomainVerificationCollector mCollector;
@@ -50,7 +65,7 @@
public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
@Nullable @UserIdInt Integer userId,
- @NonNull DomainVerificationService.Connection connection,
+ @NonNull Function<String, PackageSetting> pkgSettingFunction,
@NonNull DomainVerificationStateMap<DomainVerificationPkgState> stateMap)
throws NameNotFoundException {
ArrayMap<String, Integer> reusedMap = new ArrayMap<>();
@@ -61,7 +76,7 @@
for (int index = 0; index < size; index++) {
DomainVerificationPkgState pkgState = stateMap.valueAt(index);
String pkgName = pkgState.getPackageName();
- PackageSetting pkgSetting = connection.getPackageSettingLocked(pkgName);
+ PackageSetting pkgSetting = pkgSettingFunction.apply(pkgName);
if (pkgSetting == null || pkgSetting.getPkg() == null) {
continue;
}
@@ -77,7 +92,7 @@
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
- PackageSetting pkgSetting = connection.getPackageSettingLocked(packageName);
+ PackageSetting pkgSetting = pkgSettingFunction.apply(packageName);
if (pkgSetting == null || pkgSetting.getPkg() == null) {
throw DomainVerificationUtils.throwPackageUnavailable(packageName);
}
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 c521f82..275dd053 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 @@
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 @@
@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 @@
"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 @@
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 @@
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 7ad275a..50fd6e3 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
@@ -32,15 +32,16 @@
import android.util.TypedXmlSerializer;
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.proxy.DomainVerificationProxy;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.Set;
import java.util.UUID;
+import java.util.function.Function;
public interface DomainVerificationManagerInternal extends DomainVerificationManager {
@@ -173,8 +174,10 @@
* 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
@@ -183,20 +186,23 @@
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 specified
- * @param userId the specific user to print, or null to skip printing user selection
- * states, supports {@link android.os.UserHandle#USER_ALL}
+ * @param packageName the package whose state to change, or all packages if none is
+ * specified
+ * @param userId the specific user to print, or null to skip printing user selection
+ * states, supports {@link android.os.UserHandle#USER_ALL}
+ * @param pkgSettingFunction the method by which to retrieve package data; if this is called
+ * from {@link com.android.server.pm.PackageManagerService}, it is
+ * expected to pass in the snapshot of {@link PackageSetting} objects,
+ * or if null is passed, the manager may decide to lock {@link
+ * com.android.server.pm.PackageManagerService} through {@link
+ * Connection#getPackageSettingLocked(String)}
*/
void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
- @Nullable @UserIdInt Integer userId) throws NameNotFoundException;
+ @Nullable @UserIdInt Integer userId,
+ @Nullable Function<String, PackageSetting> pkgSettingFunction)
+ throws NameNotFoundException;
@NonNull
DomainVerificationShell getShell();
@@ -225,7 +231,8 @@
throws IllegalArgumentException, NameNotFoundException;
- interface Connection {
+ interface Connection extends DomainVerificationEnforcer.Callback,
+ Function<String, PackageSetting> {
/**
* Notify that a settings change has been made and that eventually
@@ -249,10 +256,19 @@
*/
void schedule(int code, @Nullable Object object);
+ // TODO(b/178733426): Make DomainVerificationService PMS snapshot aware so it can avoid
+ // locking package state at all. This can be as simple as removing this method in favor of
+ // accepting a PackageSetting function in at every method call, although should probably
+ // be abstracted to a wrapper class.
@Nullable
PackageSetting getPackageSettingLocked(@NonNull String pkgName);
@Nullable
AndroidPackage getPackageLocked(@NonNull String pkgName);
+
+ @Override
+ default PackageSetting apply(@NonNull String pkgName) {
+ return getPackageSettingLocked(pkgName);
+ }
}
}
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 53540c8..fa03274 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.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;
@@ -64,13 +64,14 @@
import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import java.util.function.Function;
public class DomainVerificationService extends SystemService
implements DomainVerificationManagerInternal, DomainVerificationShell.Callback {
private static final String TAG = "DomainVerificationService";
- public static final boolean DEBUG_APPROVAL = true;
+ public static final boolean DEBUG_APPROVAL = DomainVerificationDebug.DEBUG_APPROVAL;
/**
* The new user preference API for verifying domains marked autoVerify=true in
@@ -91,9 +92,9 @@
* 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
@@ -159,6 +160,7 @@
@Override
public void setConnection(@NonNull Connection connection) {
mConnection = connection;
+ mEnforcer.setCallback(mConnection);
}
@NonNull
@@ -284,7 +286,7 @@
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);
@@ -388,8 +390,10 @@
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) {
@@ -454,11 +458,18 @@
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);
@@ -541,8 +552,6 @@
userState.removeHosts(domains);
}
}
-
- mConnection.scheduleWriteSettings();
}
@Nullable
@@ -557,8 +566,10 @@
@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) {
@@ -845,22 +856,27 @@
}
@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);
@@ -890,9 +906,24 @@
@Override
public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
- @Nullable @UserIdInt Integer userId) throws NameNotFoundException {
+ @Nullable Integer userId) throws NameNotFoundException {
+ // This method is only used by DomainVerificationShell, which doesn't lock PMS, so it's
+ // safe to pass mConnection directly here and lock PMS. This method is not exposed
+ // to the general system server/PMS.
+ printState(writer, packageName, userId, mConnection);
+ }
+
+ @Override
+ public void printState(@NonNull IndentingPrintWriter writer, @Nullable String packageName,
+ @Nullable @UserIdInt Integer userId,
+ @Nullable Function<String, PackageSetting> pkgSettingFunction)
+ throws NameNotFoundException {
+ if (pkgSettingFunction == null) {
+ pkgSettingFunction = mConnection;
+ }
+
synchronized (mLock) {
- mDebug.printState(writer, packageName, userId, mConnection, mAttachedPkgStates);
+ mDebug.printState(writer, packageName, userId, pkgSettingFunction, mAttachedPkgStates);
}
}
@@ -920,10 +951,14 @@
* 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,
@@ -937,6 +972,13 @@
}
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);
@@ -1051,6 +1093,8 @@
}
}
}
+
+ mConnection.scheduleWriteSettings();
}
/**
@@ -1108,6 +1152,8 @@
}
}
}
+
+ mConnection.scheduleWriteSettings();
}
@Override
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java
index 715d8fb..09abdd0 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxy.java
@@ -23,9 +23,10 @@
import android.util.Slog;
import com.android.server.DeviceIdleInternal;
-import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
import com.android.server.pm.verify.domain.DomainVerificationCollector;
+import com.android.server.pm.verify.domain.DomainVerificationDebug;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
import java.util.Objects;
import java.util.Set;
@@ -35,7 +36,7 @@
String TAG = "DomainVerificationProxy";
- boolean DEBUG_PROXIES = false;
+ boolean DEBUG_PROXIES = DomainVerificationDebug.DEBUG_PROXIES;
static <ConnectionType extends DomainVerificationProxyV1.Connection
& DomainVerificationProxyV2.Connection> DomainVerificationProxy makeProxy(
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
index eab89e98..9389e63 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV1.java
@@ -37,10 +37,11 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.verify.domain.DomainVerificationCollector;
+import com.android.server.pm.verify.domain.DomainVerificationDebug;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.util.Collections;
import java.util.List;
@@ -52,7 +53,7 @@
private static final String TAG = "DomainVerificationProxyV1";
- private static final boolean DEBUG_BROADCASTS = false;
+ private static final boolean DEBUG_BROADCASTS = DomainVerificationDebug.DEBUG_BROADCASTS;
@NonNull
private final Context mContext;
@@ -152,18 +153,22 @@
UUID domainSetId = pair.first;
String packageName = pair.second;
- DomainVerificationInfo set;
+ DomainVerificationInfo info;
try {
- set = mManager.getDomainVerificationInfo(packageName);
+ info = mManager.getDomainVerificationInfo(packageName);
} catch (PackageManager.NameNotFoundException ignored) {
return true;
}
- if (!Objects.equals(domainSetId, set.getIdentifier())) {
+ if (info == null) {
return true;
}
- Set<String> successfulDomains = new ArraySet<>(set.getHostToStateMap().keySet());
+ if (!Objects.equals(domainSetId, info.getIdentifier())) {
+ return true;
+ }
+
+ Set<String> successfulDomains = new ArraySet<>(info.getHostToStateMap().keySet());
successfulDomains.removeAll(response.failedDomains);
int callingUid = response.callingUid;
diff --git a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
index 9fcbce2..1ef06036 100644
--- a/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
+++ b/services/core/java/com/android/server/pm/verify/domain/proxy/DomainVerificationProxyV2.java
@@ -28,6 +28,7 @@
import android.os.UserHandle;
import android.util.Slog;
+import com.android.server.pm.verify.domain.DomainVerificationDebug;
import com.android.server.pm.verify.domain.DomainVerificationMessageCodes;
import java.util.Set;
@@ -36,7 +37,7 @@
private static final String TAG = "DomainVerificationProxyV2";
- private static final boolean DEBUG_BROADCASTS = true;
+ private static final boolean DEBUG_BROADCASTS = DomainVerificationDebug.DEBUG_BROADCASTS;
@NonNull
private final Context mContext;
diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index c10e828..82fc22c 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -74,8 +74,8 @@
mHandler = handler;
DeviceStateManager deviceStateManager = context.getSystemService(DeviceStateManager.class);
- deviceStateManager.registerDeviceStateListener(new DeviceStateListener(context),
- new HandlerExecutor(handler));
+ deviceStateManager.addDeviceStateListener(new HandlerExecutor(handler),
+ new DeviceStateListener(context));
}
void finishedGoingToSleep() {
diff --git a/services/core/java/com/android/server/policy/IconUtilities.java b/services/core/java/com/android/server/policy/IconUtilities.java
deleted file mode 100644
index 884d7d4..0000000
--- a/services/core/java/com/android/server/policy/IconUtilities.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.policy;
-
-import android.graphics.ColorFilter;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.PaintDrawable;
-import android.graphics.drawable.StateListDrawable;
-import android.graphics.Bitmap;
-import android.graphics.BlurMaskFilter;
-import android.graphics.Canvas;
-import android.graphics.ColorMatrix;
-import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.TableMaskFilter;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.content.res.Resources;
-import android.content.Context;
-
-/**
- * Various utilities shared amongst the Launcher's classes.
- */
-public final class IconUtilities {
-
- private int mIconWidth = -1;
- private int mIconHeight = -1;
- private int mIconTextureWidth = -1;
- private int mIconTextureHeight = -1;
-
- private final Rect mOldBounds = new Rect();
- private final Canvas mCanvas = new Canvas();
- private final DisplayMetrics mDisplayMetrics;
-
- private ColorFilter mDisabledColorFilter;
-
- public IconUtilities(Context context) {
- final Resources resources = context.getResources();
- DisplayMetrics metrics = mDisplayMetrics = resources.getDisplayMetrics();
- final float density = metrics.density;
- final float blurPx = 5 * density;
-
- mIconWidth = mIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size);
- mIconTextureWidth = mIconTextureHeight = mIconWidth + (int)(blurPx*2);
- mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
- Paint.FILTER_BITMAP_FLAG));
- }
-
- /**
- * Returns a bitmap suitable for the all apps view. The bitmap will be a power
- * of two sized ARGB_8888 bitmap that can be used as a gl texture.
- */
- public Bitmap createIconBitmap(Drawable icon) {
- int width = mIconWidth;
- int height = mIconHeight;
-
- if (icon instanceof PaintDrawable) {
- PaintDrawable painter = (PaintDrawable) icon;
- painter.setIntrinsicWidth(width);
- painter.setIntrinsicHeight(height);
- } else if (icon instanceof BitmapDrawable) {
- // Ensure the bitmap has a density.
- BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
- Bitmap bitmap = bitmapDrawable.getBitmap();
- if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
- bitmapDrawable.setTargetDensity(mDisplayMetrics);
- }
- }
- int sourceWidth = icon.getIntrinsicWidth();
- int sourceHeight = icon.getIntrinsicHeight();
-
- if (sourceWidth > 0 && sourceHeight > 0) {
- // There are intrinsic sizes.
- if (width < sourceWidth || height < sourceHeight) {
- // It's too big, scale it down.
- final float ratio = (float) sourceWidth / sourceHeight;
- if (sourceWidth > sourceHeight) {
- height = (int) (width / ratio);
- } else if (sourceHeight > sourceWidth) {
- width = (int) (height * ratio);
- }
- } else if (sourceWidth < width && sourceHeight < height) {
- // It's small, use the size they gave us.
- width = sourceWidth;
- height = sourceHeight;
- }
- }
-
- // no intrinsic size --> use default size
- int textureWidth = mIconTextureWidth;
- int textureHeight = mIconTextureHeight;
-
- final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
- Bitmap.Config.ARGB_8888);
- final Canvas canvas = mCanvas;
- canvas.setBitmap(bitmap);
-
- final int left = (textureWidth-width) / 2;
- final int top = (textureHeight-height) / 2;
-
- mOldBounds.set(icon.getBounds());
- icon.setBounds(left, top, left+width, top+height);
- icon.draw(canvas);
- icon.setBounds(mOldBounds);
-
- return bitmap;
- }
-
- public ColorFilter getDisabledColorFilter() {
- if (mDisabledColorFilter != null) {
- return mDisabledColorFilter;
- }
- ColorMatrix brightnessMatrix = new ColorMatrix();
- float brightnessF = 0.5f;
- int brightnessI = (int) (255 * brightnessF);
- // Brightness: C-new = C-old*(1-amount) + amount
- float scale = 1f - brightnessF;
- float[] mat = brightnessMatrix.getArray();
- mat[0] = scale;
- mat[6] = scale;
- mat[12] = scale;
- mat[4] = brightnessI;
- mat[9] = brightnessI;
- mat[14] = brightnessI;
-
- ColorMatrix filterMatrix = new ColorMatrix();
- filterMatrix.setSaturation(0);
- filterMatrix.preConcat(brightnessMatrix);
-
- mDisabledColorFilter = new ColorMatrixColorFilter(filterMatrix);
- return mDisabledColorFilter;
- }
-}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a407e8e..bc81961 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -222,6 +222,7 @@
import com.android.server.wm.DisplayRotation;
import com.android.server.wm.WindowManagerInternal;
import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
+import com.android.server.wm.WindowManagerService;
import java.io.File;
import java.io.FileNotFoundException;
@@ -1913,6 +1914,7 @@
handleStartTransitionForKeyguardLw(keyguardGoingAway, 0 /* duration */);
}
});
+
mKeyguardDelegate = new KeyguardServiceDelegate(mContext,
new StateCallback() {
@Override
@@ -3136,7 +3138,7 @@
private int handleStartTransitionForKeyguardLw(boolean keyguardGoingAway, long duration) {
final int res = applyKeyguardOcclusionChange();
if (res != 0) return res;
- if (keyguardGoingAway) {
+ if (!WindowManagerService.sEnableRemoteKeyguardAnimation && keyguardGoingAway) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration);
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index e9d6440..db33e75 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -417,8 +417,7 @@
WindowManagerFuncs windowManagerFuncs);
/**
- * Check permissions when adding a window or a window token from
- * {@link android.app.WindowContext}.
+ * Check permissions when adding a window.
*
* @param type The window type
* @param isRoundedCornerOverlay {@code true} to indicate the adding window is
@@ -431,7 +430,6 @@
* else an error code, usually
* {@link WindowManagerGlobal#ADD_PERMISSION_DENIED}, to abort the add.
*
- * @see IWindowManager#addWindowTokenWithOptions(IBinder, int, int, Bundle, String)
* @see WindowManager.LayoutParams#PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY
*/
int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
@@ -1158,7 +1156,7 @@
* @param startTime the start time of the animation in uptime milliseconds
* @param fadeoutDuration the duration of the exit animation, in milliseconds
*/
- public void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
+ void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
/**
* Called when System UI has been started.
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index c2a1c79..a95628f 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -29,6 +29,7 @@
import com.android.internal.policy.IKeyguardService;
import com.android.server.UiThread;
import com.android.server.policy.WindowManagerPolicy.OnKeyguardExitResult;
+import com.android.server.wm.WindowManagerService;
import java.io.PrintWriter;
@@ -398,7 +399,7 @@
}
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
- if (mKeyguardService != null) {
+ if (!WindowManagerService.sEnableRemoteKeyguardAnimation && mKeyguardService != null) {
mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration);
}
}
diff --git a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
index 57e39b6..b995b19 100644
--- a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
+++ b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
@@ -45,6 +45,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.infra.ServiceConnector;
+import java.lang.ref.WeakReference;
+
/** Manages the connection to the remote rotation resolver service. */
class RemoteRotationResolverService extends ServiceConnector.Impl<IRotationResolverService> {
@@ -128,13 +130,20 @@
mProposedRotation = proposedRotation;
mCurrentRotation = currentRotation;
mPackageName = packageName;
- mIRotationResolverCallback = new RotationResolverCallback();
+ mIRotationResolverCallback = new RotationResolverCallback(this);
mCancellationSignalInternal = cancellationSignal;
mRequestStartTimeMillis = SystemClock.elapsedRealtime();
}
void cancelInternal() {
+ synchronized (mLock) {
+ if (mIsFulfilled) {
+ Slog.v(TAG, "Trying to cancel the request that has been already fulfilled.");
+ return;
+ }
+ mIsFulfilled = true;
+ }
Handler.getMain().post(() -> {
synchronized (mLock) {
try {
@@ -147,9 +156,6 @@
}
}
});
- synchronized (mLock) {
- mIsFulfilled = true;
- }
mCallbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED);
}
@@ -160,44 +166,53 @@
ipw.decreaseIndent();
}
- private class RotationResolverCallback extends IRotationResolverCallback.Stub {
+ private static class RotationResolverCallback extends IRotationResolverCallback.Stub {
+ private WeakReference<RotationRequest> mRequestWeakReference;
+
+ RotationResolverCallback(RotationRequest request) {
+ this.mRequestWeakReference = new WeakReference<>(request);
+ }
+
@Override
public void onSuccess(int rotation) {
- synchronized (mLock) {
- if (mIsFulfilled) {
+ final RotationRequest request = mRequestWeakReference.get();
+ synchronized (request.mLock) {
+ if (request.mIsFulfilled) {
Slog.w(TAG, "Callback received after the rotation request is fulfilled.");
return;
}
- mIsFulfilled = true;
- mCallbackInternal.onSuccess(rotation);
+ request.mIsFulfilled = true;
+ request.mCallbackInternal.onSuccess(rotation);
final long timeToCalculate =
- SystemClock.elapsedRealtime() - mRequestStartTimeMillis;
- logRotationStats(mProposedRotation, mCurrentRotation, rotation,
+ SystemClock.elapsedRealtime() - request.mRequestStartTimeMillis;
+ logRotationStats(request.mProposedRotation, request.mCurrentRotation, rotation,
timeToCalculate);
}
}
@Override
public void onFailure(int error) {
- synchronized (mLock) {
- if (mIsFulfilled) {
+ final RotationRequest request = mRequestWeakReference.get();
+ synchronized (request.mLock) {
+ if (request.mIsFulfilled) {
Slog.w(TAG, "Callback received after the rotation request is fulfilled.");
return;
}
- mIsFulfilled = true;
- mCallbackInternal.onFailure(error);
+ request.mIsFulfilled = true;
+ request.mCallbackInternal.onFailure(error);
final long timeToCalculate =
- SystemClock.elapsedRealtime() - mRequestStartTimeMillis;
- logRotationStats(mProposedRotation, mCurrentRotation, RESOLUTION_FAILURE,
- timeToCalculate);
+ SystemClock.elapsedRealtime() - request.mRequestStartTimeMillis;
+ logRotationStats(request.mProposedRotation, request.mCurrentRotation,
+ RESOLUTION_FAILURE, timeToCalculate);
}
}
@Override
public void onCancellable(@NonNull ICancellationSignal cancellation) {
- synchronized (mLock) {
- mCancellation = cancellation;
- if (mCancellationSignalInternal.isCanceled()) {
+ final RotationRequest request = mRequestWeakReference.get();
+ synchronized (request.mLock) {
+ request.mCancellation = cancellation;
+ if (request.mCancellationSignalInternal.isCanceled()) {
// Dispatch the cancellation signal if the client has cancelled the request.
try {
cancellation.cancel();
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
index 3dbc32a..6f7c016 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
@@ -122,14 +122,9 @@
}
});
- if (mRemoteService != null) {
- mRemoteService.resolveRotationLocked(mCurrentRequest);
- mCurrentRequest.mIsDispatched = true;
- } else {
- Slog.w(TAG, "Remote service is not available at this moment.");
- callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED);
- cancelLocked();
- }
+
+ mRemoteService.resolveRotationLocked(mCurrentRequest);
+ mCurrentRequest.mIsDispatched = true;
}
@GuardedBy("mLock")
@@ -198,15 +193,6 @@
if (mCurrentRequest == null) {
return;
}
-
- if (mCurrentRequest.mIsFulfilled) {
- if (isVerbose()) {
- Slog.d(TAG, "Trying to cancel the request that has been already fulfilled.");
- }
- mCurrentRequest = null;
- return;
- }
-
mCurrentRequest.cancelInternal();
mCurrentRequest = null;
}
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
index 4a37e79..e57d4ce 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 @@
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;
@@ -150,7 +150,7 @@
@Override
public void resolveRotation(
@NonNull RotationResolverCallbackInternal callbackInternal, int proposedRotation,
- int currentRotation, String packageName, long timeout,
+ int currentRotation, long timeout,
@NonNull CancellationSignal cancellationSignalInternal) {
Objects.requireNonNull(callbackInternal);
Objects.requireNonNull(cancellationSignalInternal);
@@ -159,7 +159,8 @@
final RotationResolverManagerPerUserService service = getServiceForUserLocked(
UserHandle.getCallingUserId());
service.resolveRotationLocked(callbackInternal, proposedRotation,
- currentRotation, packageName, timeout, cancellationSignalInternal);
+ currentRotation, /* packageName */ "", timeout,
+ cancellationSignalInternal);
} else {
Slog.w(TAG, "Rotation Resolver service is disabled.");
callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED);
@@ -191,7 +192,7 @@
TAG);
final RotationResolverManagerPerUserService service = getServiceForUserLocked(
UserHandle.getCallingUserId());
- new RotationResolverShellCommend(service).exec(this, in, out, err, args, callback,
+ new RotationResolverShellCommand(service).exec(this, in, out, err, args, callback,
resultReceiver);
}
}
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommend.java b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
similarity index 95%
rename from services/core/java/com/android/server/rotationresolver/RotationResolverShellCommend.java
rename to services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
index 0a873892..54a9edb 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommend.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverShellCommand.java
@@ -26,13 +26,13 @@
import java.io.PrintWriter;
-final class RotationResolverShellCommend extends ShellCommand {
+final class RotationResolverShellCommand extends ShellCommand {
private static final int INITIAL_RESULT_CODE = -1;
@NonNull
private final RotationResolverManagerPerUserService mService;
- RotationResolverShellCommend(@NonNull RotationResolverManagerPerUserService service) {
+ RotationResolverShellCommand(@NonNull RotationResolverManagerPerUserService service) {
mService = service;
}
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 263776c..5e681c6 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -2509,7 +2509,6 @@
try {
// force procstats to flush & combine old files into one store
long lastHighWaterMark = readProcStatsHighWaterMark(section);
- List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
ProtoOutputStream[] protoStreams = new ProtoOutputStream[MAX_PROCSTATS_SHARDS];
for (int i = 0; i < protoStreams.length; i++) {
@@ -2519,7 +2518,7 @@
ProcessStats procStats = new ProcessStats(false);
// Force processStatsService to aggregate all in-storage and in-memory data.
long highWaterMark = processStatsService.getCommittedStatsMerged(
- lastHighWaterMark, section, true, statsFiles, procStats);
+ lastHighWaterMark, section, true, null, procStats);
procStats.dumpAggregatedProtoForStatsd(protoStreams, MAX_PROCSTATS_RAW_SHARD_SIZE);
for (int i = 0; i < protoStreams.length; i++) {
diff --git a/services/core/java/com/android/server/tracing/OWNERS b/services/core/java/com/android/server/tracing/OWNERS
new file mode 100644
index 0000000..f5de4eb
--- /dev/null
+++ b/services/core/java/com/android/server/tracing/OWNERS
@@ -0,0 +1,2 @@
+cfijalkovich@google.com
+carmenjackson@google.com
diff --git a/services/core/java/com/android/server/tracing/TracingServiceProxy.java b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
new file mode 100644
index 0000000..8f22748
--- /dev/null
+++ b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.tracing;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.UserHandle;
+import android.tracing.ITracingServiceProxy;
+import android.util.Log;
+
+import com.android.server.SystemService;
+
+/**
+ * TracingServiceProxy is the system_server intermediary between the Perfetto tracing daemon and the
+ * system tracing app Traceur.
+ *
+ * @hide
+ */
+public class TracingServiceProxy extends SystemService {
+ private static final String TAG = "TracingServiceProxy";
+
+ public static final String TRACING_SERVICE_PROXY_BINDER_NAME = "tracing.proxy";
+
+ private static final String TRACING_APP_PACKAGE_NAME = "com.android.traceur";
+ private static final String TRACING_APP_ACTIVITY = "com.android.traceur.StopTraceService";
+
+ // Keep this in sync with the definitions in TraceService
+ private static final String INTENT_ACTION_NOTIFY_SESSION_STOPPED =
+ "com.android.traceur.NOTIFY_SESSION_STOPPED";
+ private static final String INTENT_ACTION_NOTIFY_SESSION_STOLEN =
+ "com.android.traceur.NOTIFY_SESSION_STOLEN";
+
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+
+ private final ITracingServiceProxy.Stub mTracingServiceProxy = new ITracingServiceProxy.Stub() {
+ /**
+ * Notifies system tracing app that a tracing session has ended. If a session is repurposed
+ * for use in a bugreport, sessionStolen can be set to indicate that tracing has ended but
+ * there is no buffer available to dump.
+ */
+ @Override
+ public void notifyTraceSessionEnded(boolean sessionStolen) {
+ notifyTraceur(sessionStolen);
+ }
+ };
+
+ public TracingServiceProxy(Context context) {
+ super(context);
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(TRACING_SERVICE_PROXY_BINDER_NAME, mTracingServiceProxy);
+ }
+
+ private void notifyTraceur(boolean sessionStolen) {
+ final Intent intent = new Intent();
+
+ try {
+ // Validate that Traceur is a system app.
+ PackageInfo info = mPackageManager.getPackageInfo(TRACING_APP_PACKAGE_NAME,
+ PackageManager.MATCH_SYSTEM_ONLY);
+
+ intent.setClassName(info.packageName, TRACING_APP_ACTIVITY);
+ if (sessionStolen) {
+ intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOLEN);
+ } else {
+ intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOPPED);
+ }
+
+ try {
+ mContext.startForegroundServiceAsUser(intent, UserHandle.SYSTEM);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Failed to notifyTraceSessionEnded", e);
+ }
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Failed to locate Traceur", e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/utils/Watchable.java b/services/core/java/com/android/server/utils/Watchable.java
index f936693..44a7459 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 @@
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 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/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index a82f239..3726407 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -16,6 +16,7 @@
package com.android.server.vcn;
+import static com.android.server.VcnManagementService.VDBG;
import android.annotation.NonNull;
import android.net.NetworkCapabilities;
@@ -29,6 +30,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import java.util.Collections;
@@ -37,6 +39,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Represents an single instance of a VCN.
@@ -82,10 +85,19 @@
/** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
+ /**
+ * Causes this VCN to immediately enter Safemode.
+ *
+ * <p>Upon entering Safemode, the VCN will unregister its RequestListener, tear down all of its
+ * VcnGatewayConnections, and notify VcnManagementService that it is in Safemode.
+ */
+ private static final int MSG_CMD_ENTER_SAFEMODE = MSG_CMD_BASE + 1;
+
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final Dependencies mDeps;
@NonNull private final VcnNetworkRequestListener mRequestListener;
+ @NonNull private final VcnSafemodeCallback mVcnSafemodeCallback;
@NonNull
private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections =
@@ -94,14 +106,33 @@
@NonNull private VcnConfig mConfig;
@NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
- private boolean mIsRunning = true;
+ /**
+ * Whether this Vcn instance is active and running.
+ *
+ * <p>The value will be {@code true} while running. It will be {@code false} if the VCN has been
+ * shut down or has entered safe mode.
+ *
+ * <p>This AtomicBoolean is required in order to ensure consistency and correctness across
+ * multiple threads. Unlike the rest of the Vcn, this is queried synchronously on Binder threads
+ * from VcnManagementService, and therefore cannot rely on guarantees of running on the VCN
+ * Looper.
+ */
+ // TODO(b/179429339): update when exiting safemode (when a new VcnConfig is provided)
+ private final AtomicBoolean mIsActive = new AtomicBoolean(true);
public Vcn(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
- @NonNull TelephonySubscriptionSnapshot snapshot) {
- this(vcnContext, subscriptionGroup, config, snapshot, new Dependencies());
+ @NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull VcnSafemodeCallback vcnSafemodeCallback) {
+ this(
+ vcnContext,
+ subscriptionGroup,
+ config,
+ snapshot,
+ vcnSafemodeCallback,
+ new Dependencies());
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -110,10 +141,13 @@
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config,
@NonNull TelephonySubscriptionSnapshot snapshot,
+ @NonNull VcnSafemodeCallback vcnSafemodeCallback,
@NonNull Dependencies deps) {
super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext;
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+ mVcnSafemodeCallback =
+ Objects.requireNonNull(vcnSafemodeCallback, "Missing vcnSafemodeCallback");
mDeps = Objects.requireNonNull(deps, "Missing deps");
mRequestListener = new VcnNetworkRequestListener();
@@ -143,6 +177,11 @@
sendMessageAtFrontOfQueue(obtainMessage(MSG_CMD_TEARDOWN));
}
+ /** Synchronously checks whether this Vcn is active. */
+ public boolean isActive() {
+ return mIsActive.get();
+ }
+
/** Get current Gateways for testing purposes */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public Set<VcnGatewayConnection> getVcnGatewayConnections() {
@@ -160,7 +199,7 @@
@Override
public void handleMessage(@NonNull Message msg) {
- if (!mIsRunning) {
+ if (!isActive()) {
return;
}
@@ -177,6 +216,9 @@
case MSG_CMD_TEARDOWN:
handleTeardown();
break;
+ case MSG_CMD_ENTER_SAFEMODE:
+ handleEnterSafemode();
+ break;
default:
Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what);
}
@@ -184,7 +226,7 @@
private void handleConfigUpdated(@NonNull VcnConfig config) {
// TODO: Add a dump function in VcnConfig that omits PII. Until then, use hashCode()
- Slog.v(getLogTag(), String.format("Config updated: config = %s", config.hashCode()));
+ Slog.v(getLogTag(), "Config updated: config = " + config.hashCode());
mConfig = config;
@@ -198,23 +240,41 @@
gatewayConnection.teardownAsynchronously();
}
- mIsRunning = false;
+ mIsActive.set(false);
+ }
+
+ private void handleEnterSafemode() {
+ handleTeardown();
+
+ mVcnSafemodeCallback.onEnteredSafemode();
}
private void handleNetworkRequested(
@NonNull NetworkRequest request, int score, int providerId) {
if (score > getNetworkScore()) {
- Slog.v(getLogTag(),
- "Request already satisfied by higher-scoring (" + score + ") network from "
- + "provider " + providerId + ": " + request);
+ if (VDBG) {
+ Slog.v(
+ getLogTag(),
+ "Request already satisfied by higher-scoring ("
+ + score
+ + ") network from "
+ + "provider "
+ + providerId
+ + ": "
+ + request);
+ }
return;
}
// If preexisting VcnGatewayConnection(s) satisfy request, return
for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) {
if (requestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
- Slog.v(getLogTag(),
- "Request already satisfied by existing VcnGatewayConnection: " + request);
+ if (VDBG) {
+ Slog.v(
+ getLogTag(),
+ "Request already satisfied by existing VcnGatewayConnection: "
+ + request);
+ }
return;
}
}
@@ -233,7 +293,8 @@
mVcnContext,
mSubscriptionGroup,
mLastSnapshot,
- gatewayConnectionConfig);
+ gatewayConnectionConfig,
+ new VcnGatewayStatusCallbackImpl());
mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
}
}
@@ -242,7 +303,7 @@
private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
mLastSnapshot = snapshot;
- if (mIsRunning) {
+ if (isActive()) {
for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
}
@@ -260,7 +321,7 @@
}
private String getLogTag() {
- return String.format("%s [%d]", TAG, mSubscriptionGroup.hashCode());
+ return TAG + " [" + mSubscriptionGroup.hashCode() + "]";
}
/** Retrieves the network score for a VCN Network */
@@ -271,6 +332,20 @@
return 52;
}
+ /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */
+ @VisibleForTesting(visibility = Visibility.PACKAGE)
+ public interface VcnGatewayStatusCallback {
+ /** Called by a VcnGatewayConnection to indicate that it has entered Safemode. */
+ void onEnteredSafemode();
+ }
+
+ private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback {
+ @Override
+ public void onEnteredSafemode() {
+ sendMessage(obtainMessage(MSG_CMD_ENTER_SAFEMODE));
+ }
+ }
+
/** External dependencies used by Vcn, for injection in tests */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class Dependencies {
@@ -279,9 +354,14 @@
VcnContext vcnContext,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
- VcnGatewayConnectionConfig connectionConfig) {
+ VcnGatewayConnectionConfig connectionConfig,
+ VcnGatewayStatusCallback gatewayStatusCallback) {
return new VcnGatewayConnection(
- vcnContext, subscriptionGroup, snapshot, connectionConfig);
+ vcnContext,
+ subscriptionGroup,
+ snapshot,
+ connectionConfig,
+ gatewayStatusCallback);
}
}
}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 853bb43..2503e81 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -61,7 +61,6 @@
import android.util.ArraySet;
import android.util.Slog;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.State;
@@ -69,12 +68,14 @@
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -297,9 +298,9 @@
private static final int EVENT_SETUP_COMPLETED = 6;
private static class EventSetupCompletedInfo implements EventInfo {
- @NonNull public final ChildSessionConfiguration childSessionConfig;
+ @NonNull public final VcnChildSessionConfiguration childSessionConfig;
- EventSetupCompletedInfo(@NonNull ChildSessionConfiguration childSessionConfig) {
+ EventSetupCompletedInfo(@NonNull VcnChildSessionConfiguration childSessionConfig) {
this.childSessionConfig = Objects.requireNonNull(childSessionConfig);
}
@@ -403,15 +404,13 @@
@NonNull
final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
- @NonNull private final Object mLock = new Object();
-
- @GuardedBy("mLock")
@NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
@NonNull private final VcnGatewayConnectionConfig mConnectionConfig;
+ @NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback;
@NonNull private final Dependencies mDeps;
@NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback;
@@ -473,7 +472,7 @@
* <p>Set in Connected and Migrating states, always @NonNull in Connected, Migrating
* states, @Nullable otherwise.
*/
- private ChildSessionConfiguration mChildConfig;
+ private VcnChildSessionConfiguration mChildConfig;
/**
* The active network agent.
@@ -487,8 +486,15 @@
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
- @NonNull VcnGatewayConnectionConfig connectionConfig) {
- this(vcnContext, subscriptionGroup, snapshot, connectionConfig, new Dependencies());
+ @NonNull VcnGatewayConnectionConfig connectionConfig,
+ @NonNull VcnGatewayStatusCallback gatewayStatusCallback) {
+ this(
+ vcnContext,
+ subscriptionGroup,
+ snapshot,
+ connectionConfig,
+ gatewayStatusCallback,
+ new Dependencies());
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -497,16 +503,17 @@
@NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull VcnGatewayConnectionConfig connectionConfig,
+ @NonNull VcnGatewayStatusCallback gatewayStatusCallback,
@NonNull Dependencies deps) {
super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext;
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig");
+ mGatewayStatusCallback =
+ Objects.requireNonNull(gatewayStatusCallback, "Missing gatewayStatusCallback");
mDeps = Objects.requireNonNull(deps, "Missing deps");
- synchronized (mLock) {
- mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
- }
+ mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
mUnderlyingNetworkTrackerCallback = new VcnUnderlyingNetworkTrackerCallback();
@@ -577,13 +584,10 @@
*/
public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
Objects.requireNonNull(snapshot, "Missing snapshot");
+ mVcnContext.ensureRunningOnLooperThread();
- // Vcn is the only user of this method and runs on the same Thread, but lock around
- // mLastSnapshot to be technically correct.
- synchronized (mLock) {
- mLastSnapshot = snapshot;
- mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot);
- }
+ mLastSnapshot = snapshot;
+ mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot);
sendMessage(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL);
}
@@ -656,7 +660,7 @@
new EventTransformCreatedInfo(direction, transform));
}
- private void childOpened(int token, @NonNull ChildSessionConfiguration childConfig) {
+ private void childOpened(int token, @NonNull VcnChildSessionConfiguration childConfig) {
sendMessage(EVENT_SETUP_COMPLETED, token, new EventSetupCompletedInfo(childConfig));
}
@@ -1005,7 +1009,7 @@
protected void updateNetworkAgent(
@NonNull IpSecTunnelInterface tunnelIface,
@NonNull NetworkAgent agent,
- @NonNull ChildSessionConfiguration childConfig) {
+ @NonNull VcnChildSessionConfiguration childConfig) {
final NetworkCapabilities caps =
buildNetworkCapabilities(mConnectionConfig, mUnderlying);
final LinkProperties lp =
@@ -1017,7 +1021,7 @@
protected NetworkAgent buildNetworkAgent(
@NonNull IpSecTunnelInterface tunnelIface,
- @NonNull ChildSessionConfiguration childConfig) {
+ @NonNull VcnChildSessionConfiguration childConfig) {
final NetworkCapabilities caps =
buildNetworkCapabilities(mConnectionConfig, mUnderlying);
final LinkProperties lp =
@@ -1065,15 +1069,15 @@
protected void setupInterface(
int token,
@NonNull IpSecTunnelInterface tunnelIface,
- @NonNull ChildSessionConfiguration childConfig) {
+ @NonNull VcnChildSessionConfiguration childConfig) {
setupInterface(token, tunnelIface, childConfig, null);
}
protected void setupInterface(
int token,
@NonNull IpSecTunnelInterface tunnelIface,
- @NonNull ChildSessionConfiguration childConfig,
- @Nullable ChildSessionConfiguration oldChildConfig) {
+ @NonNull VcnChildSessionConfiguration childConfig,
+ @Nullable VcnChildSessionConfiguration oldChildConfig) {
try {
final Set<LinkAddress> newAddrs =
new ArraySet<>(childConfig.getInternalAddresses());
@@ -1186,7 +1190,7 @@
protected void setupInterfaceAndNetworkAgent(
int token,
@NonNull IpSecTunnelInterface tunnelIface,
- @NonNull ChildSessionConfiguration childConfig) {
+ @NonNull VcnChildSessionConfiguration childConfig) {
setupInterface(token, tunnelIface, childConfig);
if (mNetworkAgent == null) {
@@ -1204,7 +1208,64 @@
*/
class RetryTimeoutState extends ActiveBaseState {
@Override
- protected void processStateMsg(Message msg) {}
+ protected void enterState() throws Exception {
+ // Reset upon entry to ConnectedState
+ mFailedAttempts++;
+
+ if (mUnderlying == null) {
+ Slog.wtf(TAG, "Underlying network was null in retry state");
+ transitionTo(mDisconnectedState);
+ } else {
+ sendMessageDelayed(
+ EVENT_RETRY_TIMEOUT_EXPIRED, mCurrentToken, getNextRetryIntervalsMs());
+ }
+ }
+
+ @Override
+ protected void processStateMsg(Message msg) {
+ switch (msg.what) {
+ case EVENT_UNDERLYING_NETWORK_CHANGED:
+ final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
+ mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+ // If new underlying is null, all networks were lost; go back to disconnected.
+ if (mUnderlying == null) {
+ removeMessages(EVENT_RETRY_TIMEOUT_EXPIRED);
+
+ transitionTo(mDisconnectedState);
+ return;
+ } else if (oldUnderlying != null
+ && mUnderlying.network.equals(oldUnderlying.network)) {
+ // If the network has not changed, do nothing.
+ return;
+ }
+
+ // Fallthrough
+ case EVENT_RETRY_TIMEOUT_EXPIRED:
+ removeMessages(EVENT_RETRY_TIMEOUT_EXPIRED);
+
+ transitionTo(mConnectingState);
+ break;
+ case EVENT_DISCONNECT_REQUESTED:
+ handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
+ break;
+ default:
+ logUnhandledMessage(msg);
+ break;
+ }
+ }
+
+ private long getNextRetryIntervalsMs() {
+ final int retryDelayIndex = mFailedAttempts - 1;
+ final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMs();
+
+ // Repeatedly use last item in retry timeout list.
+ if (retryDelayIndex >= retryIntervalsMs.length) {
+ return retryIntervalsMs[retryIntervalsMs.length - 1];
+ }
+
+ return retryIntervalsMs[retryDelayIndex];
+ }
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -1274,7 +1335,7 @@
private static LinkProperties buildConnectedLinkProperties(
@NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
@NonNull IpSecTunnelInterface tunnelIface,
- @NonNull ChildSessionConfiguration childConfig) {
+ @NonNull VcnChildSessionConfiguration childConfig) {
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName(tunnelIface.getInterfaceName());
@@ -1325,17 +1386,25 @@
}
}
- private class ChildSessionCallbackImpl implements ChildSessionCallback {
+ /** Implementation of ChildSessionCallback, exposed for testing. */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public class VcnChildSessionCallback implements ChildSessionCallback {
private final int mToken;
- ChildSessionCallbackImpl(int token) {
+ VcnChildSessionCallback(int token) {
mToken = token;
}
+ /** Internal proxy method for injecting of mocked ChildSessionConfiguration */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void onOpened(@NonNull VcnChildSessionConfiguration childConfig) {
+ Slog.v(TAG, "ChildOpened for token " + mToken);
+ childOpened(mToken, childConfig);
+ }
+
@Override
public void onOpened(@NonNull ChildSessionConfiguration childConfig) {
- Slog.v(TAG, "ChildOpened for token " + mToken);
- childOpened(mToken, childConfig);
+ onOpened(new VcnChildSessionConfiguration(childConfig));
}
@Override
@@ -1418,7 +1487,7 @@
buildIkeParams(),
buildChildParams(),
new IkeSessionCallbackImpl(token),
- new ChildSessionCallbackImpl(token));
+ new VcnChildSessionCallback(token));
}
/** External dependencies used by VcnGatewayConnection, for injection in tests */
@@ -1455,6 +1524,35 @@
}
}
+ /**
+ * Proxy implementation of Child Session Configuration, used for testing.
+ *
+ * <p>This wrapper allows mocking of the final, parcelable ChildSessionConfiguration object for
+ * testing purposes. This is the unfortunate result of mockito-inline (for mocking final
+ * classes) not working properly with system services & associated classes.
+ *
+ * <p>This class MUST EXCLUSIVELY be a passthrough, proxying calls directly to the actual
+ * ChildSessionConfiguration.
+ */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class VcnChildSessionConfiguration {
+ private final ChildSessionConfiguration mChildConfig;
+
+ public VcnChildSessionConfiguration(ChildSessionConfiguration childConfig) {
+ mChildConfig = childConfig;
+ }
+
+ /** Retrieves the addresses to be used inside the tunnel. */
+ public List<LinkAddress> getInternalAddresses() {
+ return mChildConfig.getInternalAddresses();
+ }
+
+ /** Retrieves the DNS servers to be used inside the tunnel. */
+ public List<InetAddress> getInternalDnsServers() {
+ return mChildConfig.getInternalDnsServers();
+ }
+ }
+
/** Proxy implementation of IKE session, used for testing. */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class VcnIkeSession {
diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
index fe4ea30..bfeec01 100644
--- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
+++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
@@ -16,6 +16,8 @@
package com.android.server.vcn;
+import static com.android.server.VcnManagementService.VDBG;
+
import android.annotation.NonNull;
import android.content.Context;
import android.net.NetworkProvider;
@@ -83,11 +85,16 @@
@Override
public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
- Slog.v(
- TAG,
- String.format(
- "Network requested: Request = %s, score = %d, providerId = %d",
- request, score, providerId));
+ if (VDBG) {
+ Slog.v(
+ TAG,
+ "Network requested: Request = "
+ + request
+ + ", score = "
+ + score
+ + ", providerId = "
+ + providerId);
+ }
final NetworkRequestEntry entry = new NetworkRequestEntry(request, score, providerId);
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 4f2fc86..bee66637 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 @@
* 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 @@
}
}
+ /** 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 @@
/** 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 @@
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 @@
@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 @@
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 @@
return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
}
} finally {
- if (timeout > 0) {
+ if (duration > 0) {
noteVibratorOff();
}
if (DEBUG) {
@@ -628,14 +648,48 @@
}
/**
+ * 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 @@
/**
* 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 @@
}
}
}
+
+ /**
+ * 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 @@
// 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/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 3198453..5697564 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1094,7 +1094,8 @@
return;
}
if (DEBUG) Slog.v(TAG, "Adding window token: " + mToken);
- mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId);
+ mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId,
+ null /* options */);
final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId);
try {
connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false,
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 2ecefea..3bbc81a 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -16,12 +16,27 @@
package com.android.server.wm;
+import static android.os.Build.IS_USER;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
+import static com.android.server.accessibility.AccessibilityTraceFileProto.ENTRY;
+import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER;
+import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER_H;
+import static com.android.server.accessibility.AccessibilityTraceFileProto.MAGIC_NUMBER_L;
+import static com.android.server.accessibility.AccessibilityTraceProto.ACCESSIBILITY_SERVICE;
+import static com.android.server.accessibility.AccessibilityTraceProto.CALENDAR_TIME;
+import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PARAMS;
+import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_PKG;
+import static com.android.server.accessibility.AccessibilityTraceProto.CALLING_STACKS;
+import static com.android.server.accessibility.AccessibilityTraceProto.ELAPSED_REALTIME_NANOS;
+import static com.android.server.accessibility.AccessibilityTraceProto.PROCESS_NAME;
+import static com.android.server.accessibility.AccessibilityTraceProto.THREAD_ID_NAME;
+import static com.android.server.accessibility.AccessibilityTraceProto.WHERE;
+import static com.android.server.accessibility.AccessibilityTraceProto.WINDOW_MANAGER_SERVICE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.utils.RegionUtils.forEachRect;
@@ -29,7 +44,9 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
+import android.app.Application;
import android.content.Context;
+import android.content.pm.PackageManagerInternal;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
@@ -41,15 +58,20 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.os.Binder;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TypedValue;
+import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.InsetsSource;
import android.view.MagnificationSpec;
@@ -64,13 +86,22 @@
import com.android.internal.R;
import com.android.internal.os.SomeArgs;
+import com.android.internal.util.TraceBuffer;
+import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal;
import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks;
import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback;
+import java.io.File;
+import java.io.IOException;
import java.io.PrintWriter;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -79,26 +110,37 @@
* This class contains the accessibility related logic of the window manager.
*/
final class AccessibilityController {
+ private static final String TAG = AccessibilityController.class.getSimpleName();
+ private static final Object STATIC_LOCK = new Object();
+ static AccessibilityControllerInternal
+ getAccessibilityControllerInternal(WindowManagerService service) {
+ return AccessibilityControllerInternalImpl.getInstance(service);
+ }
+
+ private final AccessibilityTracing mAccessibilityTracing;
private final WindowManagerService mService;
-
private static final Rect EMPTY_RECT = new Rect();
private static final float[] sTempFloats = new float[9];
- public AccessibilityController(WindowManagerService service) {
+ AccessibilityController(WindowManagerService service) {
mService = service;
+ mAccessibilityTracing = AccessibilityTracing.getInstance(service);
}
private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>();
-
private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver =
new SparseArray<>();
// Set to true if initializing window population complete.
private boolean mAllObserversInitialized = true;
- public boolean setMagnificationCallbacksLocked(int displayId,
- MagnificationCallbacks callbacks) {
+ boolean setMagnificationCallbacks(int displayId, MagnificationCallbacks callbacks) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ TAG + ".setMagnificationCallbacks",
+ "displayId=" + displayId + "; callbacks={" + callbacks + "}");
+ }
boolean result = false;
if (callbacks != null) {
if (mDisplayMagnifiers.get(displayId) != null) {
@@ -118,7 +160,7 @@
if (displayMagnifier == null) {
throw new IllegalStateException("Magnification callbacks already cleared!");
}
- displayMagnifier.destroyLocked();
+ displayMagnifier.destroy();
mDisplayMagnifiers.remove(displayId);
result = true;
}
@@ -133,8 +175,13 @@
* @param callback The callback.
* @return {@code false} if display id is not valid or an embedded display.
*/
- public boolean setWindowsForAccessibilityCallbackLocked(int displayId,
+ boolean setWindowsForAccessibilityCallback(int displayId,
WindowsForAccessibilityCallback callback) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ TAG + ".setWindowsForAccessibilityCallback",
+ "displayId=" + displayId + "; callback={" + callback + "}");
+ }
final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
if (dc == null) {
return false;
@@ -147,7 +194,7 @@
// empty, that means this mapping didn't be set, and needs to do this again.
// This happened when accessibility window observer is disabled and enabled again.
if (mWindowsForAccessibilityObserver.get(displayId) == null) {
- handleWindowObserverOfEmbeddedDisplayLocked(displayId, dc.getParentWindow());
+ handleWindowObserverOfEmbeddedDisplay(displayId, dc.getParentWindow());
}
return false;
} else if (mWindowsForAccessibilityObserver.get(displayId) != null) {
@@ -181,7 +228,12 @@
return true;
}
- public void performComputeChangedWindowsNotLocked(int displayId, boolean forceSend) {
+ void performComputeChangedWindowsNot(int displayId, boolean forceSend) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ TAG + ".performComputeChangedWindowsNot",
+ "displayId=" + displayId + "; forceSend=" + forceSend);
+ }
WindowsForAccessibilityObserver observer = null;
synchronized (mService.mGlobalLock) {
final WindowsForAccessibilityObserver windowsForA11yObserver =
@@ -191,86 +243,119 @@
}
}
if (observer != null) {
- observer.performComputeChangedWindowsNotLocked(forceSend);
+ observer.performComputeChangedWindows(forceSend);
}
}
- public void setMagnificationSpecLocked(int displayId, MagnificationSpec spec) {
+ void setMagnificationSpec(int displayId, MagnificationSpec spec) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".setMagnificationSpec",
+ "displayId=" + displayId + "; spec={" + spec + "}");
+ }
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.setMagnificationSpecLocked(spec);
+ displayMagnifier.setMagnificationSpec(spec);
}
final WindowsForAccessibilityObserver windowsForA11yObserver =
mWindowsForAccessibilityObserver.get(displayId);
if (windowsForA11yObserver != null) {
- windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
+ windowsForA11yObserver.scheduleComputeChangedWindows();
}
}
- public void getMagnificationRegionLocked(int displayId, Region outMagnificationRegion) {
+ void getMagnificationRegion(int displayId, Region outMagnificationRegion) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".getMagnificationRegion",
+ "displayId=" + displayId + "; outMagnificationRegion={" + outMagnificationRegion
+ + "}");
+ }
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.getMagnificationRegionLocked(outMagnificationRegion);
+ displayMagnifier.getMagnificationRegion(outMagnificationRegion);
}
}
- public void onRectangleOnScreenRequestedLocked(int displayId, Rect rectangle) {
+ void onRectangleOnScreenRequested(int displayId, Rect rectangle) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ TAG + ".onRectangleOnScreenRequested",
+ "displayId=" + displayId + "; rectangle={" + rectangle + "}");
+ }
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
+ displayMagnifier.onRectangleOnScreenRequested(rectangle);
}
// Not relevant for the window observer.
}
- public void onWindowLayersChangedLocked(int displayId) {
+ void onWindowLayersChanged(int displayId) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ TAG + ".onWindowLayersChanged", "displayId=" + displayId);
+ }
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.onWindowLayersChangedLocked();
+ displayMagnifier.onWindowLayersChanged();
}
final WindowsForAccessibilityObserver windowsForA11yObserver =
mWindowsForAccessibilityObserver.get(displayId);
if (windowsForA11yObserver != null) {
- windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
+ windowsForA11yObserver.scheduleComputeChangedWindows();
}
}
- public void onRotationChangedLocked(DisplayContent displayContent) {
+ void onRotationChanged(DisplayContent displayContent) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".onRotationChanged",
+ "displayContent={" + displayContent + "}");
+ }
final int displayId = displayContent.getDisplayId();
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.onRotationChangedLocked(displayContent);
+ displayMagnifier.onRotationChanged(displayContent);
}
final WindowsForAccessibilityObserver windowsForA11yObserver =
mWindowsForAccessibilityObserver.get(displayId);
if (windowsForA11yObserver != null) {
- windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
+ windowsForA11yObserver.scheduleComputeChangedWindows();
}
}
- public void onAppWindowTransitionLocked(int displayId, int transition) {
+ void onAppWindowTransition(int displayId, int transition) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".onAppWindowTransition",
+ "displayId=" + displayId + "; transition=" + transition);
+ }
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.onAppWindowTransitionLocked(displayId, transition);
+ displayMagnifier.onAppWindowTransition(displayId, transition);
}
// Not relevant for the window observer.
}
- public void onWindowTransitionLocked(WindowState windowState, int transition) {
+ void onWindowTransition(WindowState windowState, int transition) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".onWindowTransition",
+ "windowState={" + windowState + "}; transition=" + transition);
+ }
final int displayId = windowState.getDisplayId();
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.onWindowTransitionLocked(windowState, transition);
+ displayMagnifier.onWindowTransition(windowState, transition);
}
final WindowsForAccessibilityObserver windowsForA11yObserver =
mWindowsForAccessibilityObserver.get(displayId);
if (windowsForA11yObserver != null) {
- windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
+ windowsForA11yObserver.scheduleComputeChangedWindows();
}
}
- public void onWindowFocusChangedNotLocked(int displayId) {
+ void onWindowFocusChangedNot(int displayId) {
// Not relevant for the display magnifier.
-
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ TAG + ".onWindowFocusChangedNot", "displayId=" + displayId);
+ }
WindowsForAccessibilityObserver observer = null;
synchronized (mService.mGlobalLock) {
final WindowsForAccessibilityObserver windowsForA11yObserver =
@@ -280,7 +365,7 @@
}
}
if (observer != null) {
- observer.performComputeChangedWindowsNotLocked(false);
+ observer.performComputeChangedWindows(false);
}
// Since we abandon initializing observers if no window has focus, make sure all observers
// are initialized.
@@ -311,7 +396,7 @@
boolean areAllObserversInitialized = true;
for (int i = unInitializedObservers.size() - 1; i >= 0; --i) {
final WindowsForAccessibilityObserver observer = unInitializedObservers.get(i);
- observer.performComputeChangedWindowsNotLocked(true);
+ observer.performComputeChangedWindows(true);
areAllObserversInitialized &= observer.mInitialized;
}
synchronized (mService.mGlobalLock) {
@@ -324,50 +409,89 @@
* another display is also taken into consideration.
* @param displayIds the display ids of displays when the situation happens.
*/
- public void onSomeWindowResizedOrMovedLocked(int... displayIds) {
+ void onSomeWindowResizedOrMoved(int... displayIds) {
+ onSomeWindowResizedOrMovedWithCallingUid(Binder.getCallingUid(), displayIds);
+ }
+
+ void onSomeWindowResizedOrMovedWithCallingUid(int callingUid, int... displayIds) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ TAG + ".onSomeWindowResizedOrMoved",
+ "displayIds={" + displayIds.toString() + "}",
+ "".getBytes(),
+ callingUid);
+ }
// Not relevant for the display magnifier.
for (int i = 0; i < displayIds.length; i++) {
final WindowsForAccessibilityObserver windowsForA11yObserver =
mWindowsForAccessibilityObserver.get(displayIds[i]);
if (windowsForA11yObserver != null) {
- windowsForA11yObserver.scheduleComputeChangedWindowsLocked();
+ windowsForA11yObserver.scheduleComputeChangedWindows();
}
}
}
- public void drawMagnifiedRegionBorderIfNeededLocked(int displayId,
- SurfaceControl.Transaction t) {
+ void drawMagnifiedRegionBorderIfNeeded(int displayId, SurfaceControl.Transaction t) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ TAG + ".drawMagnifiedRegionBorderIfNeeded",
+ "displayId=" + displayId + "; transaction={" + t + "}");
+ }
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.drawMagnifiedRegionBorderIfNeededLocked(t);
+ displayMagnifier.drawMagnifiedRegionBorderIfNeeded(t);
}
// Not relevant for the window observer.
}
- public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
+ MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".getMagnificationSpecForWindow",
+ "windowState={" + windowState + "}");
+ }
final int displayId = windowState.getDisplayId();
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- return displayMagnifier.getMagnificationSpecForWindowLocked(windowState);
+ return displayMagnifier.getMagnificationSpecForWindow(windowState);
}
return null;
}
- public boolean hasCallbacksLocked() {
+ boolean hasCallbacks() {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".hasCallbacks");
+ }
return (mDisplayMagnifiers.size() > 0
|| mWindowsForAccessibilityObserver.size() > 0);
}
- public void setForceShowMagnifiableBoundsLocked(int displayId, boolean show) {
+ void setForceShowMagnifiableBounds(int displayId, boolean show) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".setForceShowMagnifiableBounds",
+ "displayId=" + displayId + "; show=" + show);
+ }
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
- displayMagnifier.setForceShowMagnifiableBoundsLocked(show);
+ displayMagnifier.setForceShowMagnifiableBounds(show);
displayMagnifier.showMagnificationBoundsIfNeeded();
}
}
- public void handleWindowObserverOfEmbeddedDisplayLocked(int embeddedDisplayId,
+ void handleWindowObserverOfEmbeddedDisplay(int embeddedDisplayId,
WindowState parentWindow) {
+ handleWindowObserverOfEmbeddedDisplay(
+ embeddedDisplayId, parentWindow, Binder.getCallingUid());
+ }
+
+ void handleWindowObserverOfEmbeddedDisplay(
+ int embeddedDisplayId, WindowState parentWindow, int callingUid) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(TAG + ".handleWindowObserverOfEmbeddedDisplay",
+ "embeddedDisplayId=" + embeddedDisplayId + "; parentWindowState={"
+ + parentWindow + "}",
+ "".getBytes(),
+ callingUid);
+ }
if (embeddedDisplayId == Display.DEFAULT_DISPLAY || parentWindow == null) {
return;
}
@@ -390,7 +514,7 @@
}
}
- private static void populateTransformationMatrixLocked(WindowState windowState,
+ private static void populateTransformationMatrix(WindowState windowState,
Matrix outMatrix) {
windowState.getTransformationMatrix(sTempFloats, outMatrix);
}
@@ -451,6 +575,7 @@
private final Handler mHandler;
private final DisplayContent mDisplayContent;
private final Display mDisplay;
+ private final AccessibilityTracing mAccessibilityTracing;
private final MagnificationCallbacks mCallbacks;
@@ -458,7 +583,7 @@
private boolean mForceShowMagnifiableBounds = false;
- public DisplayMagnifier(WindowManagerService windowManagerService,
+ DisplayMagnifier(WindowManagerService windowManagerService,
DisplayContent displayContent,
Display display,
MagnificationCallbacks callbacks) {
@@ -469,36 +594,58 @@
mDisplay = display;
mHandler = new MyHandler(mService.mH.getLooper());
mMagnifedViewport = new MagnifiedViewport();
+ mAccessibilityTracing = AccessibilityTracing.getInstance(mService);
mLongAnimationDuration = mDisplayContext.getResources().getInteger(
com.android.internal.R.integer.config_longAnimTime);
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".DisplayMagnifier.constructor",
+ "windowManagerService={" + windowManagerService + "}; displayContent={"
+ + displayContent + "}; display={" + display + "}; callbacks={"
+ + callbacks + "}");
+ }
}
- public void setMagnificationSpecLocked(MagnificationSpec spec) {
- mMagnifedViewport.updateMagnificationSpecLocked(spec);
- mMagnifedViewport.recomputeBoundsLocked();
+ void setMagnificationSpec(MagnificationSpec spec) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ LOG_TAG + ".setMagnificationSpec", "spec={" + spec + "}");
+ }
+ mMagnifedViewport.updateMagnificationSpec(spec);
+ mMagnifedViewport.recomputeBounds();
mService.applyMagnificationSpecLocked(mDisplay.getDisplayId(), spec);
mService.scheduleAnimationLocked();
}
- public void setForceShowMagnifiableBoundsLocked(boolean show) {
+ void setForceShowMagnifiableBounds(boolean show) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ LOG_TAG + ".setForceShowMagnifiableBounds", "show=" + show);
+ }
mForceShowMagnifiableBounds = show;
- mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true);
+ mMagnifedViewport.setMagnifiedRegionBorderShown(show, true);
}
- public boolean isForceShowingMagnifiableBoundsLocked() {
+ boolean isForceShowingMagnifiableBounds() {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".isForceShowingMagnifiableBounds");
+ }
return mForceShowMagnifiableBounds;
}
- public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
+ void onRectangleOnScreenRequested(Rect rectangle) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ LOG_TAG + ".onRectangleOnScreenRequested", "rectangle={" + rectangle + "}");
+ }
if (DEBUG_RECTANGLE_REQUESTED) {
Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
}
- if (!mMagnifedViewport.isMagnifyingLocked()) {
+ if (!mMagnifedViewport.isMagnifying()) {
return;
}
Rect magnifiedRegionBounds = mTempRect2;
- mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
+ mMagnifedViewport.getMagnifiedFrameInContentCoords(magnifiedRegionBounds);
if (magnifiedRegionBounds.contains(rectangle)) {
return;
}
@@ -511,31 +658,42 @@
args).sendToTarget();
}
- public void onWindowLayersChangedLocked() {
+ void onWindowLayersChanged() {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".onWindowLayersChanged");
+ }
if (DEBUG_LAYERS) {
Slog.i(LOG_TAG, "Layers changed.");
}
- mMagnifedViewport.recomputeBoundsLocked();
+ mMagnifedViewport.recomputeBounds();
mService.scheduleAnimationLocked();
}
- public void onRotationChangedLocked(DisplayContent displayContent) {
+ void onRotationChanged(DisplayContent displayContent) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ LOG_TAG + ".onRotationChanged", "displayContent={" + displayContent + "}");
+ }
if (DEBUG_ROTATION) {
final int rotation = displayContent.getRotation();
Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
+ " displayId: " + displayContent.getDisplayId());
}
- mMagnifedViewport.onRotationChangedLocked(displayContent.getPendingTransaction());
+ mMagnifedViewport.onRotationChanged(displayContent.getPendingTransaction());
mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
}
- public void onAppWindowTransitionLocked(int displayId, int transition) {
+ void onAppWindowTransition(int displayId, int transition) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".onAppWindowTransition",
+ "displayId=" + displayId + "; transition=" + transition);
+ }
if (DEBUG_WINDOW_TRANSITIONS) {
Slog.i(LOG_TAG, "Window transition: "
+ AppTransition.appTransitionOldToString(transition)
+ " displayId: " + displayId);
}
- final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
+ final boolean magnifying = mMagnifedViewport.isMagnifying();
if (magnifying) {
switch (transition) {
case WindowManager.TRANSIT_OLD_ACTIVITY_OPEN:
@@ -550,13 +708,17 @@
}
}
- public void onWindowTransitionLocked(WindowState windowState, int transition) {
+ void onWindowTransition(WindowState windowState, int transition) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".onWindowTransition",
+ "windowState={" + windowState + "}; transition=" + transition);
+ }
if (DEBUG_WINDOW_TRANSITIONS) {
Slog.i(LOG_TAG, "Window transition: "
+ AppTransition.appTransitionOldToString(transition)
+ " displayId: " + windowState.getDisplayId());
}
- final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
+ final boolean magnifying = mMagnifedViewport.isMagnifying();
final int type = windowState.mAttrs.type;
switch (transition) {
case WindowManagerPolicy.TRANSIT_ENTER:
@@ -586,7 +748,7 @@
case WindowManager.LayoutParams.TYPE_QS_DIALOG:
case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: {
Rect magnifiedRegionBounds = mTempRect2;
- mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
+ mMagnifedViewport.getMagnifiedFrameInContentCoords(
magnifiedRegionBounds);
Rect touchableRegionBounds = mTempRect1;
windowState.getTouchableRegion(mTempRegion1);
@@ -604,8 +766,12 @@
}
}
- public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
- MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
+ MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationSpecForWindow",
+ "windowState={" + windowState + "}");
+ }
+ MagnificationSpec spec = mMagnifedViewport.getMagnificationSpec();
if (spec != null && !spec.isNop()) {
if (!windowState.shouldMagnify()) {
return null;
@@ -614,24 +780,38 @@
return spec;
}
- public void getMagnificationRegionLocked(Region outMagnificationRegion) {
+ void getMagnificationRegion(Region outMagnificationRegion) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".getMagnificationRegion",
+ "outMagnificationRegion={" + outMagnificationRegion + "}");
+ }
// Make sure we're working with the most current bounds
- mMagnifedViewport.recomputeBoundsLocked();
- mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion);
+ mMagnifedViewport.recomputeBounds();
+ mMagnifedViewport.getMagnificationRegion(outMagnificationRegion);
}
- public void destroyLocked() {
+ void destroy() {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".destroy");
+ }
mMagnifedViewport.destroyWindow();
}
// Can be called outside of a surface transaction
- public void showMagnificationBoundsIfNeeded() {
+ void showMagnificationBoundsIfNeeded() {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".showMagnificationBoundsIfNeeded");
+ }
mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)
.sendToTarget();
}
- public void drawMagnifiedRegionBorderIfNeededLocked(SurfaceControl.Transaction t) {
- mMagnifedViewport.drawWindowIfNeededLocked(t);
+ void drawMagnifiedRegionBorderIfNeeded(SurfaceControl.Transaction t) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".drawMagnifiedRegionBorderIfNeeded",
+ "transition={" + t + "}");
+ }
+ mMagnifedViewport.drawWindowIfNeeded(t);
}
void dump(PrintWriter pw, String prefix) {
@@ -665,7 +845,7 @@
private boolean mFullRedrawNeeded;
private int mTempLayer = 0;
- public MagnifiedViewport() {
+ MagnifiedViewport() {
mBorderWidth = mDisplayContext.getResources().getDimension(
com.android.internal.R.dimen.accessibility_magnification_indicator_width);
mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2);
@@ -681,14 +861,14 @@
mCircularPath = null;
}
- recomputeBoundsLocked();
+ recomputeBounds();
}
- public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) {
+ void getMagnificationRegion(@NonNull Region outMagnificationRegion) {
outMagnificationRegion.set(mMagnificationRegion);
}
- public void updateMagnificationSpecLocked(MagnificationSpec spec) {
+ void updateMagnificationSpec(MagnificationSpec spec) {
if (spec != null) {
mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
} else {
@@ -698,12 +878,12 @@
// to show the border. We will do so when the pending message is handled.
if (!mHandler.hasMessages(
MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
- setMagnifiedRegionBorderShownLocked(
- isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true);
+ setMagnifiedRegionBorderShown(
+ isMagnifying() || isForceShowingMagnifiableBounds(), true);
}
}
- public void recomputeBoundsLocked() {
+ void recomputeBounds() {
mDisplay.getRealSize(mTempPoint);
final int screenWidth = mTempPoint.x;
final int screenHeight = mTempPoint.y;
@@ -721,7 +901,7 @@
SparseArray<WindowState> visibleWindows = mTempWindowStates;
visibleWindows.clear();
- populateWindowsOnScreenLocked(visibleWindows);
+ populateWindowsOnScreen(visibleWindows);
final int visibleWindowCount = visibleWindows.size();
for (int i = visibleWindowCount - 1; i >= 0; i--) {
@@ -736,7 +916,7 @@
// Consider the touchable portion of the window
Matrix matrix = mTempMatrix;
- populateTransformationMatrixLocked(windowState, matrix);
+ populateTransformationMatrix(windowState, matrix);
Region touchableRegion = mTempRegion3;
windowState.getTouchableRegion(touchableRegion);
Rect touchableFrame = mTempRect1;
@@ -848,24 +1028,24 @@
return letterboxBounds;
}
- public void onRotationChangedLocked(SurfaceControl.Transaction t) {
+ void onRotationChanged(SurfaceControl.Transaction t) {
// If we are showing the magnification border, hide it immediately so
// the user does not see strange artifacts during rotation. The screenshot
// used for rotation already has the border. After the rotation is complete
// we will show the border.
- if (isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked()) {
- setMagnifiedRegionBorderShownLocked(false, false);
+ if (isMagnifying() || isForceShowingMagnifiableBounds()) {
+ setMagnifiedRegionBorderShown(false, false);
final long delay = (long) (mLongAnimationDuration
* mService.getWindowAnimationScaleLocked());
Message message = mHandler.obtainMessage(
MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
mHandler.sendMessageDelayed(message, delay);
}
- recomputeBoundsLocked();
+ recomputeBounds();
mWindow.updateSize(t);
}
- public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
+ void setMagnifiedRegionBorderShown(boolean shown, boolean animate) {
if (shown) {
mFullRedrawNeeded = true;
mOldMagnificationRegion.set(0, 0, 0, 0);
@@ -873,31 +1053,31 @@
mWindow.setShown(shown, animate);
}
- public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
+ void getMagnifiedFrameInContentCoords(Rect rect) {
MagnificationSpec spec = mMagnificationSpec;
mMagnificationRegion.getBounds(rect);
rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
rect.scale(1.0f / spec.scale);
}
- public boolean isMagnifyingLocked() {
+ boolean isMagnifying() {
return mMagnificationSpec.scale > 1.0f;
}
- public MagnificationSpec getMagnificationSpecLocked() {
+ MagnificationSpec getMagnificationSpec() {
return mMagnificationSpec;
}
- public void drawWindowIfNeededLocked(SurfaceControl.Transaction t) {
- recomputeBoundsLocked();
+ void drawWindowIfNeeded(SurfaceControl.Transaction t) {
+ recomputeBounds();
mWindow.drawIfNeeded(t);
}
- public void destroyWindow() {
+ void destroyWindow() {
mWindow.releaseSurface();
}
- private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
+ private void populateWindowsOnScreen(SparseArray<WindowState> outWindows) {
mTempLayer = 0;
mDisplayContent.forAllWindows((w) -> {
if (w.isOnScreen() && w.isVisible()
@@ -929,7 +1109,7 @@
private boolean mInvalidated;
- public ViewportWindow(Context context) {
+ ViewportWindow(Context context) {
SurfaceControl surfaceControl = null;
try {
mDisplay.getRealSize(mTempPoint);
@@ -971,7 +1151,7 @@
mInvalidated = true;
}
- public void setShown(boolean shown, boolean animate) {
+ void setShown(boolean shown, boolean animate) {
synchronized (mService.mGlobalLock) {
if (mShown == shown) {
return;
@@ -986,13 +1166,13 @@
@SuppressWarnings("unused")
// Called reflectively from an animator.
- public int getAlpha() {
+ int getAlpha() {
synchronized (mService.mGlobalLock) {
return mAlpha;
}
}
- public void setAlpha(int alpha) {
+ void setAlpha(int alpha) {
synchronized (mService.mGlobalLock) {
if (mAlpha == alpha) {
return;
@@ -1005,7 +1185,7 @@
}
}
- public void setBounds(Region bounds) {
+ void setBounds(Region bounds) {
synchronized (mService.mGlobalLock) {
if (mBounds.equals(bounds)) {
return;
@@ -1018,7 +1198,7 @@
}
}
- public void updateSize(SurfaceControl.Transaction t) {
+ void updateSize(SurfaceControl.Transaction t) {
synchronized (mService.mGlobalLock) {
mDisplay.getRealSize(mTempPoint);
t.setBufferSize(mSurfaceControl, mTempPoint.x, mTempPoint.y);
@@ -1026,7 +1206,7 @@
}
}
- public void invalidate(Rect dirtyRect) {
+ void invalidate(Rect dirtyRect) {
if (dirtyRect != null) {
mDirtyRect.set(dirtyRect);
} else {
@@ -1036,7 +1216,7 @@
mService.scheduleAnimationLocked();
}
- public void drawIfNeeded(SurfaceControl.Transaction t) {
+ void drawIfNeeded(SurfaceControl.Transaction t) {
synchronized (mService.mGlobalLock) {
if (!mInvalidated) {
return;
@@ -1078,7 +1258,7 @@
}
}
- public void releaseSurface() {
+ void releaseSurface() {
mService.mTransactionFactory.get().remove(mSurfaceControl).apply();
mSurface.release();
}
@@ -1101,7 +1281,7 @@
private final ValueAnimator mShowHideFrameAnimator;
- public AnimationController(Context context, Looper looper) {
+ AnimationController(Context context, Looper looper) {
super(looper);
mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this,
PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA);
@@ -1114,7 +1294,7 @@
mShowHideFrameAnimator.setDuration(longAnimationDuration);
}
- public void onFrameShownStateChanged(boolean shown, boolean animate) {
+ void onFrameShownStateChanged(boolean shown, boolean animate) {
obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED,
shown ? 1 : 0, animate ? 1 : 0).sendToTarget();
}
@@ -1158,7 +1338,7 @@
public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
- public MyHandler(Looper looper) {
+ MyHandler(Looper looper) {
super(looper);
}
@@ -1193,9 +1373,9 @@
case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
synchronized (mService.mGlobalLock) {
- if (mMagnifedViewport.isMagnifyingLocked()
- || isForceShowingMagnifiableBoundsLocked()) {
- mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
+ if (mMagnifedViewport.isMagnifying()
+ || isForceShowingMagnifiableBounds()) {
+ mMagnifedViewport.setMagnifiedRegionBorderShown(true, true);
mService.scheduleAnimationLocked();
}
}
@@ -1252,6 +1432,8 @@
private final Handler mHandler;
+ private final AccessibilityTracing mAccessibilityTracing;
+
private final WindowsForAccessibilityCallback mCallback;
private final int mDisplayId;
@@ -1263,24 +1445,32 @@
// Set to true if initializing window population complete.
private boolean mInitialized;
- public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
+ WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
int displayId,
WindowsForAccessibilityCallback callback) {
mService = windowManagerService;
mCallback = callback;
mDisplayId = displayId;
mHandler = new MyHandler(mService.mH.getLooper());
+ mAccessibilityTracing = AccessibilityTracing.getInstance(mService);
mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
.getSendRecurringAccessibilityEventsInterval();
computeChangedWindows(true);
}
- public void performComputeChangedWindowsNotLocked(boolean forceSend) {
+ void performComputeChangedWindows(boolean forceSend) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".performComputeChangedWindows",
+ "forceSend=" + forceSend);
+ }
mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
computeChangedWindows(forceSend);
}
- public void scheduleComputeChangedWindowsLocked() {
+ void scheduleComputeChangedWindows() {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(LOG_TAG + ".scheduleComputeChangedWindows");
+ }
if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
mRecurringAccessibilityEventsIntervalMillis);
@@ -1307,7 +1497,11 @@
*
* @param forceSend Send the windows the accessibility even if they haven't changed.
*/
- public void computeChangedWindows(boolean forceSend) {
+ void computeChangedWindows(boolean forceSend) {
+ if (mAccessibilityTracing.isEnabled()) {
+ mAccessibilityTracing.logState(
+ LOG_TAG + ".computeChangedWindows", "forceSend=" + forceSend);
+ }
if (DEBUG) {
Slog.i(LOG_TAG, "computeChangedWindows()");
}
@@ -1343,7 +1537,7 @@
unaccountedSpace.set(0, 0, screenWidth, screenHeight);
final SparseArray<WindowState> visibleWindows = mTempWindowStates;
- populateVisibleWindowsOnScreenLocked(visibleWindows);
+ populateVisibleWindowsOnScreen(visibleWindows);
Set<IBinder> addedWindows = mTempBinderSet;
addedWindows.clear();
@@ -1518,7 +1712,7 @@
// Map the frame to get what appears on the screen.
Matrix matrix = mTempMatrix;
- populateTransformationMatrixLocked(windowState, matrix);
+ populateTransformationMatrix(windowState, matrix);
forEachRect(touchableRegion, rect -> {
// Move to origin as all transforms are captured by the matrix.
@@ -1563,7 +1757,7 @@
&& windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
}
- private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
+ private void populateVisibleWindowsOnScreen(SparseArray<WindowState> outWindows) {
final List<WindowState> tempWindowStatesList = new ArrayList<>();
final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
if (dc == null) {
@@ -1637,4 +1831,292 @@
}
}
}
+
+ private static final class AccessibilityControllerInternalImpl
+ implements AccessibilityControllerInternal {
+
+ private static AccessibilityControllerInternal sInstance;
+ static AccessibilityControllerInternal getInstance(WindowManagerService service) {
+ synchronized (STATIC_LOCK) {
+ if (sInstance == null) {
+ sInstance = new AccessibilityControllerInternalImpl(service);
+ }
+ return sInstance;
+ }
+ }
+
+ private final AccessibilityTracing mTracing;
+ private AccessibilityControllerInternalImpl(WindowManagerService service) {
+ mTracing = AccessibilityTracing.getInstance(service);
+ }
+
+ @Override
+ public void startTrace() {
+ mTracing.startTrace();
+ }
+
+ @Override
+ public void stopTrace() {
+ mTracing.stopTrace();
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return mTracing.isEnabled();
+ }
+
+ @Override
+ public void logTrace(
+ String where, String callingParams, byte[] a11yDump, int callingUid,
+ StackTraceElement[] stackTrace) {
+ mTracing.logState(where, callingParams, a11yDump, callingUid, stackTrace);
+ }
+ }
+
+ private static final class AccessibilityTracing {
+ private static AccessibilityTracing sInstance;
+ static AccessibilityTracing getInstance(WindowManagerService service) {
+ synchronized (STATIC_LOCK) {
+ if (sInstance == null) {
+ sInstance = new AccessibilityTracing(service);
+ }
+ return sInstance;
+ }
+ }
+
+ private static final int BUFFER_CAPACITY = 4096 * 1024;
+ private static final String TRACE_FILENAME = "/data/misc/a11ytrace/a11y_trace.pb";
+ private static final String TRACE_DIRECTORY = "/data/misc/a11ytrace/";
+ private static final String TAG = "AccessibilityTracing";
+ private static final long MAGIC_NUMBER_VALUE =
+ ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+
+ private final Object mLock = new Object();
+ private final WindowManagerService mService;
+ private final File mTraceFile;
+ private final TraceBuffer mBuffer;
+ private final LogHandler mHandler;
+ private volatile boolean mEnabled;
+
+ AccessibilityTracing(WindowManagerService service) {
+ mService = service;
+ mTraceFile = new File(TRACE_FILENAME);
+ mBuffer = new TraceBuffer(BUFFER_CAPACITY);
+ HandlerThread workThread = new HandlerThread(TAG);
+ workThread.start();
+ mHandler = new LogHandler(workThread.getLooper());
+ }
+
+ /**
+ * Start the trace.
+ */
+ void startTrace() {
+ if (IS_USER) {
+ Slog.e(TAG, "Error: Tracing is not supported on user builds.");
+ return;
+ }
+ synchronized (mLock) {
+ try {
+ Files.createDirectories(Paths.get(TRACE_DIRECTORY));
+ mTraceFile.createNewFile();
+ } catch (Exception e) {
+ Slog.e(TAG, "Error: Failed to create trace file.");
+ return;
+ }
+ mEnabled = true;
+ mBuffer.resetBuffer();
+ }
+ }
+
+ /**
+ * Stops the trace and write the current buffer to disk
+ */
+ void stopTrace() {
+ if (IS_USER) {
+ Slog.e(TAG, "Error: Tracing is not supported on user builds.");
+ return;
+ }
+ synchronized (mLock) {
+ mEnabled = false;
+ if (mEnabled) {
+ Slog.e(TAG, "Error: tracing enabled while waiting for flush.");
+ return;
+ }
+ writeTraceToFile();
+ }
+ }
+
+ boolean isEnabled() {
+ return mEnabled;
+ }
+
+ /**
+ * Write an accessibility trace log entry.
+ */
+ void logState(String where) {
+ if (!mEnabled) {
+ return;
+ }
+ logState(where, "");
+ }
+
+ /**
+ * Write an accessibility trace log entry.
+ */
+ void logState(String where, String callingParams) {
+ if (!mEnabled) {
+ return;
+ }
+ logState(where, callingParams, "".getBytes());
+ }
+
+ /**
+ * Write an accessibility trace log entry.
+ */
+ void logState(String where, String callingParams, byte[] a11yDump) {
+ if (!mEnabled) {
+ return;
+ }
+ logState(where, callingParams, a11yDump, Binder.getCallingUid());
+ }
+
+ /**
+ * Write an accessibility trace log entry.
+ */
+ void logState(
+ String where, String callingParams, byte[] a11yDump, int callingUid) {
+ if (!mEnabled) {
+ return;
+ }
+ StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
+
+ logState(where, callingParams, a11yDump, callingUid, stackTraceElements);
+ }
+
+ /**
+ * Write an accessibility trace log entry.
+ */
+ void logState(String where, String callingParams, byte[] a11yDump, int callingUid,
+ StackTraceElement[] stackTrace) {
+ if (!mEnabled) {
+ return;
+ }
+
+ log(where, callingParams, a11yDump, callingUid, stackTrace);
+ }
+
+ private String toStackTraceString(StackTraceElement[] stackTraceElements) {
+ if (stackTraceElements == null) {
+ return "";
+ }
+ StringBuilder stringBuilder = new StringBuilder();
+ boolean skip = true;
+ for (int i = 0; i < stackTraceElements.length; i++) {
+ if (stackTraceElements[i].toString().contains(
+ AccessibilityTracing.class.getSimpleName())) {
+ skip = false;
+ } else if (!skip) {
+ stringBuilder.append(stackTraceElements[i].toString()).append("\n");
+ }
+ }
+ return stringBuilder.toString();
+ }
+
+ /**
+ * Write the current state to the buffer
+ */
+ private void log(String where, String callingParams, byte[] a11yDump, int callingUid,
+ StackTraceElement[] stackTrace) {
+ SimpleDateFormat fm = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = SystemClock.elapsedRealtimeNanos();
+ args.arg2 = fm.format(new Date()).toString();
+ args.arg3 = where;
+ args.arg4 = Process.myPid() + ":" + Application.getProcessName();
+ args.arg5 = Thread.currentThread().getId() + ":" + Thread.currentThread().getName();
+ args.arg6 = callingUid;
+ args.arg7 = callingParams;
+ args.arg8 = stackTrace;
+ args.arg9 = a11yDump;
+ mHandler.obtainMessage(LogHandler.MESSAGE_LOG_TRACE_ENTRY, args).sendToTarget();
+ }
+
+ /**
+ * Writes the trace buffer to new file for the bugreport.
+ */
+ void writeTraceToFile() {
+ mHandler.sendEmptyMessage(LogHandler.MESSAGE_WRITE_FILE);
+ }
+
+ private class LogHandler extends Handler {
+ public static final int MESSAGE_LOG_TRACE_ENTRY = 1;
+ public static final int MESSAGE_WRITE_FILE = 2;
+
+ LogHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MESSAGE_LOG_TRACE_ENTRY: {
+ final SomeArgs args = (SomeArgs) message.obj;
+ try {
+ ProtoOutputStream os = new ProtoOutputStream();
+ PackageManagerInternal pmInternal =
+ LocalServices.getService(PackageManagerInternal.class);
+
+ long tokenOuter = os.start(ENTRY);
+ String callingStack =
+ toStackTraceString((StackTraceElement[]) args.arg8);
+
+ os.write(ELAPSED_REALTIME_NANOS, (long) args.arg1);
+ os.write(CALENDAR_TIME, (String) args.arg2);
+ os.write(WHERE, (String) args.arg3);
+ os.write(PROCESS_NAME, (String) args.arg4);
+ os.write(THREAD_ID_NAME, (String) args.arg5);
+ os.write(CALLING_PKG, pmInternal.getNameForUid((int) args.arg6));
+ os.write(CALLING_PARAMS, (String) args.arg7);
+ os.write(CALLING_STACKS, callingStack);
+ os.write(ACCESSIBILITY_SERVICE, (byte[]) args.arg9);
+
+ long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
+ synchronized (mService.mGlobalLock) {
+ mService.dumpDebugLocked(os, WindowTraceLogLevel.ALL);
+ }
+ os.end(tokenInner);
+
+ os.end(tokenOuter);
+ synchronized (mLock) {
+ mBuffer.add(os);
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception while tracing state", e);
+ }
+ break;
+ }
+ case MESSAGE_WRITE_FILE: {
+ synchronized (mLock) {
+ writeTraceToFileInternal();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Writes the trace buffer to disk.
+ */
+ private void writeTraceToFileInternal() {
+ try {
+ ProtoOutputStream proto = new ProtoOutputStream();
+ proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+ mBuffer.writeTraceToFile(mTraceFile, proto);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to write buffer to file", e);
+ }
+ }
+ }
+
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 6bca484..3a0eb39 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -978,6 +978,7 @@
final TransitionInfoSnapshot infoSnapshot =
new TransitionInfoSnapshot(info, r, (int) startupTimeMs);
BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot));
+ mLastTransitionInfo.remove(r);
if (!info.isInterestingToLoggerAndObserver()) {
return infoSnapshot;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 81baaa2..68a2c5d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
@@ -82,7 +81,6 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.res.Configuration.EMPTY;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -203,7 +201,6 @@
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.ACTIVITY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -273,7 +270,6 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
-import android.permission.PermissionManager;
import android.service.dreams.DreamActivity;
import android.service.dreams.DreamManagerInternal;
import android.service.voice.IVoiceInteractionSession;
@@ -1366,7 +1362,6 @@
return;
}
final boolean surfaceReady = w.isDrawn() // Regular case
- || w.mWinAnimator.mSurfaceDestroyDeferred // The preserved surface is still ready.
|| w.isDragResizeChanged(); // Waiting for relayoutWindow to call preserveSurface.
final boolean needsLetterbox = surfaceReady && isLetterboxed(w);
updateRoundedCorners(w);
@@ -6533,7 +6528,7 @@
// Allowing closing {@link ActivityRecord} to participate can lead to an Activity in another
// task being started in the wrong orientation during the transition.
if (!getDisplayContent().mClosingApps.contains(this)
- && (isVisible() || getDisplayContent().mOpeningApps.contains(this))) {
+ && (isVisibleRequested() || getDisplayContent().mOpeningApps.contains(this))) {
return mOrientation;
}
@@ -6779,20 +6774,6 @@
// layout traversals.
mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
getResolvedOverrideConfiguration().seq = mConfigurationSeq;
-
- // Sandbox max bounds by setting it to the app bounds, if activity is letterboxed or in
- // size compat mode.
- if (providesMaxBounds()) {
- if (DEBUG_CONFIGURATION) {
- ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds for uid %s to bounds %s "
- + "due to letterboxing? %s mismatch with parent bounds? %s size compat "
- + "mode %s", getUid(),
- resolvedConfig.windowConfiguration.getBounds(), mLetterbox != null,
- !matchParentBounds(), inSizeCompatMode());
- }
- resolvedConfig.windowConfiguration
- .setMaxBounds(resolvedConfig.windowConfiguration.getBounds());
- }
}
/**
@@ -6976,19 +6957,6 @@
return super.getBounds();
}
- @Override
- public boolean providesMaxBounds() {
- // System and SystemUI should always be able to access the physical display bounds,
- // so do not provide it with the overridden maximum bounds.
- // TODO(b/179179513) check WindowState#mOwnerCanAddInternalSystemWindow instead
- if (getUid() == SYSTEM_UID || PermissionManager.checkPermission(INTERNAL_SYSTEM_WINDOW,
- getPid(), info.applicationInfo.uid) == PERMISSION_GRANTED) {
- return false;
- }
- // Max bounds should be sandboxed when this is letterboxed or in size compat mode.
- return mLetterbox != null || !matchParentBounds() || inSizeCompatMode();
- }
-
@VisibleForTesting
@Override
Rect getAnimationBounds(int appRootTaskClipMode) {
@@ -7037,7 +7005,32 @@
return;
}
}
- super.onConfigurationChanged(newParentConfig);
+
+ final DisplayContent display = mDisplayContent;
+ if (inPinnedWindowingMode() && attachedToProcess() && display != null) {
+ // If the PIP activity is changing to fullscreen with display orientation change, the
+ // fixed rotation will take effect that requires to send fixed rotation adjustments
+ // before the process configuration (if the process is a configuration listener of the
+ // activity). So when performing process configuration on client side, it can apply
+ // the adjustments (see WindowToken#onFixedRotationStatePrepared).
+ try {
+ app.pauseConfigurationDispatch();
+ super.onConfigurationChanged(newParentConfig);
+ if (mVisibleRequested && !inMultiWindowMode()) {
+ final int rotation = display.rotationForActivityInDifferentOrientation(this);
+ if (rotation != ROTATION_UNDEFINED) {
+ app.resumeConfigurationDispatch();
+ display.setFixedRotationLaunchingApp(this, rotation);
+ }
+ }
+ } finally {
+ if (app.resumeConfigurationDispatch()) {
+ app.dispatchConfiguration(app.getConfiguration());
+ }
+ }
+ } else {
+ super.onConfigurationChanged(newParentConfig);
+ }
// Configuration's equality doesn't consider seq so if only seq number changes in resolved
// override configuration. Therefore ConfigurationContainer doesn't change merged override
@@ -7046,7 +7039,6 @@
onMergedOverrideConfigurationChanged();
}
- final DisplayContent display = mDisplayContent;
if (display == null) {
return;
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 79f8229..3456e51 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -772,6 +772,11 @@
newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_HAS_RESULT, true);
}
newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_INTENT, new IntentSender(target));
+ ActivityOptions options = mRequest.activityOptions.getOptions(mRequest.intent,
+ mRequest.activityInfo,
+ mService.getProcessController(mRequest.caller),
+ mSupervisor);
+ newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_ACTIVITY_OPTIONS, options.toBundle());
heavy.updateIntentForHeavyWeightActivity(newIntent);
newIntent.putExtra(HeavyWeightSwitcherActivity.KEY_NEW_APP,
mRequest.activityInfo.packageName);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 2d6e9b2..f16a646 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -130,6 +130,7 @@
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.ActivityThread;
import android.app.AlertDialog;
+import android.app.AnrController;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.Dialog;
@@ -175,6 +176,7 @@
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.power.Mode;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -342,7 +344,7 @@
private StatusBarManagerInternal mStatusBarManagerInternal;
@VisibleForTesting
final ActivityTaskManagerInternal mInternal;
- PowerManagerInternal mPowerManagerInternal;
+ private PowerManagerInternal mPowerManagerInternal;
private UsageStatsManagerInternal mUsageStatsInternal;
PendingIntentController mPendingIntentController;
@@ -494,6 +496,7 @@
*/
private volatile long mLastStopAppSwitchesTime;
+ private final List<AnrController> mAnrController = new ArrayList<>();
IActivityController mController = null;
boolean mControllerIsAMonkey = false;
@@ -589,6 +592,22 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef({
+ POWER_MODE_REASON_START_ACTIVITY,
+ POWER_MODE_REASON_FREEZE_DISPLAY,
+ POWER_MODE_REASON_ALL,
+ })
+ @interface PowerModeReason {}
+
+ static final int POWER_MODE_REASON_START_ACTIVITY = 1 << 0;
+ static final int POWER_MODE_REASON_FREEZE_DISPLAY = 1 << 1;
+ /** This can only be used by {@link #endLaunchPowerMode(int)}.*/
+ static final int POWER_MODE_REASON_ALL = (1 << 2) - 1;
+
+ /** The reasons to use {@link Mode#LAUNCH} power mode. */
+ private @PowerModeReason int mLaunchPowerModeReasons;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
LAYOUT_REASON_CONFIG_CHANGED,
LAYOUT_REASON_VISIBILITY_CHANGED,
})
@@ -769,7 +788,7 @@
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);
@@ -2058,6 +2077,40 @@
return mAppSwitchesAllowed;
}
+ /** Register an {@link AnrController} to control the ANR dialog behavior */
+ public void registerAnrController(AnrController controller) {
+ synchronized (mGlobalLock) {
+ mAnrController.add(controller);
+ }
+ }
+
+ /** Unregister an {@link AnrController} */
+ public void unregisterAnrController(AnrController controller) {
+ synchronized (mGlobalLock) {
+ mAnrController.remove(controller);
+ }
+ }
+
+ /** @return the max ANR delay from all registered {@link AnrController} instances */
+ public long getMaxAnrDelayMillis(ApplicationInfo info) {
+ if (info == null || info.packageName == null) {
+ return 0;
+ }
+
+ final ArrayList<AnrController> controllers;
+ synchronized (mGlobalLock) {
+ controllers = new ArrayList<>(mAnrController);
+ }
+
+ final String packageName = info.packageName;
+ long maxDelayMs = 0;
+ for (AnrController controller : controllers) {
+ maxDelayMs = Math.max(maxDelayMs, controller.getAnrDelayMillis(packageName, info.uid));
+ }
+ maxDelayMs = Math.max(maxDelayMs, 0);
+ return maxDelayMs;
+ }
+
@Override
public void setActivityController(IActivityController controller, boolean imAMonkey) {
mAmInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
@@ -4059,6 +4112,20 @@
return changes;
}
+ void startLaunchPowerMode(@PowerModeReason int reason) {
+ if (mPowerManagerInternal == null) return;
+ mPowerManagerInternal.setPowerMode(Mode.LAUNCH, true);
+ mLaunchPowerModeReasons |= reason;
+ }
+
+ void endLaunchPowerMode(@PowerModeReason int reason) {
+ if (mPowerManagerInternal == null || mLaunchPowerModeReasons == 0) return;
+ mLaunchPowerModeReasons &= ~reason;
+ if (mLaunchPowerModeReasons == 0) {
+ mPowerManagerInternal.setPowerMode(Mode.LAUNCH, false);
+ }
+ }
+
/** @see WindowSurfacePlacer#deferLayout */
void deferWindowLayout() {
if (!mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index de43643..1264d0c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1755,7 +1755,7 @@
}
// End power mode launch before going sleep
- mRootWindowContainer.endPowerModeLaunchIfNeeded();
+ mService.endLaunchPowerMode(ActivityTaskManagerService.POWER_MODE_REASON_ALL);
removeSleepTimeouts();
diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java
index fde0369..c589fea 100644
--- a/services/core/java/com/android/server/wm/AlertWindowNotification.java
+++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java
@@ -39,7 +39,7 @@
import android.os.Bundle;
import com.android.internal.R;
-import com.android.server.policy.IconUtilities;
+import com.android.internal.util.ImageUtils;
/** Displays an ongoing notification for a process displaying an alert window */
class AlertWindowNotification {
@@ -54,7 +54,6 @@
private final NotificationManager mNotificationManager;
private final String mPackageName;
private boolean mPosted;
- private IconUtilities mIconUtilities;
AlertWindowNotification(WindowManagerService service, String packageName) {
mService = service;
@@ -63,7 +62,6 @@
(NotificationManager) mService.mContext.getSystemService(NOTIFICATION_SERVICE);
mNotificationTag = CHANNEL_PREFIX + mPackageName;
mRequestCode = sNextRequestCode++;
- mIconUtilities = new IconUtilities(mService.mContext);
}
void post() {
@@ -126,8 +124,9 @@
if (aInfo != null) {
final Drawable drawable = pm.getApplicationIcon(aInfo);
- if (drawable != null) {
- final Bitmap bitmap = mIconUtilities.createIconBitmap(drawable);
+ int size = context.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
+ final Bitmap bitmap = ImageUtils.buildScaledBitmap(drawable, size, size);
+ if (bitmap != null) {
builder.setLargeIcon(bitmap);
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index eba3f93..5b685b4 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -90,6 +90,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
+import static com.android.server.wm.WindowManagerInternal.KeyguardExitAnimationStartListener;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE;
@@ -257,6 +258,7 @@
private long mLastClipRevealTransitionDuration = DEFAULT_APP_TRANSITION_DURATION;
private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
+ private KeyguardExitAnimationStartListener mKeyguardExitAnimationStartListener;
private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor();
private int mLastClipRevealMaxTranslation;
@@ -446,7 +448,7 @@
AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
if (mRemoteAnimationController != null) {
- mRemoteAnimationController.goodToGo();
+ mRemoteAnimationController.goodToGo(transit);
}
return redoLayout;
}
@@ -509,6 +511,11 @@
mListeners.remove(listener);
}
+ void registerKeygaurdExitAnimationStartListener(
+ KeyguardExitAnimationStartListener listener) {
+ mKeyguardExitAnimationStartListener = listener;
+ }
+
public void notifyAppTransitionFinishedLocked(IBinder token) {
for (int i = 0; i < mListeners.size(); i++) {
mListeners.get(i).onAppTransitionFinishedLocked(token);
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 6e8257b..9ca7eca 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -102,6 +102,7 @@
private final DisplayContent mDisplayContent;
private final WallpaperController mWallpaperControllerLocked;
private RemoteAnimationDefinition mRemoteAnimationDefinition = null;
+ private static final int KEYGUARD_GOING_AWAY_ANIMATION_DURATION = 400;
private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>();
@@ -437,10 +438,14 @@
return adapter;
}
}
- if (mRemoteAnimationDefinition == null) {
- return null;
+ if (mRemoteAnimationDefinition != null) {
+ final RemoteAnimationAdapter adapter = mRemoteAnimationDefinition.getAdapter(
+ transit, activityTypes);
+ if (adapter != null) {
+ return adapter;
+ }
}
- return mRemoteAnimationDefinition.getAdapter(transit, activityTypes);
+ return null;
}
/**
@@ -719,8 +724,7 @@
final AccessibilityController accessibilityController =
mDisplayContent.mWmService.mAccessibilityController;
if (accessibilityController != null) {
- accessibilityController.onAppWindowTransitionLocked(
- mDisplayContent.getDisplayId(), transit);
+ accessibilityController.onAppWindowTransition(mDisplayContent.getDisplayId(), transit);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3ab7952..23eab98 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -685,6 +685,10 @@
*/
private boolean mInEnsureActivitiesVisible = false;
+ // Used to indicate that the movement of child tasks to top will not move the display to top as
+ // well and thus won't change the top resumed / focused record
+ boolean mDontMoveToTop;
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
final ActivityRecord activity = w.mActivityRecord;
@@ -948,7 +952,7 @@
}
final ActivityRecord activity = w.mActivityRecord;
- if (activity != null) {
+ if (activity != null && activity.isVisibleRequested()) {
activity.updateLetterboxSurface(w);
final boolean updateAllDrawn = activity.updateDrawnWindowStates(w);
if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(activity)) {
@@ -1226,7 +1230,7 @@
if (mWmService.mAccessibilityController != null) {
final int prevDisplayId = prevDc != null ? prevDc.getDisplayId() : INVALID_DISPLAY;
- mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(prevDisplayId,
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(prevDisplayId,
getDisplayId());
}
}
@@ -1447,7 +1451,9 @@
}
config = new Configuration();
computeScreenConfiguration(config);
- } else if (currentConfig != null) {
+ } else if (currentConfig != null
+ // If waiting for a remote rotation, don't prematurely update configuration.
+ && !mDisplayRotation.isWaitingForRemoteRotation()) {
// No obvious action we need to take, but if our current state mismatches the
// activity manager's, update it, disregarding font scale, which should remain set
// to the value of the previous configuration.
@@ -1597,7 +1603,6 @@
&& mFixedRotationLaunchingApp != mFixedRotationTransitionListener.mAnimatingRecents;
}
- @VisibleForTesting
boolean isFixedRotationLaunchingApp(ActivityRecord r) {
return mFixedRotationLaunchingApp == r;
}
@@ -1826,8 +1831,6 @@
if (w.mHasSurface && !rotateSeamlessly) {
ProtoLog.v(WM_DEBUG_ORIENTATION, "Set mOrientationChanging of %s", w);
w.setOrientationChanging(true);
- mWmService.mRoot.mOrientationChangeComplete = false;
- w.mLastFreezeDuration = 0;
}
w.mReportOrientationChanged = true;
}, true /* traverseTopToBottom */);
@@ -1850,7 +1853,7 @@
}
if (mWmService.mAccessibilityController != null) {
- mWmService.mAccessibilityController.onRotationChangedLocked(this);
+ mWmService.mAccessibilityController.onRotationChanged(this);
}
}
@@ -3400,7 +3403,7 @@
}
void updateAccessibilityOnWindowFocusChanged(AccessibilityController accessibilityController) {
- accessibilityController.onWindowFocusChangedNotLocked(getDisplayId());
+ accessibilityController.onWindowFocusChangedNot(getDisplayId());
}
private static void onWindowFocusChanged(WindowState oldFocus, WindowState newFocus) {
@@ -4963,7 +4966,7 @@
if (!mLocationInParentWindow.equals(x, y)) {
mLocationInParentWindow.set(x, y);
if (mWmService.mAccessibilityController != null) {
- mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(mDisplayId);
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(mDisplayId);
}
notifyLocationInParentDisplayChanged();
}
@@ -5960,36 +5963,6 @@
}
}
- /**
- * Returns the number of window tokens without surface on this display. A {@link WindowToken}
- * won't have its {@link SurfaceControl} until a window is added to a {@link WindowToken}.
- * The purpose of this method is to accumulate non-window containing {@link WindowToken}s and
- * limit the usage if the count exceeds a number.
- *
- * @param callingUid app calling uid
- * @return the number of window tokens without surface on this display
- * @see WindowToken#addWindow(WindowState)
- */
- int getWindowTokensWithoutSurfaceCount(int callingUid) {
- List<WindowToken> tokens = new ArrayList<>(mTokenMap.values());
- int count = 0;
- for (int i = tokens.size() - 1; i >= 0; i--) {
- final WindowToken token = tokens.get(i);
- if (callingUid != token.getOwnerUid()) {
- continue;
- }
- // Skip if token is an Activity
- if (token.asActivityRecord() != null) {
- continue;
- }
- if (token.mSurfaceControl != null) {
- continue;
- }
- count++;
- }
- return count;
- }
-
MagnificationSpec getMagnificationSpec() {
return mMagnificationSpec;
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 61fe023..5460e36 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 @@
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/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 48e4df7..b106657 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -481,12 +481,12 @@
mRotation = rotation;
+ mDisplayContent.setLayoutNeeded();
+
mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION);
- mDisplayContent.setLayoutNeeded();
-
if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {
// The screen rotation animation uses a screenshot to freeze the screen while windows
// resize underneath. When we are rotating seamlessly, we allow the elements to
@@ -1499,6 +1499,21 @@
}
@Override
+ public boolean canUseRotationResolver() {
+ if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
+
+ switch (mCurrentAppOrientation) {
+ case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
+ case ActivityInfo.SCREEN_ORIENTATION_USER:
+ case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
+ case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
+ case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
+ return true;
+ }
+ return false;
+ }
+
+ @Override
public void onProposedRotationChanged(int rotation) {
ProtoLog.v(WM_DEBUG_ORIENTATION, "onProposedRotationChanged, rotation=%d", rotation);
Runnable r = mRunnableCache.get(rotation, null);
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index b82fdd2..6d5abe1 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -16,11 +16,11 @@
package com.android.server.wm;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_DESTROY;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY;
import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
-import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
-import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_AUTO;
import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_DISABLED;
@@ -196,6 +196,14 @@
mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
}
+ void setDontMoveToTop(DisplayContent dc, boolean dontMoveToTop) {
+ DisplayInfo displayInfo = dc.getDisplayInfo();
+ SettingsProvider.SettingsEntry overrideSettings =
+ mSettingsProvider.getSettings(displayInfo);
+ overrideSettings.mDontMoveToTop = dontMoveToTop;
+ mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings);
+ }
+
boolean shouldShowSystemDecorsLocked(DisplayContent dc) {
if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) {
// Default display should show system decors.
@@ -274,6 +282,10 @@
final int forcedScalingMode = settings.mForcedScalingMode != null
? settings.mForcedScalingMode : FORCE_SCALING_MODE_AUTO;
dc.mDisplayScalingDisabled = forcedScalingMode == FORCE_SCALING_MODE_DISABLED;
+
+ boolean dontMoveToTop = settings.mDontMoveToTop != null
+ ? settings.mDontMoveToTop : false;
+ dc.mDontMoveToTop = dontMoveToTop;
}
/**
@@ -358,6 +370,8 @@
Boolean mIgnoreOrientationRequest;
@Nullable
Boolean mIgnoreDisplayCutout;
+ @Nullable
+ Boolean mDontMoveToTop;
SettingsEntry() {}
@@ -432,6 +446,10 @@
mIgnoreDisplayCutout = other.mIgnoreDisplayCutout;
changed = true;
}
+ if (other.mDontMoveToTop != mDontMoveToTop) {
+ mDontMoveToTop = other.mDontMoveToTop;
+ changed = true;
+ }
return changed;
}
@@ -515,6 +533,11 @@
mIgnoreDisplayCutout = delta.mIgnoreDisplayCutout;
changed = true;
}
+ if (delta.mDontMoveToTop != null
+ && delta.mDontMoveToTop != mDontMoveToTop) {
+ mDontMoveToTop = delta.mDontMoveToTop;
+ changed = true;
+ }
return changed;
}
@@ -531,7 +554,8 @@
&& mImePolicy == null
&& mFixedToUserRotation == null
&& mIgnoreOrientationRequest == null
- && mIgnoreDisplayCutout == null;
+ && mIgnoreDisplayCutout == null
+ && mDontMoveToTop == null;
}
@Override
@@ -553,7 +577,8 @@
&& Objects.equals(mImePolicy, that.mImePolicy)
&& Objects.equals(mFixedToUserRotation, that.mFixedToUserRotation)
&& Objects.equals(mIgnoreOrientationRequest, that.mIgnoreOrientationRequest)
- && Objects.equals(mIgnoreDisplayCutout, that.mIgnoreDisplayCutout);
+ && Objects.equals(mIgnoreDisplayCutout, that.mIgnoreDisplayCutout)
+ && Objects.equals(mDontMoveToTop, that.mDontMoveToTop);
}
@Override
@@ -561,7 +586,8 @@
return Objects.hash(mWindowingMode, mUserRotationMode, mUserRotation, mForcedWidth,
mForcedHeight, mForcedDensity, mForcedScalingMode, mRemoveContentMode,
mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mImePolicy,
- mFixedToUserRotation, mIgnoreOrientationRequest, mIgnoreDisplayCutout);
+ mFixedToUserRotation, mIgnoreOrientationRequest, mIgnoreDisplayCutout,
+ mDontMoveToTop);
}
@Override
@@ -581,6 +607,7 @@
+ ", mFixedToUserRotation=" + mFixedToUserRotation
+ ", mIgnoreOrientationRequest=" + mIgnoreOrientationRequest
+ ", mIgnoreDisplayCutout=" + mIgnoreDisplayCutout
+ + ", mDontMoveToTop=" + mDontMoveToTop
+ '}';
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
index 5f3ab43..8fcdf2e 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 @@
*/
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));
}
@@ -405,6 +406,9 @@
"ignoreOrientationRequest", null /* defaultValue */);
settingsEntry.mIgnoreDisplayCutout = getBooleanAttribute(parser,
"ignoreDisplayCutout", null /* defaultValue */);
+ settingsEntry.mDontMoveToTop = getBooleanAttribute(parser,
+ "dontMoveToTop", null /* defaultValue */);
+
fileData.mSettings.put(name, settingsEntry);
}
XmlUtils.skipCurrentTag(parser);
@@ -496,6 +500,10 @@
out.attributeBoolean(null, "ignoreDisplayCutout",
settingsEntry.mIgnoreDisplayCutout);
}
+ if (settingsEntry.mDontMoveToTop != null) {
+ out.attributeBoolean(null, "dontMoveToTop",
+ settingsEntry.mDontMoveToTop);
+ }
out.endTag(null, "display");
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 803bec8..de4bdaa 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -47,7 +47,7 @@
mTouchRegion.set(touchRegion);
// We need to report touchable region changes to accessibility.
if (mDisplayContent.mWmService.mAccessibilityController != null) {
- mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(
+ mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(
mDisplayContent.getDisplayId());
}
}
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 2274a4a..99c9e79 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -340,7 +340,7 @@
}
public void applySurfaceChanges(SurfaceControl.Transaction t) {
- if (mSurfaceFrameRelative.equals(mLayoutFrameRelative)) {
+ if (!needsApplySurfaceChanges()) {
// Nothing changed.
return;
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 26f0f09..4f8ea1a 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -284,7 +284,8 @@
// Just to be sure end the launch hint in case the target activity was never launched.
// However, if we're keeping the activity and making it visible, we can leave it on.
if (reorderMode != REORDER_KEEP_IN_PLACE) {
- mService.mRootWindowContainer.endPowerModeLaunchIfNeeded();
+ mService.endLaunchPowerMode(
+ ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY);
}
// Once the target is shown, prevent spurious background app switches
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index a1d2072..392f27e 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -36,6 +36,7 @@
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
+import android.view.WindowManager;
import com.android.internal.protolog.ProtoLogImpl;
import com.android.internal.protolog.common.ProtoLog;
@@ -98,7 +99,7 @@
/**
* Called when the transition is ready to be started, and all leashes have been set up.
*/
- void goodToGo() {
+ void goodToGo(@WindowManager.TransitionOldType int transit) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()");
if (mPendingAnimations.isEmpty() || mCanceled) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
@@ -123,11 +124,15 @@
// Create the remote wallpaper animation targets (if any)
final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
+
+ // TODO(bc-unlock): Create the remote non app animation targets (if any)
+ final RemoteAnimationTarget[] nonAppTargets = new RemoteAnimationTarget[0];
+
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
try {
linkToDeathOfRunner();
- mRemoteAnimationAdapter.getRunner().onAnimationStart(appTargets, wallpaperTargets,
- mFinishedCallback);
+ mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets,
+ wallpaperTargets, nonAppTargets, mFinishedCallback);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to start remote animation", e);
onAnimationFinished();
@@ -274,6 +279,7 @@
private void setRunningRemoteAnimation(boolean running) {
final int pid = mRemoteAnimationAdapter.getCallingPid();
final int uid = mRemoteAnimationAdapter.getCallingUid();
+
if (pid == 0) {
throw new RuntimeException("Calling pid of remote animation was null");
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 61fec0d..bd93e04 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -89,7 +89,6 @@
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_NONE;
-import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING;
@@ -267,9 +266,6 @@
*/
final SparseArray<SleepToken> mSleepTokens = new SparseArray<>();
- /** Set when a power mode launch has started, but not ended. */
- private boolean mPowerModeLaunchStarted;
-
// The default minimal size that will be used if the activity doesn't specify its minimal size.
// It will be calculated when the default display gets added.
int mDefaultMinSizeOfResizeableTaskDp = -1;
@@ -847,12 +843,12 @@
Slog.i(TAG,
">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
}
- // Send any pending task-info changes that were queued-up during a layout deferment
- mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
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);
@@ -933,7 +929,6 @@
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
win.destroySurfaceUnchecked();
- win.mWinAnimator.destroyPreservedSurfaceLocked(win.getSyncTransaction());
} while (i > 0);
mWmService.mDestroySurface.clear();
}
@@ -1185,10 +1180,7 @@
mUpdateRotation = true;
doRequest = true;
}
- if ((bulkUpdateParams & SET_ORIENTATION_CHANGE_COMPLETE) == 0) {
- mOrientationChangeComplete = false;
- } else {
- mOrientationChangeComplete = true;
+ if (mOrientationChangeComplete) {
mLastWindowFreezeSource = mWmService.mAnimator.mLastWindowFreezeSource;
if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
doRequest = true;
@@ -3328,7 +3320,7 @@
}
}
// End power mode launch when idle.
- endPowerModeLaunchIfNeeded();
+ mService.endLaunchPowerMode(ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY);
return true;
}
@@ -3541,17 +3533,9 @@
sendPowerModeLaunch = noResumedActivities[0] || allFocusedProcessesDiffer[0];
}
- if (sendPowerModeLaunch && mService.mPowerManagerInternal != null) {
- mService.mPowerManagerInternal.setPowerMode(Mode.LAUNCH, true);
- mPowerModeLaunchStarted = true;
- }
- }
-
- void endPowerModeLaunchIfNeeded() {
- // Trigger launch power mode off if activity is launched
- if (mPowerModeLaunchStarted && mService.mPowerManagerInternal != null) {
- mService.mPowerManagerInternal.setPowerMode(Mode.LAUNCH, false);
- mPowerModeLaunchStarted = false;
+ if (sendPowerModeLaunch) {
+ mService.startLaunchPowerMode(
+ ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY);
}
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 1f8daf6..8b18679 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 @@
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 @@
== 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 @@
}
@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 @@
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/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java
index 62c0527..0902948 100644
--- a/services/core/java/com/android/server/wm/ShellRoot.java
+++ b/services/core/java/com/android/server/wm/ShellRoot.java
@@ -191,7 +191,7 @@
}
}
if (mDisplayContent.mWmService.mAccessibilityController != null) {
- mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(
+ mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(
mDisplayContent.getDisplayId());
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 30af0ed..9bbbbe0 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -77,7 +77,6 @@
import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
@@ -145,7 +144,6 @@
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainerChildProto.TASK;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -812,6 +810,16 @@
private boolean mDeferTaskAppear;
/**
+ * Forces this task to be unorganized. Currently it is used for deferring the control of
+ * organizer when windowing mode is changing from PiP to fullscreen with orientation change.
+ * It is true only during Task#setWindowingMode ~ DisplayRotation#continueRotation.
+ *
+ * TODO(b/179235349): Remove this field by making surface operations from task organizer sync
+ * with display rotation.
+ */
+ private boolean mForceNotOrganized;
+
+ /**
* This task was created by the task organizer which has the following implementations.
* <ul>
* <lis>The task won't be removed when it is empty. Removal has to be an explicit request
@@ -921,10 +929,8 @@
return;
}
- if (isLeafTask()) {
- // This task is going away, so save the last state if necessary.
- saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
- }
+ // This task is going away, so save the last state if necessary.
+ saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
// TODO: VI what about activity?
final boolean isVoiceSession = voiceSession != null;
@@ -2250,6 +2256,21 @@
if (pipChanging) {
mDisplayContent.getPinnedStackController().setPipWindowingModeChanging(true);
+ // If the top activity is using fixed rotation, it should be changing from PiP to
+ // fullscreen with display orientation change. Do not notify fullscreen task organizer
+ // because the restoration of task surface and the transformation of activity surface
+ // need to be done synchronously.
+ final ActivityRecord r = topRunningActivity();
+ if (r != null && mDisplayContent.isFixedRotationLaunchingApp(r)) {
+ mForceNotOrganized = true;
+ }
+ } else if (mForceNotOrganized) {
+ // If the display orientation change is done, let the corresponding task organizer take
+ // back the control of this task.
+ final ActivityRecord r = topRunningActivity();
+ if (r == null || !mDisplayContent.isFixedRotationLaunchingApp(r)) {
+ mForceNotOrganized = false;
+ }
}
try {
// We have 2 reasons why we need to report orientation change here.
@@ -2459,14 +2480,16 @@
/**
* Saves launching state if necessary so that we can launch the activity to its latest state.
- * It only saves state if this task has been shown to user and it's in fullscreen or freeform
- * mode on freeform displays.
*/
private void saveLaunchingStateIfNeeded() {
saveLaunchingStateIfNeeded(getDisplayContent());
}
private void saveLaunchingStateIfNeeded(DisplayContent display) {
+ if (!isLeafTask()) {
+ return;
+ }
+
if (!getHasBeenVisible()) {
// Not ever visible to user.
return;
@@ -2837,6 +2860,9 @@
if (windowingMode == WINDOWING_MODE_UNDEFINED) {
windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
}
+ // Commit the resolved windowing mode so the canSpecifyOrientation won't get the old
+ // mode that may cause the bounds to be miscalculated, e.g. letterboxed.
+ getConfiguration().windowConfiguration.setWindowingMode(windowingMode);
Rect outOverrideBounds =
getResolvedOverrideConfiguration().windowConfiguration.getBounds();
@@ -2867,16 +2893,6 @@
// In FULLSCREEN mode, always start with empty bounds to indicate "fill parent".
outBounds.setEmpty();
computeLetterboxBounds(outBounds, newParentConfig);
- // Since the task is letterboxed due to mismatched orientation against its parent,
- // sandbox max bounds to the app bounds.
- if (!outBounds.isEmpty()) {
- if (DEBUG_CONFIGURATION) {
- ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds due to mismatched "
- + "orientation with parent, to %s vs DisplayArea %s", outBounds,
- getDisplayArea() != null ? getDisplayArea().getBounds() : "null");
- }
- getResolvedOverrideConfiguration().windowConfiguration.setMaxBounds(outBounds);
- }
}
/** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
@@ -4545,6 +4561,9 @@
pw.print(" mSupportsPictureInPicture="); pw.print(mSupportsPictureInPicture);
pw.print(" isResizeable="); pw.println(isResizeable());
pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
+ if (mForceNotOrganized) {
+ pw.print(prefix); pw.println("mForceNotOrganized=true");
+ }
pw.println(" (inactive for " + (getInactiveDuration() / 1000) + "s)");
}
@@ -4989,6 +5008,9 @@
}
private boolean canBeOrganized() {
+ if (mForceNotOrganized) {
+ return false;
+ }
// All root tasks can be organized
if (isRootTask()) {
return true;
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index e18219e..622fe05 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -497,7 +497,7 @@
void notifyTaskDisplayChanged(int taskId, int newDisplayId) {
final Message msg = mHandler.obtainMessage(NOTIFY_TASK_DISPLAY_CHANGED_LISTENERS_MSG,
taskId, newDisplayId);
- forAllLocalListeners(mNotifyTaskStackChanged, msg);
+ forAllLocalListeners(mNotifyTaskDisplayChanged, msg);
msg.sendToTarget();
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 4185407..40248c4 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -47,6 +47,7 @@
import android.os.UserHandle;
import android.util.IntArray;
import android.util.Slog;
+import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
@@ -403,7 +404,11 @@
}
// We don't allow untrusted display to top when root task moves to top,
// until user tapping this display to change display position as top intentionally.
- if (!mDisplayContent.isTrusted() && !getParent().isOnTop()) {
+ //
+ // Displays with {@code mDontMoveToTop} property set to {@code true} won't be
+ // allowed to top neither.
+ if ((!mDisplayContent.isTrusted() || mDisplayContent.mDontMoveToTop)
+ && !getParent().isOnTop()) {
includingParents = false;
}
final int targetPosition = findPositionForRootTask(position, child, false /* adding */);
@@ -905,6 +910,13 @@
}
}
+ @Override
+ RemoteAnimationTarget createRemoteAnimationTarget(
+ RemoteAnimationController.RemoteAnimationRecord record) {
+ final ActivityRecord activity = getTopMostActivity();
+ return activity != null ? activity.createRemoteAnimationTarget(record) : null;
+ }
+
SurfaceControl getSplitScreenDividerAnchor() {
return mSplitScreenDividerAnchor;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 46aea23..98eb11f 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -372,8 +372,6 @@
dc.mWallpaperController.startWallpaperAnimation(anim);
}
}
- }
- if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
dc.startKeyguardExitOnNonAppWindows(
(flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0,
(flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0,
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index f572e8e..43303d4 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
@@ -51,7 +50,7 @@
WallpaperWindowToken(WindowManagerService service, IBinder token, boolean explicit,
DisplayContent dc, boolean ownerCanManageAppTokens, @Nullable Bundle options) {
- super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens, INVALID_UID,
+ super(service, token, TYPE_WALLPAPER, explicit, dc, ownerCanManageAppTokens,
false /* roundedCornerOverlay */, false /* fromClientToken */, options);
dc.mWallpaperController.addWallpaperToken(this);
setWindowingMode(WINDOWING_MODE_FULLSCREEN);
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 52ed278..eb32486 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -26,7 +26,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import android.content.Context;
import android.os.Trace;
@@ -134,8 +133,10 @@
// Schedule next frame already such that back-pressure happens continuously.
scheduleAnimation();
+ final RootWindowContainer root = mService.mRoot;
mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
- mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
+ mBulkUpdateParams = 0;
+ root.mOrientationChangeComplete = true;
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
@@ -144,14 +145,14 @@
mService.openSurfaceTransaction();
try {
// Remove all deferred displays, tasks, and activities.
- mService.mRoot.handleCompleteDeferredRemoval();
+ root.handleCompleteDeferredRemoval();
final AccessibilityController accessibilityController =
mService.mAccessibilityController;
final int numDisplays = mDisplayContentsAnimators.size();
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
- final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+ final DisplayContent dc = root.getDisplayContent(displayId);
// Update animations of all applications, including those associated with
// exiting/removed apps.
dc.updateWindowsForAnimator();
@@ -160,11 +161,11 @@
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
- final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+ final DisplayContent dc = root.getDisplayContent(displayId);
dc.checkAppWindowsReadyToShow();
if (accessibilityController != null) {
- accessibilityController.drawMagnifiedRegionBorderIfNeededLocked(displayId,
+ accessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId,
mTransaction);
}
}
@@ -179,13 +180,14 @@
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
}
- final boolean hasPendingLayoutChanges = mService.mRoot.hasPendingLayoutChanges(this);
- final boolean doRequest = mBulkUpdateParams != 0 && mService.mRoot.copyAnimToLayoutParams();
+ final boolean hasPendingLayoutChanges = root.hasPendingLayoutChanges(this);
+ final boolean doRequest = (mBulkUpdateParams != 0 || root.mOrientationChangeComplete)
+ && root.copyAnimToLayoutParams();
if (hasPendingLayoutChanges || doRequest) {
mService.mWindowPlacerLocked.requestTraversal();
}
- final boolean rootAnimating = mService.mRoot.isAnimating(TRANSITION | CHILDREN /* flags */,
+ final boolean rootAnimating = root.isAnimating(TRANSITION | CHILDREN /* flags */,
ANIMATION_TYPE_ALL /* typesToCheck */);
if (rootAnimating && !mLastRootAnimating) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
@@ -197,7 +199,7 @@
mLastRootAnimating = rootAnimating;
final boolean runningExpensiveAnimations =
- mService.mRoot.isAnimating(TRANSITION | CHILDREN /* flags */,
+ root.isAnimating(TRANSITION | CHILDREN /* flags */,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_SCREEN_ROTATION
| ANIMATION_TYPE_RECENTS /* typesToCheck */);
if (runningExpensiveAnimations && !mRunningExpensiveAnimations) {
@@ -216,12 +218,10 @@
ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
if (mRemoveReplacedWindows) {
- mService.mRoot.removeReplacedWindows();
+ root.removeReplacedWindows();
mRemoveReplacedWindows = false;
}
- mService.destroyPreservedSurfaceLocked();
-
mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
executeAfterPrepareSurfacesRunnables();
@@ -237,8 +237,8 @@
if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
builder.append(" UPDATE_ROTATION");
}
- if ((bulkUpdateParams & WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE) != 0) {
- builder.append(" ORIENTATION_CHANGE_COMPLETE");
+ if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING) != 0) {
+ builder.append(" SET_WALLPAPER_ACTION_PENDING");
}
return builder.toString();
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index a3a9eb7..e183ea0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -23,12 +23,15 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
+import android.os.Bundle;
import android.os.IBinder;
import android.view.Display;
import android.view.IInputFilter;
+import android.view.IRemoteAnimationFinishedCallback;
import android.view.IWindow;
import android.view.InputChannel;
import android.view.MagnificationSpec;
+import android.view.RemoteAnimationTarget;
import android.view.WindowInfo;
import android.view.WindowManager.DisplayImePolicy;
@@ -46,6 +49,41 @@
public abstract class WindowManagerInternal {
/**
+ * Interface for accessibility features implemented by AccessibilityController inside
+ * WindowManager.
+ */
+ public interface AccessibilityControllerInternal {
+ /**
+ * Enable the accessibility trace logging.
+ */
+ void startTrace();
+
+ /**
+ * Disable the accessibility trace logging.
+ */
+ void stopTrace();
+
+ /**
+ * Is trace enabled or not.
+ */
+ boolean isEnabled();
+
+ /**
+ * Add an accessibility trace entry.
+ *
+ * @param where A string to identify this log entry, which can be used to filter/search
+ * through the tracing file.
+ * @param callingParams The parameters for the method to be logged.
+ * @param a11yDump The proto byte array for a11y state when the entry is generated.
+ * @param callingUid The calling uid.
+ * @param stackTrace The stack trace, null if not needed.
+ */
+ void logTrace(
+ String where, String callingParams, byte[] a11yDump, int callingUid,
+ StackTraceElement[] stackTrace);
+ }
+
+ /**
* Interface to receive a callback when the windows reported for
* accessibility changed.
*/
@@ -154,6 +192,21 @@
}
/**
+ * An interface to be notified when keyguard exit animation should start.
+ */
+ public interface KeyguardExitAnimationStartListener {
+ /**
+ * Called when keyguard exit animation should start.
+ * @param apps The list of apps to animate.
+ * @param wallpapers The list of wallpapers to animate.
+ * @param finishedCallback The callback to invoke when the animation is finished.
+ */
+ void onAnimationStart(RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ IRemoteAnimationFinishedCallback finishedCallback);
+ }
+
+ /**
* An interface to be notified about hardware keyboard status.
*/
public interface OnHardKeyboardStatusChangeListener {
@@ -207,6 +260,11 @@
}
/**
+ * Request the interface to access features implemented by AccessibilityController.
+ */
+ public abstract AccessibilityControllerInternal getAccessibilityController();
+
+ /**
* Request that the window manager call
* {@link DisplayManagerInternal#performTraversalInTransactionFromWindowManager}
* within a surface transaction at a later time.
@@ -351,8 +409,10 @@
* @param token The token to add.
* @param type The window type.
* @param displayId The display to add the token to.
+ * @param options A bundle used to pass window-related options.
*/
- public abstract void addWindowToken(android.os.IBinder token, int type, int displayId);
+ public abstract void addWindowToken(@NonNull android.os.IBinder token, int type, int displayId,
+ @Nullable Bundle options);
/**
* Removes a window token.
@@ -372,6 +432,14 @@
public abstract void registerAppTransitionListener(AppTransitionListener listener);
/**
+ * Registers a listener to be notified to start the keyguard exit animation.
+ *
+ * @param listener The listener to register.
+ */
+ public abstract void registerKeyguardExitAnimationStartListener(
+ KeyguardExitAnimationStartListener listener);
+
+ /**
* Reports that the password for the given user has changed.
*/
public abstract void reportPasswordChanged(int userId);
@@ -543,9 +611,9 @@
public abstract void removeNonHighRefreshRatePackage(@NonNull String packageName);
/**
- * Checks if this display is touchable.
+ * Checks if the device supports touch or faketouch.
*/
- public abstract boolean isTouchableDisplay(int displayId);
+ public abstract boolean isTouchOrFaketouchDevice();
/**
* Returns the info associated with the input token used to determine if a key should be
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6319c80..c54c978 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -23,7 +23,6 @@
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
import static android.Manifest.permission.RESTRICTED_VR_ACCESS;
-import static android.Manifest.permission.STATUS_BAR_SERVICE;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
@@ -35,7 +34,6 @@
import static android.content.pm.PackageManager.FEATURE_PC;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
-import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myPid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -82,8 +80,6 @@
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManagerGlobal.ADD_OKAY;
-import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS;
-import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_BLAST_SYNC;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
@@ -104,6 +100,7 @@
import static com.android.server.LockGuard.INDEX_WINDOW;
import static com.android.server.LockGuard.installLock;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_FREEZE_DISPLAY;
import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
@@ -405,6 +402,8 @@
// trying to apply a new one.
private static final boolean ALWAYS_KEEP_CURRENT = true;
+ static final int LOGTAG_INPUT_FOCUS = 62001;
+
/**
* Restrict ability of activities overriding transition animation in a way such that
* an activity can do it only when the transition happens within a same task.
@@ -413,7 +412,6 @@
*/
private static final String DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY =
"persist.wm.disable_custom_task_animation";
- static final int LOGTAG_INPUT_FOCUS = 62001;
/**
* @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY
@@ -421,6 +419,19 @@
static boolean sDisableCustomTaskAnimationProperty =
SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
+ /**
+ * Run Keyguard animation as remote animation in System UI instead of local animation in
+ * the server process.
+ */
+ private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
+ "persist.wm.enable_remote_keyguard_animation";
+
+ /**
+ * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
+ */
+ public static boolean sEnableRemoteKeyguardAnimation =
+ SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
+
private static final String DISABLE_TRIPLE_BUFFERING_PROPERTY =
"ro.sf.disable_triple_buffer";
@@ -444,12 +455,6 @@
private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000;
- /** The maximum count of window tokens without surface that an app can register. */
- private static final int MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE = 5;
-
- /** System UI can create more window context... */
- private static final int SYSTEM_UI_MULTIPLIER = 2;
-
/**
* Override of task letterbox aspect ratio that is set via ADB with
* set-task-letterbox-aspect-ratio or via {@link
@@ -626,13 +631,6 @@
final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
/**
- * Windows with a preserved surface waiting to be destroyed. These windows
- * are going through a surface change. We keep the old surface around until
- * the first frame on the new surface finishes drawing.
- */
- final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();
-
- /**
* This is set when we have run out of memory, and will either be an empty
* list or contain windows that need to be force removed.
*/
@@ -753,6 +751,7 @@
final TaskSnapshotController mTaskSnapshotController;
boolean mIsTouchDevice;
+ boolean mIsFakeTouchDevice;
final H mH = new H();
@@ -978,7 +977,7 @@
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;
}
@@ -1604,7 +1603,7 @@
final Bundle options = mWindowContextListenerController
.getOptions(windowContextToken);
token = new WindowToken(this, binder, type, false /* persistOnEmpty */,
- displayContent, session.mCanAddInternalSystemWindow, callingUid,
+ displayContent, session.mCanAddInternalSystemWindow,
isRoundedCornerOverlay, true /* fromClientToken */, options);
} else {
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
@@ -2151,25 +2150,9 @@
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();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -2195,8 +2178,8 @@
// We need to report touchable region changes to accessibility.
if (mAccessibilityController != null) {
- mAccessibilityController.onSomeWindowResizedOrMovedLocked(
- w.getDisplayContent().getDisplayId());
+ mAccessibilityController.onSomeWindowResizedOrMovedWithCallingUid(
+ uid, w.getDisplayContent().getDisplayId());
}
}
}
@@ -2210,7 +2193,7 @@
if (mAccessibilityController != null) {
WindowState window = mWindowMap.get(token);
if (window != null) {
- mAccessibilityController.onRectangleOnScreenRequestedLocked(
+ mAccessibilityController.onRectangleOnScreenRequested(
window.getDisplayId(), rectangle);
}
}
@@ -2313,8 +2296,8 @@
if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0)
&& (mAccessibilityController != null)) {
// No move or resize, but the controller checks for title changes as well
- mAccessibilityController.onSomeWindowResizedOrMovedLocked(
- win.getDisplayContent().getDisplayId());
+ mAccessibilityController.onSomeWindowResizedOrMovedWithCallingUid(
+ uid, win.getDisplayContent().getDisplayId());
}
if ((privateFlagChanges & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
@@ -2329,7 +2312,6 @@
if (DEBUG_LAYOUT) Slog.v(TAG_WM, "Relayout " + win + ": viewVisibility=" + viewVisibility
+ " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs);
- winAnimator.mSurfaceDestroyDeferred = (flags & RELAYOUT_DEFER_SURFACE_DESTROY) != 0;
if ((attrChanges & WindowManager.LayoutParams.ALPHA_CHANGED) != 0) {
winAnimator.mAlpha = attrs.alpha;
}
@@ -2615,7 +2597,7 @@
win.destroySurface(false, stopped);
}
if (mAccessibilityController != null) {
- mAccessibilityController.onWindowTransitionLocked(win, transit);
+ mAccessibilityController.onWindowTransition(win, transit);
}
return focusMayChange;
@@ -2708,91 +2690,36 @@
}
@Override
- public void addWindowToken(IBinder binder, int type, int displayId) {
- addWindowTokenWithOptions(binder, type, displayId, null /* options */,
- null /* packageName */, false /* fromClientToken */);
- }
-
- @Override
- public int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options,
- String packageName) {
- if (tokenCountExceed()) {
- return ADD_TOO_MANY_TOKENS;
+ public void addWindowToken(@NonNull IBinder binder, int type, int displayId,
+ @Nullable Bundle options) {
+ if (!checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()")) {
+ throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
- return addWindowTokenWithOptions(binder, type, displayId, options, packageName,
- true /* fromClientToken */);
- }
- private boolean tokenCountExceed() {
- final int callingUid = Binder.getCallingUid();
- // Don't check if caller is from system server.
- if (callingUid == myPid()) {
- return false;
- }
- final int limit =
- (checkCallingPermission(STATUS_BAR_SERVICE, "addWindowTokenWithOptions"))
- ? MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE * SYSTEM_UI_MULTIPLIER
- : MAXIMUM_WINDOW_TOKEN_COUNT_WITHOUT_SURFACE;
synchronized (mGlobalLock) {
- int[] count = new int[1];
- mRoot.forAllDisplays(d -> count[0] += d.getWindowTokensWithoutSurfaceCount(callingUid));
- return count[0] >= limit;
- }
- }
+ final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */);
+ if (dc == null) {
+ ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add token: %s"
+ + " for non-exiting displayId=%d", binder, displayId);
+ return;
+ }
- private int addWindowTokenWithOptions(IBinder binder, int type, int displayId, Bundle options,
- String packageName, boolean fromClientToken) {
- final boolean callerCanManageAppTokens =
- checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()");
- // WindowContext users usually don't hold MANAGE_APP_TOKEN permission. Check permissions
- // by checkAddPermission.
- if (!callerCanManageAppTokens) {
- final int res = mPolicy.checkAddPermission(type, false /* isRoundedCornerOverlay */,
- packageName, new int[1]);
- if (res != ADD_OKAY) {
- return res;
+ WindowToken token = dc.getWindowToken(binder);
+ if (token != null) {
+ ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add binder token: %s"
+ + " for already created window token: %s"
+ + " displayId=%d", binder, token, displayId);
+ return;
+ }
+ if (type == TYPE_WALLPAPER) {
+ new WallpaperWindowToken(this, binder, true, dc,
+ true /* ownerCanManageAppTokens */, options);
+ } else {
+ new WindowToken(this, binder, type, true /* persistOnEmpty */, dc,
+ true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
+ false /* fromClientToken */, options);
}
}
-
- final int callingUid = Binder.getCallingUid();
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- if (!callerCanManageAppTokens) {
- if (packageName == null || !unprivilegedAppCanCreateTokenWith(
- null /* parentWindow */, callingUid, type, type, binder,
- packageName)) {
- throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
- }
- }
-
- final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */);
- if (dc == null) {
- ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add token: %s"
- + " for non-exiting displayId=%d", binder, displayId);
- return WindowManagerGlobal.ADD_INVALID_DISPLAY;
- }
-
- WindowToken token = dc.getWindowToken(binder);
- if (token != null) {
- ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add binder token: %s"
- + " for already created window token: %s"
- + " displayId=%d", binder, token, displayId);
- return WindowManagerGlobal.ADD_DUPLICATE_ADD;
- }
- // TODO(window-container): Clean up dead tokens
- if (type == TYPE_WALLPAPER) {
- new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens,
- options);
- } else {
- new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens,
- callingUid, false /* roundedCornerOverlay */, fromClientToken, options);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- return WindowManagerGlobal.ADD_OKAY;
}
/**
@@ -2872,38 +2799,26 @@
@Override
public void removeWindowToken(IBinder binder, int displayId) {
- final boolean callerCanManageAppTokens =
- checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()");
- final WindowToken windowToken;
- synchronized (mGlobalLock) {
- windowToken = mRoot.getWindowToken(binder);
+ if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) {
+ throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
- if (windowToken == null) {
- ProtoLog.w(WM_ERROR,
- "removeWindowToken: Attempted to remove non-existing token: %s", binder);
- return;
- }
- final int callingUid = Binder.getCallingUid();
-
- // If MANAGE_APP_TOKEN permission is not held(usually from WindowContext), callers can only
- // remove the window tokens which they added themselves.
- if (!callerCanManageAppTokens && (windowToken.getOwnerUid() == INVALID_UID
- || callingUid != windowToken.getOwnerUid())) {
- throw new SecurityException("removeWindowToken: Requires MANAGE_APP_TOKENS permission"
- + " to remove token owned by another uid");
- }
-
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
final DisplayContent dc = mRoot.getDisplayContent(displayId);
+
if (dc == null) {
ProtoLog.w(WM_ERROR, "removeWindowToken: Attempted to remove token: %s"
+ " for non-exiting displayId=%d", binder, displayId);
return;
}
-
- dc.removeWindowToken(binder);
+ final WindowToken token = dc.removeWindowToken(binder);
+ if (token == null) {
+ ProtoLog.w(WM_ERROR,
+ "removeWindowToken: Attempted to remove non-existing token: %s",
+ binder);
+ return;
+ }
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
}
} finally {
@@ -5030,6 +4945,8 @@
mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked);
mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TOUCHSCREEN);
+ mIsFakeTouchDevice = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_FAKETOUCH);
}
try {
@@ -5504,14 +5421,6 @@
}
}
- void destroyPreservedSurfaceLocked() {
- for (int i = mDestroyPreservedSurface.size() - 1; i >= 0 ; i--) {
- final WindowState w = mDestroyPreservedSurface.get(i);
- w.mWinAnimator.destroyPreservedSurfaceLocked(w.getSyncTransaction());
- }
- mDestroyPreservedSurface.clear();
- }
-
// -------------------------------------------------------------
// IWindowManager API
// -------------------------------------------------------------
@@ -5804,8 +5713,6 @@
if (!w.mToken.okToDisplay() && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
ProtoLog.v(WM_DEBUG_ORIENTATION, "Changing surface while display frozen: %s", w);
w.setOrientationChanging(true);
- w.mLastFreezeDuration = 0;
- mRoot.mOrientationChangeComplete = false;
if (mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_NONE) {
mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
// XXX should probably keep timeout from
@@ -5916,6 +5823,9 @@
"startFreezingDisplayLocked: exitAnim=%d enterAnim=%d called by %s",
exitAnim, enterAnim, Debug.getCallers(8));
mScreenFrozenLock.acquire();
+ // Apply launch power mode to reduce screen frozen time because orientation change may
+ // relaunch activity and redraw windows. This may also help speed up user switching.
+ mAtmService.startLaunchPowerMode(POWER_MODE_REASON_FREEZE_DISPLAY);
mDisplayFrozen = true;
mDisplayFreezeTime = SystemClock.elapsedRealtime();
@@ -6059,6 +5969,7 @@
if (configChanged) {
displayContent.sendNewConfiguration();
}
+ mAtmService.endLaunchPowerMode(POWER_MODE_REASON_FREEZE_DISPLAY);
mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN);
}
@@ -7154,6 +7065,7 @@
checkCallerOwnsDisplay(displayId);
synchronized (mGlobalLock) {
+ int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
final WindowState win = windowForClientLocked(null, client, false);
@@ -7165,8 +7077,8 @@
// Notifies AccessibilityController to re-compute the window observer of
// this embedded display
if (mAccessibilityController != null) {
- mAccessibilityController.handleWindowObserverOfEmbeddedDisplayLocked(displayId,
- win);
+ mAccessibilityController.handleWindowObserverOfEmbeddedDisplay(
+ displayId, win, uid);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -7532,6 +7444,12 @@
private final class LocalService extends WindowManagerInternal {
@Override
+ public AccessibilityControllerInternal getAccessibilityController() {
+ return AccessibilityController.getAccessibilityControllerInternal(
+ WindowManagerService.this);
+ }
+
+ @Override
public void clearSnapshotCache() {
synchronized (mGlobalLock) {
mTaskSnapshotController.clearSnapshotCache();
@@ -7549,7 +7467,7 @@
public void setMagnificationSpec(int displayId, MagnificationSpec spec) {
synchronized (mGlobalLock) {
if (mAccessibilityController != null) {
- mAccessibilityController.setMagnificationSpecLocked(displayId, spec);
+ mAccessibilityController.setMagnificationSpec(displayId, spec);
} else {
throw new IllegalStateException("Magnification callbacks not set!");
}
@@ -7560,7 +7478,7 @@
public void setForceShowMagnifiableBounds(int displayId, boolean show) {
synchronized (mGlobalLock) {
if (mAccessibilityController != null) {
- mAccessibilityController.setForceShowMagnifiableBoundsLocked(displayId, show);
+ mAccessibilityController.setForceShowMagnifiableBounds(displayId, show);
} else {
throw new IllegalStateException("Magnification callbacks not set!");
}
@@ -7571,8 +7489,7 @@
public void getMagnificationRegion(int displayId, @NonNull Region magnificationRegion) {
synchronized (mGlobalLock) {
if (mAccessibilityController != null) {
- mAccessibilityController.getMagnificationRegionLocked(displayId,
- magnificationRegion);
+ mAccessibilityController.getMagnificationRegion(displayId, magnificationRegion);
} else {
throw new IllegalStateException("Magnification callbacks not set!");
}
@@ -7588,7 +7505,7 @@
}
MagnificationSpec spec = null;
if (mAccessibilityController != null) {
- spec = mAccessibilityController.getMagnificationSpecForWindowLocked(windowState);
+ spec = mAccessibilityController.getMagnificationSpecForWindow(windowState);
}
if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) {
return null;
@@ -7610,9 +7527,9 @@
mAccessibilityController = new AccessibilityController(
WindowManagerService.this);
}
- boolean result = mAccessibilityController.setMagnificationCallbacksLocked(
+ boolean result = mAccessibilityController.setMagnificationCallbacks(
displayId, callbacks);
- if (!mAccessibilityController.hasCallbacksLocked()) {
+ if (!mAccessibilityController.hasCallbacks()) {
mAccessibilityController = null;
}
return result;
@@ -7628,9 +7545,9 @@
WindowManagerService.this);
}
final boolean result =
- mAccessibilityController.setWindowsForAccessibilityCallbackLocked(
+ mAccessibilityController.setWindowsForAccessibilityCallback(
displayId, callback);
- if (!mAccessibilityController.hasCallbacksLocked()) {
+ if (!mAccessibilityController.hasCallbacks()) {
mAccessibilityController = null;
}
return result;
@@ -7719,8 +7636,9 @@
}
@Override
- public void addWindowToken(IBinder token, int type, int displayId) {
- WindowManagerService.this.addWindowToken(token, type, displayId);
+ public void addWindowToken(IBinder token, int type, int displayId,
+ @Nullable Bundle options) {
+ WindowManagerService.this.addWindowToken(token, type, displayId, options);
}
@Override
@@ -7763,6 +7681,15 @@
}
@Override
+ public void registerKeyguardExitAnimationStartListener(
+ KeyguardExitAnimationStartListener listener) {
+ synchronized (mGlobalLock) {
+ getDefaultDisplayContentLocked().mAppTransition
+ .registerKeygaurdExitAnimationStartListener(listener);
+ }
+ }
+
+ @Override
public void reportPasswordChanged(int userId) {
mKeyguardDisableHandler.updateKeyguardEnabled(userId);
}
@@ -7819,7 +7746,7 @@
accessibilityController = mAccessibilityController;
}
if (accessibilityController != null) {
- accessibilityController.performComputeChangedWindowsNotLocked(displayId, true);
+ accessibilityController.performComputeChangedWindowsNot(displayId, true);
}
}
@@ -8018,13 +7945,10 @@
}
@Override
- public boolean isTouchableDisplay(int displayId) {
+ public boolean isTouchOrFaketouchDevice() {
synchronized (mGlobalLock) {
- final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
- final Configuration configuration =
- displayContent != null ? displayContent.getConfiguration() : null;
- return configuration != null
- && configuration.touchscreen == Configuration.TOUCHSCREEN_FINGER;
+ // All touchable devices are also faketouchable.
+ return mIsFakeTouchDevice;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
index da31bb2..5ef9420 100644
--- a/services/core/java/com/android/server/wm/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -16,25 +16,36 @@
package com.android.server.wm;
+import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER;
+
import static com.android.server.wm.WindowOrientationListenerProto.ENABLED;
import static com.android.server.wm.WindowOrientationListenerProto.ROTATION;
+import android.app.ActivityThread;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.rotationresolver.RotationResolverInternal;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Surface;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.LocalServices;
import java.io.PrintWriter;
import java.util.List;
+import java.util.Set;
/**
* A special helper class used by the WindowManager
@@ -55,6 +66,9 @@
private static final boolean USE_GRAVITY_SENSOR = false;
private static final int DEFAULT_BATCH_LATENCY = 100000;
+ private static final int DEFAULT_ROTATION_RESOLVER_ENABLED = 0; // disabled
+ private static final String KEY_ROTATION_RESOLVER_TIMEOUT = "rotation_resolver_timeout_millis";
+ private static final long DEFAULT_ROTATION_RESOLVER_TIMEOUT_MILLIS = 700L;
private Handler mHandler;
private SensorManager mSensorManager;
@@ -62,7 +76,13 @@
private int mRate;
private String mSensorType;
private Sensor mSensor;
- private OrientationJudge mOrientationJudge;
+
+ @VisibleForTesting
+ OrientationJudge mOrientationJudge;
+
+ @VisibleForTesting
+ RotationResolverInternal mRotationResolverService;
+
private int mCurrentRotation = -1;
private final Context mContext;
private final WindowManagerConstants mConstants;
@@ -256,6 +276,32 @@
}
/**
+ * Returns true if the current status of the phone is suitable for using rotation resolver
+ * service.
+ *
+ * To reduce the power consumption of rotation resolver service, rotation query should run less
+ * frequently than other low power orientation sensors. This method is used to check whether
+ * the current status of the phone is necessary to request a suggested screen rotation from the
+ * rotation resolver service. Note that it always returns {@code false} in the base class. It
+ * should be overridden in the derived classes.
+ */
+ public boolean canUseRotationResolver() {
+ return false;
+ }
+
+ /**
+ * Returns true if the rotation resolver feature is enabled by setting. It means {@link
+ * WindowOrientationListener} will then ask {@link RotationResolverInternal} for the appropriate
+ * screen rotation.
+ */
+ @VisibleForTesting
+ boolean isRotationResolverEnabled() {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.CAMERA_AUTOROTATE, DEFAULT_ROTATION_RESOLVER_ENABLED,
+ UserHandle.USER_CURRENT) == 1;
+ }
+
+ /**
* Called when the rotation view of the device has changed.
*
* This method is called whenever the orientation becomes certain of an orientation.
@@ -1045,6 +1091,30 @@
private int mProposedRotation = -1;
private int mDesiredRotation = -1;
private boolean mRotationEvaluationScheduled;
+ private long mRotationResolverTimeoutMillis;
+
+ OrientationSensorJudge() {
+ super();
+ setupRotationResolverParameters();
+ }
+
+ private void setupRotationResolverParameters() {
+ DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_WINDOW_MANAGER,
+ ActivityThread.currentApplication().getMainExecutor(), (properties) -> {
+ final Set<String> keys = properties.getKeyset();
+ if (keys.contains(KEY_ROTATION_RESOLVER_TIMEOUT)) {
+ readRotationResolverParameters();
+ }
+ });
+ readRotationResolverParameters();
+ }
+
+ private void readRotationResolverParameters() {
+ mRotationResolverTimeoutMillis = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_ROTATION_RESOLVER_TIMEOUT,
+ DEFAULT_ROTATION_RESOLVER_TIMEOUT_MILLIS);
+ }
@Override
public int getProposedRotationLocked() {
@@ -1069,19 +1139,13 @@
@Override
public void onSensorChanged(SensorEvent event) {
- int newRotation;
-
int reportedRotation = (int) event.values[0];
if (reportedRotation < 0 || reportedRotation > 3) {
return;
}
- synchronized (mLock) {
- mDesiredRotation = reportedRotation;
- newRotation = evaluateRotationChangeLocked();
- }
- if (newRotation >= 0) {
- onProposedRotationChanged(newRotation);
+ // Log raw sensor rotation.
+ if (evaluateRotationChangeLocked() >= 0) {
if (mConstants.mRawSensorLoggingEnabled) {
FrameworkStatsLog.write(
FrameworkStatsLog.DEVICE_ROTATED,
@@ -1089,6 +1153,35 @@
rotationToLogEnum(reportedRotation));
}
}
+
+ if (isRotationResolverEnabled() && canUseRotationResolver()) {
+ if (mRotationResolverService == null) {
+ mRotationResolverService = LocalServices.getService(
+ RotationResolverInternal.class);
+ }
+
+ final CancellationSignal cancellationSignal = new CancellationSignal();
+ mRotationResolverService.resolveRotation(
+ new RotationResolverInternal.RotationResolverCallbackInternal() {
+ @Override
+ public void onSuccess(int result) {
+ finalizeRotation(result);
+ }
+
+ @Override
+ public void onFailure(int error) {
+ finalizeRotation(reportedRotation);
+ }
+ },
+ reportedRotation,
+ mCurrentRotation,
+ mRotationResolverTimeoutMillis,
+ cancellationSignal);
+ getHandler().postDelayed(cancellationSignal::cancel,
+ mRotationResolverTimeoutMillis);
+ } else {
+ finalizeRotation(reportedRotation);
+ }
}
@Override
@@ -1131,6 +1224,17 @@
return -1;
}
+ private void finalizeRotation(int reportedRotation) {
+ int newRotation;
+ synchronized (mLock) {
+ mDesiredRotation = reportedRotation;
+ newRotation = evaluateRotationChangeLocked();
+ }
+ if (newRotation >= 0) {
+ onProposedRotationChanged(newRotation);
+ }
+ }
+
private boolean isDesiredRotationAcceptableLocked(long now) {
if (mTouching) {
return false;
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 8b4d415..264a3b4 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1329,6 +1329,10 @@
mHasPendingConfigurationChange = true;
return;
}
+ dispatchConfiguration(config);
+ }
+
+ void dispatchConfiguration(Configuration config) {
mHasPendingConfigurationChange = false;
if (mThread == null) {
if (Build.IS_DEBUGGABLE && mHasImeService) {
@@ -1367,8 +1371,13 @@
mPauseConfigurationDispatchCount++;
}
- void resumeConfigurationDispatch() {
+ /** Returns {@code true} if the configuration change is pending to dispatch. */
+ boolean resumeConfigurationDispatch() {
+ if (mPauseConfigurationDispatchCount == 0) {
+ return false;
+ }
mPauseConfigurationDispatchCount--;
+ return mHasPendingConfigurationChange;
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 661118f..a94b0aa 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -104,7 +104,6 @@
import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME;
-import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -1491,6 +1490,10 @@
void setOrientationChanging(boolean changing) {
mOrientationChanging = changing;
mOrientationChangeTimedOut = false;
+ if (changing) {
+ mLastFreezeDuration = 0;
+ mWmService.mRoot.mOrientationChangeComplete = false;
+ }
}
void orientationChangeTimedOut() {
@@ -1722,7 +1725,7 @@
@Override
boolean isVisibleRequested() {
- return isVisible();
+ return isVisible() && (mActivityRecord == null || mActivityRecord.isVisibleRequested());
}
/**
@@ -2021,7 +2024,7 @@
final int winTransit = TRANSIT_EXIT;
mWinAnimator.applyAnimationLocked(winTransit, false /* isEntrance */);
if (accessibilityController != null) {
- accessibilityController.onWindowTransitionLocked(this, winTransit);
+ accessibilityController.onWindowTransition(this, winTransit);
}
}
setDisplayLayoutNeeded();
@@ -2035,7 +2038,7 @@
if (isVisibleNow()) {
mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false);
if (mWmService.mAccessibilityController != null) {
- mWmService.mAccessibilityController.onWindowTransitionLocked(this, TRANSIT_EXIT);
+ mWmService.mAccessibilityController.onWindowTransition(this, TRANSIT_EXIT);
}
changed = true;
if (displayContent != null) {
@@ -2113,7 +2116,7 @@
}
if (mWmService.mAccessibilityController != null) {
- mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(getDisplayId());
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(getDisplayId());
}
updateLocationInParentDisplayIfNeeded();
@@ -2133,7 +2136,8 @@
&& !mAnimatingExit
&& (mWindowFrames.mRelFrame.top != mWindowFrames.mLastRelFrame.top
|| mWindowFrames.mRelFrame.left != mWindowFrames.mLastRelFrame.left)
- && (!mIsChildWindow || !getParentWindow().hasMoved());
+ && (!mIsChildWindow || !getParentWindow().hasMoved())
+ && !mWmService.mAtmService.getTransitionController().isCollecting();
}
boolean isObscuringDisplay() {
@@ -2243,7 +2247,6 @@
disposeInputChannel();
- mWinAnimator.destroyDeferredSurfaceLocked(mTmpTransaction);
mWinAnimator.destroySurfaceLocked(mTmpTransaction);
mTmpTransaction.apply();
mSession.windowRemovedLocked();
@@ -2347,7 +2350,7 @@
mWmService.requestTraversal();
}
if (mWmService.mAccessibilityController != null) {
- mWmService.mAccessibilityController.onWindowTransitionLocked(this, transit);
+ mWmService.mAccessibilityController.onWindowTransition(this, transit);
}
}
final boolean isAnimating = mAnimatingExit || isAnimating(TRANSITION | PARENTS,
@@ -3097,7 +3100,7 @@
}
void setForceHideNonSystemOverlayWindowIfNeeded(boolean forceHide) {
- if (!mSession.mOverlaysCanBeHidden
+ if (mSession.mCanAddInternalSystemWindow
|| (!isSystemAlertWindowType(mAttrs.type) && mAttrs.type != TYPE_TOAST)) {
return;
}
@@ -3288,7 +3291,6 @@
ProtoLog.v(WM_DEBUG_ORIENTATION,
"set mOrientationChanging of %s", this);
setOrientationChanging(true);
- mWmService.mRoot.mOrientationChangeComplete = false;
}
mLastFreezeDuration = 0;
setDisplayLayoutNeeded();
@@ -3309,11 +3311,6 @@
return destroyedSomething;
}
- if (appStopped || mWindowRemovalAllowed) {
- mWinAnimator.destroyPreservedSurfaceLocked(mTmpTransaction);
- mTmpTransaction.apply();
- }
-
if (mDestroying) {
ProtoLog.e(WM_DEBUG_ADD_REMOVE, "win=%s"
+ " destroySurfaces: appStopped=%b"
@@ -3642,6 +3639,11 @@
if (mActivityRecord != null && mActivityRecord.isRelaunching()) {
return;
}
+ // If the activity is invisible or going invisible, don't report either since it is going
+ // away. This is likely during a transition so we want to preserve the original state.
+ if (mActivityRecord != null && !mActivityRecord.isVisibleRequested()) {
+ return;
+ }
if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag());
@@ -3679,7 +3681,7 @@
displayId);
if (mWmService.mAccessibilityController != null) {
- mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(displayId);
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(displayId);
}
updateLocationInParentDisplayIfNeeded();
} catch (RemoteException e) {
@@ -4782,7 +4784,7 @@
return;
}
if (mWmService.mAccessibilityController != null) {
- mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(getDisplayId());
+ mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(getDisplayId());
}
if (!isSelfOrAncestorWindowAnimatingExit()) {
@@ -5001,23 +5003,8 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
- // When we change the Surface size, in scenarios which may require changing
- // the surface position in sync with the resize, we use a preserved surface
- // so we can freeze it while waiting for the client to report draw on the newly
- // sized surface. At the moment this logic is only in place for switching
- // in and out of the big surface for split screen resize.
if (isDragResizeChanged()) {
setDragResizing();
- // We can only change top level windows to the full-screen surface when
- // resizing (as we only have one full-screen surface). So there is no need
- // to preserve and destroy windows which are attached to another, they
- // will keep their surface and its size may change over time.
- if (mHasSurface && !isChildWindow()) {
- mWinAnimator.preserveSurfaceLocked(getSyncTransaction());
- result |= RELAYOUT_RES_SURFACE_CHANGED |
- RELAYOUT_RES_FIRST_TIME;
- scheduleAnimation();
- }
}
final boolean freeformResizing = isDragResizing()
&& getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
@@ -5326,7 +5313,7 @@
updateSurfacePositionNonOrganized();
// Send information to SufaceFlinger about the priority of the current window.
updateFrameRateSelectionPriorityIfNeeded();
- updateGlobalScaleIfNeeded();
+ if (isVisibleRequested()) updateGlobalScaleIfNeeded();
mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
super.prepareSurfaces();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c8b940a..ece256e 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -52,7 +52,6 @@
import static com.android.server.wm.WindowStateAnimatorProto.DRAW_STATE;
import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
-import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import android.content.Context;
import android.graphics.Matrix;
@@ -116,15 +115,7 @@
boolean mAnimationIsEntrance;
WindowSurfaceController mSurfaceController;
- private WindowSurfaceController mPendingDestroySurface;
- /**
- * Set if the client has asked that the destroy of its surface be delayed
- * until it explicitly says it is okay.
- */
- boolean mSurfaceDestroyDeferred;
-
- private boolean mDestroyPreservedSurfaceUponRedraw;
float mShownAlpha = 0;
float mAlpha = 0;
float mLastAlpha = 0;
@@ -257,11 +248,6 @@
//dump();
mLastHidden = true;
- // We may have a preserved surface which we no longer need. If there was a quick
- // VISIBLE, GONE, VISIBLE, GONE sequence, the surface may never draw, so we don't mark
- // it to be destroyed in prepareSurfaceLocked.
- markPreservedSurfaceForDestroy();
-
if (mSurfaceController != null) {
mSurfaceController.hide(transaction, reason);
}
@@ -323,70 +309,6 @@
return result;
}
- void preserveSurfaceLocked(SurfaceControl.Transaction t) {
- if (mDestroyPreservedSurfaceUponRedraw) {
- // This could happen when switching the surface mode very fast. For example,
- // we preserved a surface when dragResizing changed to true. Then before the
- // preserved surface is removed, dragResizing changed to false again.
- // In this case, we need to leave the preserved surface alone, and destroy
- // the actual surface, so that the createSurface call could create a surface
- // of the proper size. The preserved surface will still be removed when client
- // finishes drawing to the new surface.
- mSurfaceDestroyDeferred = false;
-
- // Make sure to reparent any children of the new surface back to the preserved
- // surface before destroying it.
- if (mSurfaceController != null && mPendingDestroySurface != null) {
- mPostDrawTransaction.reparentChildren(
- mSurfaceController.mSurfaceControl,
- mPendingDestroySurface.mSurfaceControl).apply();
- }
- destroySurfaceLocked(t);
- mSurfaceDestroyDeferred = true;
- return;
- }
- ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE SET FREEZE LAYER: %s", mWin);
- if (mSurfaceController != null) {
- // Our SurfaceControl is always at layer 0 within the parent Surface managed by
- // window-state. We want this old Surface to stay on top of the new one
- // until we do the swap, so we place it at a positive layer.
- t.setLayer(mSurfaceController.mSurfaceControl, PRESERVED_SURFACE_LAYER);
- }
- mDestroyPreservedSurfaceUponRedraw = true;
- mSurfaceDestroyDeferred = true;
- destroySurfaceLocked(t);
- }
-
- void destroyPreservedSurfaceLocked(SurfaceControl.Transaction t) {
- if (!mDestroyPreservedSurfaceUponRedraw) {
- return;
- }
-
- // If we are preserving a surface but we aren't relaunching that means
- // we are just doing an in-place switch. In that case any SurfaceFlinger side
- // child layers need to be reparented to the new surface to make this
- // transparent to the app.
- // If the children are detached, we don't want to reparent them to the new surface.
- // Instead let the children get removed when the old surface is deleted.
- if (mSurfaceController != null && mPendingDestroySurface != null
- && !mPendingDestroySurface.mChildrenDetached
- && (mWin.mActivityRecord == null || !mWin.mActivityRecord.isRelaunching())) {
- mPostDrawTransaction.reparentChildren(
- mPendingDestroySurface.mSurfaceControl,
- mSurfaceController.mSurfaceControl).apply();
- }
-
- destroyDeferredSurfaceLocked(t);
- mDestroyPreservedSurfaceUponRedraw = false;
- }
-
- private void markPreservedSurfaceForDestroy() {
- if (mDestroyPreservedSurfaceUponRedraw
- && !mService.mDestroyPreservedSurface.contains(mWin)) {
- mService.mDestroyPreservedSurface.add(mWin);
- }
- }
-
void resetDrawState() {
mDrawState = DRAW_PENDING;
@@ -508,39 +430,23 @@
return;
}
- // When destroying a surface we want to make sure child windows are hidden. If we are
- // preserving the surface until redraw though we intend to swap it out with another surface
- // for resizing. In this case the window always remains visible to the user and the child
- // windows should likewise remain visible.
- if (!mDestroyPreservedSurfaceUponRedraw) {
- mWin.mHidden = true;
- }
+ mWin.mHidden = true;
try {
- if (DEBUG_VISIBILITY) logWithStack(TAG, "Window " + this + " destroying surface "
- + mSurfaceController + ", session " + mSession);
- if (mSurfaceDestroyDeferred) {
- if (mSurfaceController != null && mPendingDestroySurface != mSurfaceController) {
- if (mPendingDestroySurface != null) {
- ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY PENDING: %s. %s",
- mWin, new RuntimeException().fillInStackTrace());
- mPendingDestroySurface.destroy(t);
- }
- mPendingDestroySurface = mSurfaceController;
- }
- } else {
- ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s",
- mWin, new RuntimeException().fillInStackTrace());
- destroySurface(t);
+ if (DEBUG_VISIBILITY) {
+ logWithStack(TAG, "Window " + this + " destroying surface "
+ + mSurfaceController + ", session " + mSession);
}
+ ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s",
+ mWin, new RuntimeException().fillInStackTrace());
+ destroySurface(t);
// Don't hide wallpaper if we're deferring the surface destroy
// because of a surface change.
- if (!mDestroyPreservedSurfaceUponRedraw) {
- mWallpaperControllerLocked.hideWallpapers(mWin);
- }
+ mWallpaperControllerLocked.hideWallpapers(mWin);
} catch (RuntimeException e) {
Slog.w(TAG, "Exception thrown when destroying Window " + this
- + " surface " + mSurfaceController + " session " + mSession + ": " + e.toString());
+ + " surface " + mSurfaceController + " session " + mSession + ": "
+ + e.toString());
}
// Whether the surface was preserved (and copied to mPendingDestroySurface) or not, it
@@ -554,27 +460,6 @@
mDrawState = NO_SURFACE;
}
- void destroyDeferredSurfaceLocked(SurfaceControl.Transaction t) {
- try {
- if (mPendingDestroySurface != null) {
- ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY PENDING: %s. %s",
- mWin, new RuntimeException().fillInStackTrace());
- mPendingDestroySurface.destroy(t);
- // Don't hide wallpaper if we're destroying a deferred surface
- // after a surface mode change.
- if (!mDestroyPreservedSurfaceUponRedraw) {
- mWallpaperControllerLocked.hideWallpapers(mWin);
- }
- }
- } catch (RuntimeException e) {
- Slog.w(TAG, "Exception thrown when destroying Window "
- + this + " surface " + mPendingDestroySurface
- + " session " + mSession + ": " + e.toString());
- }
- mSurfaceDestroyDeferred = false;
- mPendingDestroySurface = null;
- }
-
void computeShownFrameLocked() {
if (mIsWallpaper && mService.mRoot.mWallpaperActionPending) {
return;
@@ -744,7 +629,6 @@
if (prepared && mDrawState == HAS_DRAWN) {
if (mLastHidden) {
if (showSurfaceRobustlyLocked(t)) {
- markPreservedSurfaceForDestroy();
mAnimator.requestRemovalOfReplacedWindows(w);
mLastHidden = false;
if (mIsWallpaper) {
@@ -779,7 +663,7 @@
if (w.getOrientationChanging()) {
if (!w.isDrawn()) {
- mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE;
+ w.mWmService.mRoot.mOrientationChangeComplete = false;
mAnimator.mLastWindowFreezeSource = w;
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Orientation continue waiting for draw in %s", w);
@@ -794,14 +678,6 @@
}
}
- 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;
@@ -905,20 +781,6 @@
if (!shown)
return false;
- // If we had a preserved surface it's no longer needed, and it may be harmful
- // if we are transparent.
- if (mPendingDestroySurface != null && mDestroyPreservedSurfaceUponRedraw) {
- final SurfaceControl pendingSurfaceControl = mPendingDestroySurface.mSurfaceControl;
- mPostDrawTransaction.reparent(pendingSurfaceControl, null);
- // If the children are detached, we don't want to reparent them to the new surface.
- // Instead let the children get removed when the old surface is deleted.
- if (!mPendingDestroySurface.mChildrenDetached) {
- mPostDrawTransaction.reparentChildren(
- mPendingDestroySurface.mSurfaceControl,
- mSurfaceController.mSurfaceControl);
- }
- }
-
t.merge(mPostDrawTransaction);
return true;
}
@@ -946,7 +808,7 @@
}
if (mService.mAccessibilityController != null) {
- mService.mAccessibilityController.onWindowTransitionLocked(mWin, transit);
+ mService.mAccessibilityController.onWindowTransition(mWin, transit);
}
}
@@ -1058,13 +920,6 @@
pw.println();
}
- if (mPendingDestroySurface != null) {
- pw.print(prefix); pw.print("mPendingDestroySurface=");
- pw.println(mPendingDestroySurface);
- }
- if (mSurfaceDestroyDeferred) {
- pw.print(" mSurfaceDestroyDeferred="); pw.println(mSurfaceDestroyDeferred);
- }
if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) {
pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha);
pw.print(" mAlpha="); pw.print(mAlpha);
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 5f4477d..636f0bb 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -55,8 +55,6 @@
private boolean mSurfaceShown = false;
private float mSurfaceX = 0;
private float mSurfaceY = 0;
- private int mSurfaceW = 0;
- private int mSurfaceH = 0;
// Initialize to the identity matrix.
private float mLastDsdx = 1;
@@ -82,9 +80,6 @@
int flags, WindowStateAnimator animator, int windowType) {
mAnimator = animator;
- mSurfaceW = w;
- mSurfaceH = h;
-
title = name;
mService = animator.mService;
@@ -104,8 +99,8 @@
.setMetadata(METADATA_OWNER_PID, mWindowSession.mPid)
.setCallsite("WindowSurfaceController");
- final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags &
- WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
+ final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags
+ & WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
if (useBLAST) {
b.setBLASTLayer();
@@ -119,7 +114,6 @@
void hide(SurfaceControl.Transaction transaction, String reason) {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE HIDE ( %s ): %s", reason, title);
- mAnimator.destroyPreservedSurfaceLocked(transaction);
if (mSurfaceShown) {
hideSurface(transaction);
}
@@ -200,22 +194,6 @@
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);
@@ -335,9 +313,7 @@
pw.print(" layer="); pw.print(mSurfaceLayer);
pw.print(" alpha="); pw.print(mSurfaceAlpha);
pw.print(" rect=("); pw.print(mSurfaceX);
- pw.print(","); pw.print(mSurfaceY);
- pw.print(") "); pw.print(mSurfaceW);
- pw.print(" x "); pw.print(mSurfaceH);
+ pw.print(","); pw.print(mSurfaceY); pw.print(") ");
pw.print(" transform=("); pw.print(mLastDsdx); pw.print(", ");
pw.print(mLastDtdx); pw.print(", "); pw.print(mLastDsdy);
pw.print(", "); pw.print(mLastDtdy); pw.println(")");
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 6b9fbcb..2ee5fb0 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -43,8 +43,7 @@
private int mLayoutRepeatCount;
static final int SET_UPDATE_ROTATION = 1 << 0;
- static final int SET_ORIENTATION_CHANGE_COMPLETE = 1 << 2;
- static final int SET_WALLPAPER_ACTION_PENDING = 1 << 3;
+ static final int SET_WALLPAPER_ACTION_PENDING = 1 << 1;
private boolean mTraversalScheduled;
private int mDeferDepth = 0;
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index cd18311..1c3fe02 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -113,8 +112,6 @@
*/
private final boolean mFromClientToken;
- private final int mOwnerUid;
-
/**
* Used to fix the transform of the token to be rotated to a rotation different than it's
* display. The window frames and surfaces corresponding to this token will be layouted and
@@ -205,27 +202,19 @@
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) {
- this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, INVALID_UID,
- roundedCornerOverlay, false /* fromClientToken */);
+ this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens,
+ roundedCornerOverlay, false /* fromClientToken */, null /* options */);
}
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
- DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
- boolean roundedCornerOverlay, boolean fromClientToken) {
- this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, ownerUid,
- roundedCornerOverlay, fromClientToken, null /* options */);
- }
-
- WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
- DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
- boolean roundedCornerOverlay, boolean fromClientToken, @Nullable Bundle options) {
+ DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay,
+ boolean fromClientToken, @Nullable Bundle options) {
super(service);
token = _token;
windowType = type;
mOptions = options;
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
- mOwnerUid = ownerUid;
mRoundedCornerOverlay = roundedCornerOverlay;
mFromClientToken = fromClientToken;
if (dc != null) {
@@ -641,9 +630,14 @@
void updateSurfacePosition(SurfaceControl.Transaction t) {
super.updateSurfacePosition(t);
if (isFixedRotationTransforming()) {
- // The window is layouted in a simulated rotated display but the real display hasn't
- // rotated, so here transforms its surface to fit in the real display.
- mFixedRotationTransformState.transform(this);
+ final ActivityRecord r = asActivityRecord();
+ final Task rootTask = r != null ? r.getRootTask() : null;
+ // Don't transform the activity in PiP because the PiP task organizer will handle it.
+ if (rootTask == null || !rootTask.inPinnedWindowingMode()) {
+ // The window is laid out in a simulated rotated display but the real display hasn't
+ // rotated, so here transforms its surface to fit in the real display.
+ mFixedRotationTransformState.transform(this);
+ }
}
}
@@ -739,10 +733,6 @@
mRoundedCornerOverlay);
}
- int getOwnerUid() {
- return mOwnerUid;
- }
-
boolean isFromClient() {
return mFromClientToken;
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index cc7e00a..1c4b034 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -56,10 +56,10 @@
"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",
],
@@ -134,7 +134,7 @@
"android.hardware.broadcastradio@1.0",
"android.hardware.broadcastradio@1.1",
"android.hardware.contexthub@1.0",
- "android.hardware.gnss-cpp",
+ "android.hardware.gnss-V1-cpp",
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
@@ -147,12 +147,12 @@
"android.hardware.light@2.0",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
- "android.hardware.power-cpp",
+ "android.hardware.power-V1-cpp",
"android.hardware.power.stats@1.0",
"android.hardware.power.stats-ndk_platform",
"android.hardware.thermal@1.0",
"android.hardware.tv.input@1.0",
- "android.hardware.vibrator-unstable-cpp",
+ "android.hardware.vibrator-V2-cpp",
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
"android.hardware.vibrator@1.2",
@@ -162,7 +162,7 @@
"android.frameworks.schedulerservice@1.0",
"android.frameworks.sensorservice@1.0",
"android.frameworks.stats@1.0",
- "android.system.suspend.control-cpp",
+ "android.system.suspend.control-V1-cpp",
"android.system.suspend.control.internal-cpp",
"android.system.suspend@1.0",
"service.incremental",
@@ -193,3 +193,10 @@
"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 71de9bd..5dbb71a 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");
+ }
- vibrator::ManagerHalWrapper* hal() const { return mHal.get(); }
+ ~NativeVibratorManagerService() {
+ auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
+ jniEnv->DeleteGlobalRef(mCallbackListener);
+ }
+
+ vibrator::ManagerHalController* 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 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 @@
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 @@
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 3f2a322..22950c5 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 31cc295..4551d49 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 @@
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 @@
(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_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 5b587e9..643503d 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -106,6 +106,7 @@
jmethodID notifyFocusChanged;
jmethodID notifySensorEvent;
jmethodID notifySensorAccuracy;
+ jmethodID notifyVibratorState;
jmethodID notifyUntrustedTouch;
jmethodID filterInputEvent;
jmethodID interceptKeyBeforeQueueing;
@@ -305,6 +306,7 @@
const std::vector<float>& values) override;
void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType,
InputDeviceSensorAccuracy accuracy) override;
+ void notifyVibratorState(int32_t deviceId, bool isOn) override;
void notifyUntrustedTouch(const std::string& obscuringPackage) override;
bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override;
@@ -918,6 +920,18 @@
checkAndClearExceptionFromCallback(env, "notifySensorAccuracy");
}
+void NativeInputManager::notifyVibratorState(int32_t deviceId, bool isOn) {
+#if DEBUG_INPUT_DISPATCHER_POLICY
+ ALOGD("notifyVibratorState isOn:%d", isOn);
+#endif
+ ATRACE_CALL();
+ JNIEnv* env = jniEnv();
+ ScopedLocalFrame localFrame(env);
+ env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyVibratorState,
+ static_cast<jint>(deviceId), static_cast<jboolean>(isOn));
+ checkAndClearExceptionFromCallback(env, "notifyVibratorState");
+}
+
void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) {
ATRACE_CALL();
JNIEnv* env = jniEnv();
@@ -2248,6 +2262,8 @@
GET_METHOD_ID(gServiceClassInfo.notifySensorAccuracy, clazz, "notifySensorAccuracy", "(III)V");
+ GET_METHOD_ID(gServiceClassInfo.notifyVibratorState, clazz, "notifyVibratorState", "(IZ)V");
+
GET_METHOD_ID(gServiceClassInfo.notifyUntrustedTouch, clazz, "notifyUntrustedTouch",
"(Ljava/lang/String;)V");
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index 7ed3748..4e47984 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 @@
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 c5394f3..34f6048 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -39,7 +39,7 @@
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 @@
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/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 48f8b15..59b7367 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -136,6 +136,8 @@
private static final String TAG_PASSWORD_COMPLEXITY = "password-complexity";
private static final String TAG_ORGANIZATION_ID = "organization-id";
private static final String TAG_ENROLLMENT_SPECIFIC_ID = "enrollment-specific-id";
+ private static final String TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS =
+ "admin-can-grant-sensors-permissions";
private static final String ATTR_VALUE = "value";
private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
@@ -277,6 +279,7 @@
boolean mCommonCriteriaMode;
public String mOrganizationId;
public String mEnrollmentSpecificId;
+ public boolean mAdminCanGrantSensorsPermissions;
ActiveAdmin(DeviceAdminInfo info, boolean isParent) {
this.info = info;
@@ -543,6 +546,8 @@
if (!TextUtils.isEmpty(mEnrollmentSpecificId)) {
writeTextToXml(out, TAG_ENROLLMENT_SPECIFIC_ID, mEnrollmentSpecificId);
}
+ writeAttributeValueToXml(out, TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS,
+ mAdminCanGrantSensorsPermissions);
}
void writeTextToXml(TypedXmlSerializer out, String tag, String text) throws IOException {
@@ -792,6 +797,9 @@
Log.w(DevicePolicyManagerService.LOG_TAG,
"Missing Enrollment-specific ID.");
}
+ } else if (TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS.equals(tag)) {
+ mAdminCanGrantSensorsPermissions = parser.getAttributeBoolean(null, ATTR_VALUE,
+ false);
} else {
Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown admin tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -1143,5 +1151,8 @@
pw.print("mEnrollmentSpecificId=");
pw.println(mEnrollmentSpecificId);
}
+
+ pw.print("mAdminCanGrantSensorsPermissions");
+ pw.println(mAdminCanGrantSensorsPermissions);
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 11e4db5..b52347f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -16,6 +16,7 @@
package com.android.server.devicepolicy;
import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.app.admin.DevicePolicySafetyChecker;
import android.app.admin.FullyManagedDeviceProvisioningParams;
import android.app.admin.IDevicePolicyManager;
@@ -127,4 +128,10 @@
public void provisionFullyManagedDevice(
FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) {
}
+
+ public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {}
+
+ public boolean canAdminGrantSensorsPermissionsForUser(int userId) {
+ return false;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
index 8b2beb2..8ea21ec 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java
@@ -46,11 +46,15 @@
@GuardedBy("mLock")
private final SparseIntArray mPermissionPolicy = new SparseIntArray();
+ @GuardedBy("mLock")
+ private final SparseBooleanArray mCanGrantSensorsPermissions = new SparseBooleanArray();
+
public void onUserRemoved(int userHandle) {
synchronized (mLock) {
mScreenCaptureDisabled.delete(userHandle);
mPasswordQuality.delete(userHandle);
mPermissionPolicy.delete(userHandle);
+ mCanGrantSensorsPermissions.delete(userHandle);
}
}
@@ -97,6 +101,21 @@
}
}
+ @Override
+ public boolean canAdminGrantSensorsPermissionsForUser(@UserIdInt int userHandle) {
+ synchronized (mLock) {
+ return mCanGrantSensorsPermissions.get(userHandle, false);
+ }
+ }
+
+ /** Sets ahmin control over permission grants for user. */
+ public void setAdminCanGrantSensorsPermissions(@UserIdInt int userHandle,
+ boolean canGrant) {
+ synchronized (mLock) {
+ mCanGrantSensorsPermissions.put(userHandle, canGrant);
+ }
+ }
+
/** Dump content */
public void dump(IndentingPrintWriter pw) {
pw.println("Device policy cache:");
@@ -104,6 +123,8 @@
pw.println("Screen capture disabled: " + mScreenCaptureDisabled.toString());
pw.println("Password quality: " + mPasswordQuality.toString());
pw.println("Permission policy: " + mPermissionPolicy.toString());
+ pw.println("Admin can grant sensors permission: "
+ + mCanGrantSensorsPermissions.toString());
pw.decreaseIndent();
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 3d2e5de..404b0cf 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -313,6 +313,7 @@
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import com.android.server.devicepolicy.ActiveAdmin.TrustAgentInfo;
+import com.android.server.devicepolicy.Owners.OwnerDto;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.RestrictionsSet;
@@ -910,12 +911,20 @@
};
protected static class RestrictionsListener implements UserRestrictionsListener {
- private Context mContext;
+ private final Context mContext;
+ private final UserManagerInternal mUserManagerInternal;
+ private final DevicePolicyManagerService mDpms;
- public RestrictionsListener(Context context) {
+ public RestrictionsListener(
+ Context context,
+ UserManagerInternal userManagerInternal,
+ DevicePolicyManagerService dpms) {
mContext = context;
+ mUserManagerInternal = userManagerInternal;
+ mDpms = dpms;
}
+ @Override
public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
Bundle prevRestrictions) {
final boolean newlyDisallowed =
@@ -925,13 +934,19 @@
final boolean restrictionChanged = (newlyDisallowed != previouslyDisallowed);
if (restrictionChanged) {
- // Notify ManagedProvisioning to update the built-in cross profile intent filters.
- Intent intent = new Intent(
- DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED);
- intent.setPackage(getManagedProvisioningPackage(mContext));
- intent.putExtra(Intent.EXTRA_USER_ID, userId);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+ final int parentId = mUserManagerInternal.getProfileParentId(userId);
+ if (parentId == userId) {
+ return;
+ }
+
+ // Always reset filters on the parent user, which handles cross profile intent
+ // filters between the parent and its profiles.
+ Slog.i(LOG_TAG, "Resetting cross-profile intent filters on restriction "
+ + "change");
+ mDpms.resetDefaultCrossProfileIntentFilters(parentId);
+ mContext.sendBroadcastAsUser(new Intent(
+ DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED),
+ UserHandle.of(userId));
}
}
}
@@ -1116,6 +1131,22 @@
mSafetyChecker = new OneTimeSafetyChecker(this, operation, reason);
}
+ // Used by DevicePolicyManagerServiceShellCommand
+ List<OwnerDto> listAllOwners() {
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
+
+ List<OwnerDto> owners = mOwners.listAllOwners();
+ synchronized (getLockObject()) {
+ for (int i = 0; i < owners.size(); i++) {
+ OwnerDto owner = owners.get(i);
+ owner.isAffiliated = isUserAffiliatedWithDeviceLocked(owner.userId);
+ }
+ }
+
+ return owners;
+ }
+
/**
* Unit test will subclass it to inject mocks.
*/
@@ -1621,7 +1652,8 @@
mSetupContentObserver = new SetupContentObserver(mHandler);
- mUserManagerInternal.addUserRestrictionsListener(new RestrictionsListener(mContext));
+ mUserManagerInternal.addUserRestrictionsListener(
+ new RestrictionsListener(mContext, mUserManagerInternal, this));
mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
loadOwners();
@@ -2959,6 +2991,7 @@
updatePasswordQualityCacheForUserGroup(
userId == UserHandle.USER_SYSTEM ? UserHandle.USER_ALL : userId);
updatePermissionPolicyCache(userId);
+ updateAdminCanGrantSensorsPermissionCache(userId);
startOwnerService(userId, "start-user");
}
@@ -13451,15 +13484,15 @@
if (!mOwners.hasDeviceOwner()) {
return false;
}
- if (userId == mOwners.getDeviceOwnerUserId()) {
- // The user that the DO is installed on is always affiliated with the device.
- return true;
- }
if (userId == UserHandle.USER_SYSTEM) {
// The system user is always affiliated in a DO device,
// even if in headless system user mode.
return true;
}
+ if (userId == mOwners.getDeviceOwnerUserId()) {
+ // The user that the DO is installed on is always affiliated with the device.
+ return true;
+ }
final ComponentName profileOwner = getProfileOwnerAsUser(userId);
if (profileOwner == null) {
@@ -16008,19 +16041,9 @@
provisioningParams.isKeepAccountMigrated(), callerPackage);
if (provisioningParams.isOrganizationOwnedProvisioning()) {
- markIsProfileOwnerOnOrganizationOwnedDevice(admin, userInfo.id);
- restrictRemovalOfManagedProfile(admin, userInfo.id);
+ setProfileOwnerOnOrgOwnedDeviceState(admin, userInfo.id, caller.getUserId());
}
- final Intent intent = new Intent(DevicePolicyManager.ACTION_MANAGED_PROFILE_CREATED)
- .putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id)
- .putExtra(
- DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
- provisioningParams.isLeaveAllSystemAppsEnabled())
- .setPackage(getManagedProvisioningPackage(mContext))
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
-
return userInfo.getUserHandle();
} catch (Exception e) {
DevicePolicyEventLogger
@@ -16250,21 +16273,20 @@
}
}
- private void markIsProfileOwnerOnOrganizationOwnedDevice(
- ComponentName admin, @UserIdInt int profileId) {
- getDpmForProfile(profileId).markProfileOwnerOnOrganizationOwnedDevice(admin);
+ private void setProfileOwnerOnOrgOwnedDeviceState(
+ ComponentName admin, @UserIdInt int profileId, @UserIdInt int parentUserId) {
+ synchronized (getLockObject()) {
+ markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(admin, profileId);
+ }
+ restrictRemovalOfManagedProfile(parentUserId);
}
- private void restrictRemovalOfManagedProfile(
- ComponentName admin, @UserIdInt int profileId) {
- getDpmForProfile(profileId).addUserRestriction(
- admin, UserManager.DISALLOW_REMOVE_MANAGED_PROFILE);
- }
-
- private DevicePolicyManager getDpmForProfile(@UserIdInt int profileId) {
- final Context profileContext = mContext.createContextAsUser(
- UserHandle.of(profileId), /* flags= */ 0);
- return profileContext.getSystemService(DevicePolicyManager.class);
+ private void restrictRemovalOfManagedProfile(@UserIdInt int parentUserId) {
+ final UserHandle parentUserHandle = UserHandle.of(parentUserId);
+ mUserManager.setUserRestriction(
+ UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+ /* value= */ true,
+ parentUserHandle);
}
@Override
@@ -16313,15 +16335,8 @@
}
disallowAddUser();
-
- final Intent intent = new Intent(DevicePolicyManager.ACTION_PROVISIONED_MANAGED_DEVICE)
- .putExtra(Intent.EXTRA_USER_HANDLE, caller.getUserId())
- .putExtra(
- DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
- provisioningParams.isLeaveAllSystemAppsEnabled())
- .setPackage(getManagedProvisioningPackage(mContext))
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+ setAdminCanGrantSensorsPermissionForUserUnchecked(caller.getUserId(),
+ provisioningParams.canDeviceOwnerGrantSensorsPermissions());
} catch (Exception e) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_ERROR)
@@ -16420,4 +16435,76 @@
.setStrings(callerPackage)
.write();
}
+
+ @Override
+ public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {
+ Preconditions.checkCallAuthorization(
+ hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ try {
+ final List<UserInfo> profiles = mUserManager.getProfiles(userId);
+ final int numOfProfiles = profiles.size();
+ if (numOfProfiles <= 1) {
+ return;
+ }
+
+ final String managedProvisioningPackageName = getManagedProvisioningPackage(
+ mContext);
+ // Removes cross profile intent filters from the parent to all the profiles.
+ mIPackageManager.clearCrossProfileIntentFilters(
+ userId, mContext.getOpPackageName());
+ // Setting and resetting default cross profile intent filters was previously handled
+ // by Managed Provisioning. For backwards compatibility, clear any intent filters
+ // that were set by ManagedProvisioning.
+ mIPackageManager.clearCrossProfileIntentFilters(
+ userId, managedProvisioningPackageName);
+
+ // For each profile reset cross profile intent filters
+ for (int i = 0; i < numOfProfiles; i++) {
+ UserInfo profile = profiles.get(i);
+ mIPackageManager.clearCrossProfileIntentFilters(
+ profile.id, mContext.getOpPackageName());
+ // Clear any intent filters that were set by ManagedProvisioning.
+ mIPackageManager.clearCrossProfileIntentFilters(
+ profile.id, managedProvisioningPackageName);
+
+ mUserManagerInternal.setDefaultCrossProfileIntentFilters(userId, profile.id);
+ }
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ }
+ });
+ }
+
+ private void setAdminCanGrantSensorsPermissionForUserUnchecked(int userId, boolean canGrant) {
+ synchronized (getLockObject()) {
+ ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
+
+ Preconditions.checkState(
+ isDeviceOwner(owner) && owner.getUserHandle().getIdentifier() == userId,
+ "May only be set on a the user of a device owner.");
+
+ owner.mAdminCanGrantSensorsPermissions = canGrant;
+ mPolicyCache.setAdminCanGrantSensorsPermissions(userId, canGrant);
+ saveSettingsLocked(userId);
+ }
+ }
+
+ private void updateAdminCanGrantSensorsPermissionCache(int userId) {
+ synchronized (getLockObject()) {
+ ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userId);
+ final boolean canGrant = owner != null ? owner.mAdminCanGrantSensorsPermissions : false;
+ mPolicyCache.setAdminCanGrantSensorsPermissions(userId, canGrant);
+ }
+ }
+
+ @Override
+ public boolean canAdminGrantSensorsPermissionsForUser(int userId) {
+ if (!mHasFeature) {
+ return false;
+ }
+
+ return mPolicyCache.canAdminGrantSensorsPermissionsForUser(userId);
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index fc1d831..222c987 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -18,13 +18,17 @@
import android.app.admin.DevicePolicyManager;
import android.os.ShellCommand;
+import com.android.server.devicepolicy.Owners.OwnerDto;
+
import java.io.PrintWriter;
+import java.util.List;
import java.util.Objects;
final class DevicePolicyManagerServiceShellCommand extends ShellCommand {
private static final String CMD_IS_SAFE_OPERATION = "is-operation-safe";
private static final String CMD_SET_SAFE_OPERATION = "set-operation-safe";
+ private static final String CMD_LIST_OWNERS = "list-owners";
private final DevicePolicyManagerService mService;
@@ -51,6 +55,8 @@
return runIsSafeOperation(pw);
case CMD_SET_SAFE_OPERATION:
return runSetSafeOperation(pw);
+ case CMD_LIST_OWNERS:
+ return runListOwners(pw);
default:
return onInvalidCommand(pw, cmd);
}
@@ -76,6 +82,8 @@
pw.printf(" %s <OPERATION_ID> <REASON_ID>\n", CMD_SET_SAFE_OPERATION);
pw.printf(" Emulates the result of the next call to check if the given operation is safe"
+ " \n\n");
+ pw.printf(" %s\n", CMD_LIST_OWNERS);
+ pw.printf(" Lists the device / profile owners per user \n\n");
}
private int runIsSafeOperation(PrintWriter pw) {
@@ -97,4 +105,36 @@
DevicePolicyManager.unsafeOperationReasonToString(reason));
return 0;
}
+
+ private int runListOwners(PrintWriter pw) {
+ List<OwnerDto> owners = mService.listAllOwners();
+ if (owners.isEmpty()) {
+ pw.println("none");
+ return 0;
+ }
+ int size = owners.size();
+ if (size == 1) {
+ pw.println("1 owner:");
+ } else {
+ pw.printf("%d owners:\n", size);
+ }
+
+ for (int i = 0; i < size; i++) {
+ OwnerDto owner = owners.get(i);
+ pw.printf("User %2d: admin=%s", owner.userId, owner.admin.flattenToShortString());
+ if (owner.isDeviceOwner) {
+ pw.print(",DeviceOwner");
+ }
+ if (owner.isProfileOwner) {
+ pw.print(",ProfileOwner");
+ }
+ if (owner.isAffiliated) {
+ pw.print(",Affiliated");
+ }
+ pw.println();
+ }
+
+ return 0;
+ }
+
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 809afe0..1e70d59 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -17,6 +17,7 @@
package com.android.server.devicepolicy;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManagerInternal;
import android.app.admin.SystemUpdateInfo;
@@ -57,6 +58,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDate;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -433,6 +435,23 @@
}
}
+ List<OwnerDto> listAllOwners() {
+ List<OwnerDto> owners = new ArrayList<>();
+ synchronized (mLock) {
+ if (mDeviceOwner != null) {
+ owners.add(new OwnerDto(mDeviceOwnerUserId, mDeviceOwner.admin,
+ /* isDeviceOwner= */ true));
+ }
+ for (int i = 0; i < mProfileOwners.size(); i++) {
+ int userId = mProfileOwners.keyAt(i);
+ OwnerInfo info = mProfileOwners.valueAt(i);
+ owners.add(new OwnerDto(userId, info.admin, /* isDeviceOwner= */ false));
+ }
+ }
+ return owners;
+ }
+
+
SystemUpdatePolicy getSystemUpdatePolicy() {
synchronized (mLock) {
return mSystemUpdatePolicy;
@@ -1076,6 +1095,24 @@
}
}
+ /**
+ * Data-transfer object used by {@link DevicePolicyManagerServiceShellCommand}.
+ */
+ static final class OwnerDto {
+ public final @UserIdInt int userId;
+ public final ComponentName admin;
+ public final boolean isDeviceOwner;
+ public final boolean isProfileOwner;
+ public boolean isAffiliated;
+
+ private OwnerDto(@UserIdInt int userId, ComponentName admin, boolean isDeviceOwner) {
+ this.userId = userId;
+ this.admin = Objects.requireNonNull(admin, "admin must not be null");
+ this.isDeviceOwner = isDeviceOwner;
+ this.isProfileOwner = !isDeviceOwner;
+ }
+ }
+
public void dump(IndentingPrintWriter pw) {
boolean needBlank = false;
if (mDeviceOwner != null) {
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index e978ed4..7534c7c 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -51,9 +51,9 @@
static_libs: [
"libbase",
"libext2_uuid",
- "libdataloader_aidl-unstable-cpp",
- "libincremental_aidl-unstable-cpp",
- "libincremental_manager_aidl-unstable-cpp",
+ "libdataloader_aidl-cpp",
+ "libincremental_aidl-cpp",
+ "libincremental_manager_aidl-cpp",
"libprotobuf-cpp-lite",
"service.incremental.proto",
"libutils",
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 50cb00f..98c3b99 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -147,6 +147,7 @@
import com.android.server.om.OverlayManagerService;
import com.android.server.os.BugreportManagerService;
import com.android.server.os.DeviceIdentifiersPolicyService;
+import com.android.server.os.NativeTombstoneManagerService;
import com.android.server.os.SchedulingPolicyService;
import com.android.server.people.PeopleService;
import com.android.server.pm.BackgroundDexOptService;
@@ -185,6 +186,7 @@
import com.android.server.testharness.TestHarnessModeService;
import com.android.server.textclassifier.TextClassificationManagerService;
import com.android.server.textservices.TextServicesManagerService;
+import com.android.server.tracing.TracingServiceProxy;
import com.android.server.trust.TrustManagerService;
import com.android.server.tv.TvInputManagerService;
import com.android.server.tv.TvRemoteService;
@@ -207,12 +209,15 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Timer;
+import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
@@ -481,6 +486,50 @@
private static native void fdtrackAbort();
+ private static final File HEAP_DUMP_PATH = new File("/data/system/heapdump/");
+ private static final int MAX_HEAP_DUMPS = 2;
+
+ /**
+ * Dump system_server's heap.
+ *
+ * For privacy reasons, these aren't automatically pulled into bugreports:
+ * they must be manually pulled by the user.
+ */
+ private static void dumpHprof() {
+ // hprof dumps are rather large, so ensure we don't fill the disk by generating
+ // hundreds of these that will live forever.
+ TreeSet<File> existingTombstones = new TreeSet<>();
+ for (File file : HEAP_DUMP_PATH.listFiles()) {
+ if (!file.isFile()) {
+ continue;
+ }
+ if (!file.getName().startsWith("fdtrack-")) {
+ continue;
+ }
+ existingTombstones.add(file);
+ }
+ if (existingTombstones.size() >= MAX_HEAP_DUMPS) {
+ for (int i = 0; i < MAX_HEAP_DUMPS - 1; ++i) {
+ // Leave the newest `MAX_HEAP_DUMPS - 1` tombstones in place.
+ existingTombstones.pollLast();
+ }
+ for (File file : existingTombstones) {
+ if (!file.delete()) {
+ Slog.w("System", "Failed to clean up hprof " + file);
+ }
+ }
+ }
+
+ try {
+ String date = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
+ String filename = "/data/system/heapdump/fdtrack-" + date + ".hprof";
+ Debug.dumpHprofData(filename);
+ } catch (IOException ex) {
+ Slog.e("System", "Failed to dump fdtrack hprof");
+ ex.printStackTrace();
+ }
+ }
+
/**
* Spawn a thread that monitors for fd leaks.
*/
@@ -505,6 +554,7 @@
enabled = true;
} else if (maxFd > abortThreshold) {
Slog.i("System", "fdtrack abort threshold reached, dumping and aborting");
+ dumpHprof();
fdtrackAbort();
}
@@ -1217,6 +1267,11 @@
mSystemServiceManager.startService(ROLLBACK_MANAGER_SERVICE_CLASS);
t.traceEnd();
+ // Tracks native tombstones.
+ t.traceBegin("StartNativeTombstoneManagerService");
+ mSystemServiceManager.startService(NativeTombstoneManagerService.class);
+ t.traceEnd();
+
// Service to capture bugreports.
t.traceBegin("StartBugreportManagerService");
mSystemServiceManager.startService(BugreportManagerService.class);
@@ -2429,6 +2484,11 @@
mSystemServiceManager.startService(AppBindingService.Lifecycle.class);
t.traceEnd();
+ // Perfetto TracingServiceProxy
+ t.traceBegin("startTracingServiceProxy");
+ mSystemServiceManager.startService(TracingServiceProxy.class);
+ t.traceEnd();
+
// It is now time to start up the app processes...
t.traceBegin("MakeVibratorServiceReady");
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 d863194..c2e0b77 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 @@
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.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.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 @@
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 visiblePkg = mockPkg(VISIBLE_PKG)
+ val visiblePkgSetting = mockPkgSetting(VISIBLE_PKG, VISIBLE_UUID)
+ val invisiblePkg = mockPkg(INVISIBLE_PKG)
+ val invisiblePkgSetting = mockPkgSetting(INVISIBLE_PKG, INVISIBLE_UUID)
+
val makeEnforcer: (Context) -> DomainVerificationEnforcer = {
- DomainVerificationEnforcer(it)
- }
-
- 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)
- }
- )
+ 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 @@
{
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") { _, _, userId ->
- setDomainVerificationUserSelection(uuid, setOf("example.com"), true, userId)
+ service(Type.SELECTOR_USER, "setUserSelectionUserId") {
+ setDomainVerificationUserSelection(
+ it.targetDomainSetId,
+ setOf("example.com"),
+ true,
+ it.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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()
- }
- }
- whenever(
- enforcePermission(
- eq(android.Manifest.permission.INTERACT_ACROSS_USERS),
- anyInt(), anyInt(), anyString()
- )
- ) {
- if (!allowInteractAcrossUsers.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 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) }
+ 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)
+ }
+ }
+ } else {
+ allUids.forEach {
+ runMethod(target, it, visible = true, 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)
- runEachTestCaseWrapped(callingUserId, callingUserId)
-
+ runTestCases(callingUserId, callingUserId, throws = false)
if (verifyCrossUser) {
- runEachTestCaseWrapped(callingUserId, notCallingUserId) {
- assertThrows(SecurityException::class.java, it)
- }
- }
-
- allowUserSelection.set(false)
-
- runEachTestCaseWrapped(callingUserId, callingUserId) {
- assertThrows(SecurityException::class.java, it)
- }
- if (verifyCrossUser) {
- runEachTestCaseWrapped(callingUserId, notCallingUserId) {
- assertThrows(SecurityException::class.java, it)
- }
+ runTestCases(callingUserId, notCallingUserId, throws = true)
}
allowInteractAcrossUsers.set(true)
- runEachTestCaseWrapped(callingUserId, callingUserId) {
- assertThrows(SecurityException::class.java, it)
- }
+ 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)
+ }
+ }
+ }
+
+ val callingUserId = 0
+ val notCallingUserId = 1
+
+ runTestCases(callingUserId, callingUserId, throws = true)
+ runTestCases(callingUserId, notCallingUserId, throws = true)
+
+ allowPreferredApps.set(true)
+
+ 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
+ )
+ }
+ 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)
+ }
+
+ private fun Context.initPermission(boolean: AtomicBoolean, permission: String) {
+ whenever(enforcePermission(eq(permission), anyInt(), anyInt(), anyString())) {
+ if (!boolean.get()) {
+ throw SecurityException()
+ }
+ }
+ 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 @@
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
new file mode 100644
index 0000000..5629d1c
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -0,0 +1,274 @@
+/*
+ * 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.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
+import android.content.pm.parsing.component.ParsedIntentInfo
+import android.os.Build
+import android.os.Process
+import android.util.ArraySet
+import android.util.SparseArray
+import com.android.server.pm.PackageSetting
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
+import com.android.server.pm.verify.domain.DomainVerificationService
+import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy
+import com.android.server.pm.parsing.pkg.AndroidPackage
+import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.spyThrowOnUnmocked
+import com.android.server.testutils.whenever
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.Mockito
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyLong
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import java.io.File
+import java.util.UUID
+
+@RunWith(Parameterized::class)
+class DomainVerificationSettingsMutationTest {
+
+ companion object {
+ private const val TEST_PKG = "com.test"
+
+ // Pretend to be the system. This class doesn't verify any enforcement behavior.
+ private const val TEST_UID = Process.SYSTEM_UID
+ private const val TEST_USER_ID = 10
+ private val TEST_UUID = UUID.fromString("5168e42e-327e-432b-b562-cfb553518a70")
+
+ @JvmStatic
+ @Parameterized.Parameters(name = "{0}")
+ fun parameters(): Array<Any> {
+ val context: Context = mockThrowOnUnmocked {
+ whenever(
+ enforcePermission(
+ eq(android.Manifest.permission.DOMAIN_VERIFICATION_AGENT),
+ anyInt(), anyInt(), anyString()
+ )
+ )
+ whenever(
+ enforcePermission(
+ eq(android.Manifest.permission.UPDATE_DOMAIN_VERIFICATION_USER_SELECTION),
+ anyInt(), anyInt(), anyString()
+ )
+ )
+ whenever(
+ enforcePermission(
+ eq(android.Manifest.permission.INTERACT_ACROSS_USERS),
+ anyInt(), anyInt(), anyString()
+ )
+ )
+ whenever(
+ enforcePermission(
+ eq(android.Manifest.permission.SET_PREFERRED_APPLICATIONS),
+ anyInt(), anyInt(), anyString()
+ )
+ )
+ }
+ val proxy: DomainVerificationProxy = mockThrowOnUnmocked {
+ whenever(isCallerVerifier(anyInt())) { true }
+ whenever(sendBroadcastForPackages(any()))
+ }
+
+ val makeService: (DomainVerificationManagerInternal.Connection) -> DomainVerificationService =
+ { connection ->
+ DomainVerificationService(
+ context,
+ mockThrowOnUnmocked { whenever(linkedApps) { ArraySet<String>() } },
+ mockThrowOnUnmocked {
+ whenever(isChangeEnabled(anyLong(),any())) { true }
+ }).apply {
+ setConnection(connection)
+ }
+ }
+
+ fun service(name: String, block: DomainVerificationService.() -> Unit) =
+ Params(makeService, name) { service ->
+ service.proxy = proxy
+ service.addPackage(mockPkgSetting())
+ service.block()
+ }
+
+ return arrayOf(
+ service("clearPackage") {
+ clearPackage(TEST_PKG)
+ },
+ service("clearUser") {
+ clearUser(TEST_USER_ID)
+ },
+ service("clearState") {
+ clearDomainVerificationState(listOf(TEST_PKG))
+ },
+ service("clearUserSelections") {
+ clearUserSelections(listOf(TEST_PKG), TEST_USER_ID)
+ },
+ service("setStatus") {
+ setDomainVerificationStatus(
+ TEST_UUID,
+ setOf("example.com"),
+ DomainVerificationManager.STATE_SUCCESS
+ )
+ },
+ service("setStatusInternalPackageName") {
+ setDomainVerificationStatusInternal(
+ TEST_PKG,
+ DomainVerificationManager.STATE_SUCCESS,
+ ArraySet(setOf("example.com"))
+ )
+ },
+ service("setStatusInternalUid") {
+ setDomainVerificationStatusInternal(
+ TEST_UID,
+ TEST_UUID,
+ setOf("example.com"),
+ DomainVerificationManager.STATE_SUCCESS
+ )
+ },
+ service("setLinkHandlingAllowed") {
+ setDomainVerificationLinkHandlingAllowed(TEST_PKG, true)
+ },
+ service("setLinkHandlingAllowedUserId") {
+ setDomainVerificationLinkHandlingAllowed(TEST_PKG, true, TEST_USER_ID)
+ },
+ service("setLinkHandlingAllowedInternal") {
+ setDomainVerificationLinkHandlingAllowedInternal(TEST_PKG, true, TEST_USER_ID)
+ },
+ service("setUserSelection") {
+ setDomainVerificationUserSelection(TEST_UUID, setOf("example.com"), true)
+ },
+ service("setUserSelectionUserId") {
+ setDomainVerificationUserSelection(
+ TEST_UUID,
+ setOf("example.com"),
+ true,
+ TEST_USER_ID
+ )
+ },
+ service("setUserSelectionInternal") {
+ setDomainVerificationUserSelectionInternal(
+ TEST_USER_ID,
+ TEST_PKG,
+ true,
+ ArraySet(setOf("example.com")),
+ )
+ },
+ service("setLegacyUserState") {
+ setLegacyUserState(
+ TEST_PKG,
+ TEST_USER_ID,
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER
+ )
+ },
+ )
+ }
+
+ data class Params(
+ val construct: (
+ DomainVerificationManagerInternal.Connection
+ ) -> DomainVerificationService,
+ val name: String,
+ val method: (DomainVerificationService) -> Unit
+ ) {
+ override fun toString() = name
+ }
+
+
+ fun 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)
+ }
+ )
+ }
+ )
+ }
+ }
+
+ // TODO: PackageSetting field encapsulation to move to whenever(name)
+ fun mockPkgSetting() = spyThrowOnUnmocked(
+ PackageSetting(
+ TEST_PKG,
+ TEST_PKG,
+ File("/test"),
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 0,
+ 0,
+ 0,
+ null,
+ null,
+ null,
+ TEST_UUID
+ )
+ ) {
+ whenever(getPkg()) { mockPkg() }
+ whenever(domainSetId) { TEST_UUID }
+ whenever(userState) {
+ SparseArray<PackageUserState>().apply {
+ this[0] = PackageUserState()
+ }
+ }
+ }
+ }
+
+ @Parameterized.Parameter(0)
+ lateinit var params: Params
+
+ @Test
+ fun writeScheduled() {
+ val connection = mockConnection()
+ val service = params.construct(connection)
+ params.method(service)
+
+ verify(connection).scheduleWriteSettings()
+ }
+
+ private fun mockConnection(): DomainVerificationManagerInternal.Connection =
+ mockThrowOnUnmocked {
+ whenever(callingUid) { TEST_UID }
+ whenever(callingUserId) { TEST_USER_ID }
+ whenever(getPackageSettingLocked(TEST_PKG)) { mockPkgSetting() }
+ 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 89abe3c..b541512 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 7c935d5..e7d56a0 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 @@
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 0000000..928065a
--- /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 0000000..147cc47
--- /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 d860326..56d30cc 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 @@
@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/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index af4130d..ca53492 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -200,12 +200,12 @@
}
private static class DisplayModeWrapper {
- public SurfaceControl.DisplayConfig config;
+ public SurfaceControl.DisplayMode mode;
public float[] expectedAlternativeRefreshRates;
- DisplayModeWrapper(SurfaceControl.DisplayConfig config,
+ DisplayModeWrapper(SurfaceControl.DisplayMode mode,
float[] expectedAlternativeRefreshRates) {
- this.config = config;
+ this.mode = mode;
this.expectedAlternativeRefreshRates = expectedAlternativeRefreshRates;
}
}
@@ -215,14 +215,15 @@
* <code>expectedAlternativeRefreshRates</code> are present for each of the
* <code>modes</code>.
*/
- private void testAlternativeRefreshRatesCommon(FakeDisplay display, DisplayModeWrapper[] modes)
+ private void testAlternativeRefreshRatesCommon(FakeDisplay display,
+ DisplayModeWrapper[] wrappedModes)
throws InterruptedException {
// Update the display.
- SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[modes.length];
- for (int i = 0; i < modes.length; i++) {
- configs[i] = modes[i].config;
+ SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[wrappedModes.length];
+ for (int i = 0; i < wrappedModes.length; i++) {
+ modes[i] = wrappedModes[i].mode;
}
- display.configs = configs;
+ display.modes = modes;
setUpDisplay(display);
mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
@@ -233,11 +234,11 @@
mListener.changedDisplays.get(mListener.changedDisplays.size() - 1);
displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
Display.Mode[] supportedModes = displayDevice.getDisplayDeviceInfoLocked().supportedModes;
- assertThat(supportedModes.length).isEqualTo(configs.length);
+ assertThat(supportedModes.length).isEqualTo(modes.length);
- for (int i = 0; i < modes.length; i++) {
- assertModeIsSupported(supportedModes, configs[i],
- modes[i].expectedAlternativeRefreshRates);
+ for (int i = 0; i < wrappedModes.length; i++) {
+ assertModeIsSupported(supportedModes, modes[i],
+ wrappedModes[i].expectedAlternativeRefreshRates);
}
}
@@ -251,56 +252,56 @@
testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] {
new DisplayModeWrapper(
- createFakeDisplayConfig(1920, 1080, 60f, 0), new float[]{24f, 50f}),
+ createFakeDisplayMode(1920, 1080, 60f, 0), new float[]{24f, 50f}),
new DisplayModeWrapper(
- createFakeDisplayConfig(1920, 1080, 50f, 0), new float[]{24f, 60f}),
+ createFakeDisplayMode(1920, 1080, 50f, 0), new float[]{24f, 60f}),
new DisplayModeWrapper(
- createFakeDisplayConfig(1920, 1080, 24f, 0), new float[]{50f, 60f}),
+ createFakeDisplayMode(1920, 1080, 24f, 0), new float[]{50f, 60f}),
new DisplayModeWrapper(
- createFakeDisplayConfig(3840, 2160, 60f, 0), new float[]{24f, 50f}),
+ createFakeDisplayMode(3840, 2160, 60f, 0), new float[]{24f, 50f}),
new DisplayModeWrapper(
- createFakeDisplayConfig(3840, 2160, 50f, 0), new float[]{24f, 60f}),
+ createFakeDisplayMode(3840, 2160, 50f, 0), new float[]{24f, 60f}),
new DisplayModeWrapper(
- createFakeDisplayConfig(3840, 2160, 24f, 0), new float[]{50f, 60f}),
+ createFakeDisplayMode(3840, 2160, 24f, 0), new float[]{50f, 60f}),
});
testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] {
new DisplayModeWrapper(
- createFakeDisplayConfig(1920, 1080, 60f, 0), new float[]{50f}),
+ createFakeDisplayMode(1920, 1080, 60f, 0), new float[]{50f}),
new DisplayModeWrapper(
- createFakeDisplayConfig(1920, 1080, 50f, 0), new float[]{60f}),
+ createFakeDisplayMode(1920, 1080, 50f, 0), new float[]{60f}),
new DisplayModeWrapper(
- createFakeDisplayConfig(1920, 1080, 24f, 1), new float[0]),
+ createFakeDisplayMode(1920, 1080, 24f, 1), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayConfig(3840, 2160, 60f, 2), new float[0]),
+ createFakeDisplayMode(3840, 2160, 60f, 2), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayConfig(3840, 2160, 50f, 3), new float[]{24f}),
+ createFakeDisplayMode(3840, 2160, 50f, 3), new float[]{24f}),
new DisplayModeWrapper(
- createFakeDisplayConfig(3840, 2160, 24f, 3), new float[]{50f}),
+ createFakeDisplayMode(3840, 2160, 24f, 3), new float[]{50f}),
});
testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] {
new DisplayModeWrapper(
- createFakeDisplayConfig(1920, 1080, 60f, 0), new float[0]),
+ createFakeDisplayMode(1920, 1080, 60f, 0), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayConfig(1920, 1080, 50f, 1), new float[0]),
+ createFakeDisplayMode(1920, 1080, 50f, 1), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayConfig(1920, 1080, 24f, 2), new float[0]),
+ createFakeDisplayMode(1920, 1080, 24f, 2), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayConfig(3840, 2160, 60f, 3), new float[0]),
+ createFakeDisplayMode(3840, 2160, 60f, 3), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayConfig(3840, 2160, 50f, 4), new float[0]),
+ createFakeDisplayMode(3840, 2160, 50f, 4), new float[0]),
new DisplayModeWrapper(
- createFakeDisplayConfig(3840, 2160, 24f, 5), new float[0]),
+ createFakeDisplayMode(3840, 2160, 24f, 5), new float[0]),
});
}
@Test
public void testAfterDisplayChange_DisplayModesAreUpdated() throws Exception {
- SurfaceControl.DisplayConfig displayConfig = createFakeDisplayConfig(1920, 1080, 60f);
- SurfaceControl.DisplayConfig[] configs =
- new SurfaceControl.DisplayConfig[]{displayConfig};
- FakeDisplay display = new FakeDisplay(PORT_A, configs, 0);
+ SurfaceControl.DisplayMode displayMode = createFakeDisplayMode(1920, 1080, 60f);
+ SurfaceControl.DisplayMode[] modes =
+ new SurfaceControl.DisplayMode[]{displayMode};
+ FakeDisplay display = new FakeDisplay(PORT_A, modes, 0);
setUpDisplay(display);
updateAvailableDisplays();
mAdapter.registerLocked();
@@ -312,29 +313,29 @@
DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(
0).getDisplayDeviceInfoLocked();
- assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(configs.length);
- assertModeIsSupported(displayDeviceInfo.supportedModes, displayConfig);
+ assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length);
+ assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode);
Display.Mode defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
- assertThat(defaultMode.matches(displayConfig.width, displayConfig.height,
- displayConfig.refreshRate)).isTrue();
+ assertThat(defaultMode.matches(displayMode.width, displayMode.height,
+ displayMode.refreshRate)).isTrue();
Display.Mode activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId);
- assertThat(activeMode.matches(displayConfig.width, displayConfig.height,
- displayConfig.refreshRate)).isTrue();
+ assertThat(activeMode.matches(displayMode.width, displayMode.height,
+ displayMode.refreshRate)).isTrue();
// Change the display
- SurfaceControl.DisplayConfig addedDisplayInfo = createFakeDisplayConfig(3840, 2160,
+ SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(3840, 2160,
60f);
- configs = new SurfaceControl.DisplayConfig[]{displayConfig, addedDisplayInfo};
- display.configs = configs;
- display.activeConfig = 1;
+ modes = new SurfaceControl.DisplayMode[]{displayMode, addedDisplayInfo};
+ display.modes = modes;
+ display.activeMode = 1;
setUpDisplay(display);
mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
- assertThat(SurfaceControl.getActiveConfig(display.token)).isEqualTo(1);
- assertThat(SurfaceControl.getDisplayConfigs(display.token).length).isEqualTo(2);
+ assertThat(SurfaceControl.getActiveDisplayMode(display.token)).isEqualTo(1);
+ assertThat(SurfaceControl.getDisplayModes(display.token).length).isEqualTo(2);
assertThat(mListener.addedDisplays.size()).isEqualTo(1);
assertThat(mListener.changedDisplays.size()).isEqualTo(1);
@@ -343,8 +344,8 @@
displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();
- assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(configs.length);
- assertModeIsSupported(displayDeviceInfo.supportedModes, displayConfig);
+ assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(modes.length);
+ assertModeIsSupported(displayDeviceInfo.supportedModes, displayMode);
assertModeIsSupported(displayDeviceInfo.supportedModes, addedDisplayInfo);
activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId);
@@ -358,11 +359,11 @@
@Test
public void testAfterDisplayChange_ActiveModeIsUpdated() throws Exception {
- SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[]{
- createFakeDisplayConfig(1920, 1080, 60f),
- createFakeDisplayConfig(1920, 1080, 50f)
+ SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{
+ createFakeDisplayMode(1920, 1080, 60f),
+ createFakeDisplayMode(1920, 1080, 50f)
};
- FakeDisplay display = new FakeDisplay(PORT_A, configs, /* activeConfig */ 0);
+ FakeDisplay display = new FakeDisplay(PORT_A, modes, /* activeMode */ 0);
setUpDisplay(display);
updateAvailableDisplays();
mAdapter.registerLocked();
@@ -378,12 +379,12 @@
assertThat(activeMode.matches(1920, 1080, 60f)).isTrue();
// Change the display
- display.activeConfig = 1;
+ display.activeMode = 1;
setUpDisplay(display);
mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
- assertThat(SurfaceControl.getActiveConfig(display.token)).isEqualTo(1);
+ assertThat(SurfaceControl.getActiveDisplayMode(display.token)).isEqualTo(1);
assertThat(mListener.addedDisplays.size()).isEqualTo(1);
assertThat(mListener.changedDisplays.size()).isEqualTo(1);
@@ -471,14 +472,14 @@
}
@Test
- public void testDisplayChange_withStaleDesiredDisplayConfigSpecs() throws Exception {
- SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[]{
- createFakeDisplayConfig(1920, 1080, 60f),
- createFakeDisplayConfig(1920, 1080, 50f)
+ public void testDisplayChange_withStaleDesiredDisplayModeSpecs() throws Exception {
+ SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{
+ createFakeDisplayMode(1920, 1080, 60f),
+ createFakeDisplayMode(1920, 1080, 50f)
};
- final int activeConfig = 0;
- FakeDisplay display = new FakeDisplay(PORT_A, configs, activeConfig);
- display.desiredDisplayConfigSpecs.defaultConfig = 1;
+ final int activeMode = 0;
+ FakeDisplay display = new FakeDisplay(PORT_A, modes, activeMode);
+ display.desiredDisplayModeSpecs.defaultMode = 1;
setUpDisplay(display);
updateAvailableDisplays();
@@ -486,12 +487,12 @@
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
// Change the display
- display.configs = new SurfaceControl.DisplayConfig[]{
- createFakeDisplayConfig(1920, 1080, 60f)
+ display.modes = new SurfaceControl.DisplayMode[]{
+ createFakeDisplayMode(1920, 1080, 60f)
};
- // SurfaceFlinger can return a stale defaultConfig. Make sure this doesn't
+ // SurfaceFlinger can return a stale defaultMode. Make sure this doesn't
// trigger ArrayOutOfBoundsException.
- display.desiredDisplayConfigSpecs.defaultConfig = 1;
+ display.desiredDisplayModeSpecs.defaultMode = 1;
setUpDisplay(display);
updateAvailableDisplays();
@@ -562,13 +563,13 @@
}
private void assertModeIsSupported(Display.Mode[] supportedModes,
- SurfaceControl.DisplayConfig mode) {
+ SurfaceControl.DisplayMode mode) {
assertThat(Arrays.stream(supportedModes).anyMatch(
x -> x.matches(mode.width, mode.height, mode.refreshRate))).isTrue();
}
private void assertModeIsSupported(Display.Mode[] supportedModes,
- SurfaceControl.DisplayConfig mode, float[] alternativeRefreshRates) {
+ SurfaceControl.DisplayMode mode, float[] alternativeRefreshRates) {
float[] sortedAlternativeRates =
Arrays.copyOf(alternativeRefreshRates, alternativeRefreshRates.length);
Arrays.sort(sortedAlternativeRates);
@@ -588,13 +589,13 @@
public final DisplayAddress.Physical address;
public final IBinder token = new Binder();
public final SurfaceControl.DisplayInfo info;
- public SurfaceControl.DisplayConfig[] configs;
- public int activeConfig;
+ public SurfaceControl.DisplayMode[] modes;
+ public int activeMode;
public int[] colorModes = new int[]{ Display.COLOR_MODE_DEFAULT };
public Display.HdrCapabilities hdrCapabilities = new Display.HdrCapabilities(new int[0],
1000, 1000, 0);
- public SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs =
- new SurfaceControl.DesiredDisplayConfigSpecs(/* defaultConfig */ 0,
+ public SurfaceControl.DesiredDisplayModeSpecs desiredDisplayModeSpecs =
+ new SurfaceControl.DesiredDisplayModeSpecs(/* defaultMode */ 0,
/* allowGroupSwitching */ false,
/* primaryRefreshRateMin */ 60.f,
/* primaryRefreshRateMax */ 60.f,
@@ -604,17 +605,17 @@
private FakeDisplay(int port) {
this.address = createDisplayAddress(port);
this.info = createFakeDisplayInfo();
- this.configs = new SurfaceControl.DisplayConfig[]{
- createFakeDisplayConfig(800, 600, 60f)
+ this.modes = new SurfaceControl.DisplayMode[]{
+ createFakeDisplayMode(800, 600, 60f)
};
- this.activeConfig = 0;
+ this.activeMode = 0;
}
- private FakeDisplay(int port, SurfaceControl.DisplayConfig[] configs, int activeConfig) {
+ private FakeDisplay(int port, SurfaceControl.DisplayMode[] modes, int activeMode) {
this.address = createDisplayAddress(port);
this.info = createFakeDisplayInfo();
- this.configs = configs;
- this.activeConfig = activeConfig;
+ this.modes = modes;
+ this.activeMode = activeMode;
}
}
@@ -623,16 +624,16 @@
doReturn(display.token).when(() ->
SurfaceControl.getPhysicalDisplayToken(display.address.getPhysicalDisplayId()));
doReturn(display.info).when(() -> SurfaceControl.getDisplayInfo(display.token));
- doReturn(display.configs).when(
- () -> SurfaceControl.getDisplayConfigs(display.token));
- doReturn(display.activeConfig).when(() -> SurfaceControl.getActiveConfig(display.token));
+ doReturn(display.modes).when(
+ () -> SurfaceControl.getDisplayModes(display.token));
+ doReturn(display.activeMode).when(() -> SurfaceControl.getActiveDisplayMode(display.token));
doReturn(0).when(() -> SurfaceControl.getActiveColorMode(display.token));
doReturn(display.colorModes).when(
() -> SurfaceControl.getDisplayColorModes(display.token));
doReturn(display.hdrCapabilities).when(
() -> SurfaceControl.getHdrCapabilities(display.token));
- doReturn(display.desiredDisplayConfigSpecs)
- .when(() -> SurfaceControl.getDesiredDisplayConfigSpecs(display.token));
+ doReturn(display.desiredDisplayModeSpecs)
+ .when(() -> SurfaceControl.getDesiredDisplayModeSpecs(display.token));
}
private void updateAvailableDisplays() {
@@ -655,21 +656,21 @@
return info;
}
- private static SurfaceControl.DisplayConfig createFakeDisplayConfig(int width, int height,
+ private static SurfaceControl.DisplayMode createFakeDisplayMode(int width, int height,
float refreshRate) {
- return createFakeDisplayConfig(width, height, refreshRate, 0);
+ return createFakeDisplayMode(width, height, refreshRate, 0);
}
- private static SurfaceControl.DisplayConfig createFakeDisplayConfig(int width, int height,
- float refreshRate, int configGroup) {
- final SurfaceControl.DisplayConfig config = new SurfaceControl.DisplayConfig();
- config.width = width;
- config.height = height;
- config.refreshRate = refreshRate;
- config.xDpi = 100;
- config.yDpi = 100;
- config.configGroup = configGroup;
- return config;
+ private static SurfaceControl.DisplayMode createFakeDisplayMode(int width, int height,
+ float refreshRate, int group) {
+ final SurfaceControl.DisplayMode mode = new SurfaceControl.DisplayMode();
+ mode.width = width;
+ mode.height = height;
+ mode.refreshRate = refreshRate;
+ mode.xDpi = 100;
+ mode.yDpi = 100;
+ mode.group = group;
+ return mode;
}
private static void waitForHandlerToComplete(Handler handler, long waitTimeMs)
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
new file mode 100644
index 0000000..35ac897
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobConcurrencyManagerTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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.job;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManagerInternal;
+import android.app.job.JobInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.os.UserHandle;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.R;
+import com.android.server.LocalServices;
+import com.android.server.job.JobConcurrencyManager.GracePeriodObserver;
+import com.android.server.job.controllers.JobStatus;
+import com.android.server.pm.UserManagerInternal;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class JobConcurrencyManagerTest {
+ private static final int UNAVAILABLE_USER = 0;
+ private JobConcurrencyManager mJobConcurrencyManager;
+ private UserManagerInternal mUserManagerInternal;
+ private ActivityManagerInternal mActivityManagerInternal;
+ private int mNextUserId;
+ private GracePeriodObserver mGracePeriodObserver;
+ private Context mContext;
+ private Resources mResources;
+
+ @BeforeClass
+ public static void setUpOnce() {
+ LocalServices.addService(UserManagerInternal.class, mock(UserManagerInternal.class));
+ LocalServices.addService(
+ ActivityManagerInternal.class, mock(ActivityManagerInternal.class));
+ }
+
+ @AfterClass
+ public static void tearDownOnce() {
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ }
+
+ @Before
+ public void setUp() {
+ final JobSchedulerService jobSchedulerService = mock(JobSchedulerService.class);
+ mContext = mock(Context.class);
+ mResources = mock(Resources.class);
+ doReturn(true).when(mResources).getBoolean(
+ R.bool.config_jobSchedulerRestrictBackgroundUser);
+ when(mContext.getResources()).thenReturn(mResources);
+ doReturn(mContext).when(jobSchedulerService).getTestableContext();
+ mJobConcurrencyManager = new JobConcurrencyManager(jobSchedulerService);
+ mGracePeriodObserver = mock(GracePeriodObserver.class);
+ mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mNextUserId = 10;
+ mJobConcurrencyManager.mGracePeriodObserver = mGracePeriodObserver;
+ }
+
+ @Test
+ public void testShouldRunAsFgUserJob_currentUser() {
+ assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+ createJob(createCurrentUser(false))));
+ }
+
+ @Test
+ public void testShouldRunAsFgUserJob_currentProfile() {
+ assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+ createJob(createCurrentUser(true))));
+ }
+
+ @Test
+ public void testShouldRunAsFgUserJob_primaryUser() {
+ assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+ createJob(createPrimaryUser(false))));
+ }
+
+ @Test
+ public void testShouldRunAsFgUserJob_primaryProfile() {
+ assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+ createJob(createPrimaryUser(true))));
+ }
+
+ @Test
+ public void testShouldRunAsFgUserJob_UnexpiredUser() {
+ assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+ createJob(createUnexpiredUser(false))));
+ }
+
+ @Test
+ public void testShouldRunAsFgUserJob_UnexpiredProfile() {
+ assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
+ createJob(createUnexpiredUser(true))));
+ }
+
+ @Test
+ public void testShouldRunAsFgUserJob_restrictedUser() {
+ assertFalse(mJobConcurrencyManager.shouldRunAsFgUserJob(
+ createJob(createRestrictedUser(false))));
+ }
+
+ @Test
+ public void testShouldRunAsFgUserJob_restrictedProfile() {
+ assertFalse(mJobConcurrencyManager.shouldRunAsFgUserJob(
+ createJob(createRestrictedUser(true))));
+ }
+
+ private UserInfo createCurrentUser(boolean isProfile) {
+ final UserInfo ui = createNewUser();
+ doReturn(ui.id).when(mActivityManagerInternal).getCurrentUserId();
+ return isProfile ? createNewProfile(ui) : ui;
+ }
+
+ private UserInfo createPrimaryUser(boolean isProfile) {
+ final UserInfo ui = createNewUser();
+ doReturn(true).when(ui).isPrimary();
+ return isProfile ? createNewProfile(ui) : ui;
+ }
+
+ private UserInfo createUnexpiredUser(boolean isProfile) {
+ final UserInfo ui = createNewUser();
+ doReturn(true).when(mGracePeriodObserver).isWithinGracePeriodForUser(ui.id);
+ return isProfile ? createNewProfile(ui) : ui;
+ }
+
+ private UserInfo createRestrictedUser(boolean isProfile) {
+ final UserInfo ui = createNewUser();
+ doReturn(UNAVAILABLE_USER).when(mActivityManagerInternal).getCurrentUserId();
+ doReturn(false).when(ui).isPrimary();
+ doReturn(false).when(mGracePeriodObserver).isWithinGracePeriodForUser(ui.id);
+ return isProfile ? createNewProfile(ui) : ui;
+ }
+
+ private UserInfo createNewProfile(UserInfo parent) {
+ final UserInfo ui = createNewUser();
+ parent.profileGroupId = parent.id;
+ ui.profileGroupId = parent.id;
+ doReturn(true).when(ui).isProfile();
+ return ui;
+ }
+
+ private UserInfo createNewUser() {
+ final UserInfo ui = mock(UserInfo.class);
+ ui.id = mNextUserId++;
+ doReturn(ui).when(mUserManagerInternal).getUserInfo(ui.id);
+ ui.profileGroupId = UserInfo.NO_PROFILE_GROUP_ID;
+ return ui;
+ }
+
+ private static JobStatus createJob(UserInfo userInfo) {
+ JobStatus jobStatus = JobStatus.createFromJobInfo(
+ new JobInfo.Builder(1, new ComponentName("foo", "bar")).build(),
+ userInfo.id * UserHandle.PER_USER_RANGE,
+ null, userInfo.id, "JobConcurrencyManagerTest");
+ return jobStatus;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 4effa4d..f2bb47b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -62,6 +62,7 @@
import com.android.server.PowerAllowlistInternal;
import com.android.server.SystemServiceManager;
import com.android.server.job.controllers.JobStatus;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.usage.AppStandbyInternal;
import org.junit.After;
@@ -128,6 +129,9 @@
// Called in DeviceIdleJobsController constructor.
doReturn(mock(DeviceIdleInternal.class))
.when(() -> LocalServices.getService(DeviceIdleInternal.class));
+ // Used in JobConcurrencyManager.
+ doReturn(mock(UserManagerInternal.class))
+ .when(() -> LocalServices.getService(UserManagerInternal.class));
// Used in JobStatus.
doReturn(mock(PackageManagerInternal.class))
.when(() -> LocalServices.getService(PackageManagerInternal.class));
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 0311920..0597443 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 @@
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 c39a77e..6156ba9 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.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 8e5b16e..2822d5c 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 @@
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 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 3b5cc88..66b037d 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
@@ -64,6 +64,7 @@
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.LocationRequest;
import android.location.LocationResult;
+import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
@@ -82,6 +83,7 @@
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;
@@ -158,17 +160,19 @@
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);
}
@@ -662,6 +666,23 @@
}
@Test
+ public void testProviderRequestListener() throws Exception {
+ IProviderRequestListener requestListener = mock(IProviderRequestListener.class);
+ mManager.addProviderRequestListener(requestListener);
+
+ ILocationListener locationListener = createMockLocationListener();
+ LocationRequest request = new LocationRequest.Builder(1).setWorkSource(
+ WORK_SOURCE).build();
+ mManager.registerLocationRequest(request, IDENTITY, PERMISSION_FINE, locationListener);
+
+ verify(requestListener, timeout(TIMEOUT_MS).times(1)).onProviderRequestChanged(anyString(),
+ any(ProviderRequest.class));
+
+ mManager.unregisterLocationRequest(locationListener);
+ mManager.removeProviderRequestListener(requestListener);
+ }
+
+ @Test
public void testGetCurrentLocation() throws Exception {
ILocationCallback listener = createMockGetCurrentLocationListener();
LocationRequest request = new LocationRequest.Builder(0).setWorkSource(WORK_SOURCE).build();
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 6daa381..31e2b64 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -59,9 +59,9 @@
},
libs: [
- "android.hardware.power-java",
+ "android.hardware.power-V1-java",
"android.hardware.tv.cec-V1.0-java",
- "android.hardware.vibrator-java",
+ "android.hardware.vibrator-V2-java",
"android.hidl.manager-V1.0-java",
"android.test.mock",
"android.test.base",
@@ -88,7 +88,7 @@
"libui",
"libunwindstack",
"libutils",
- "netd_aidl_interface-cpp",
+ "netd_aidl_interface-V5-cpp",
],
dxflags: ["--multi-dex"],
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml b/services/tests/servicestests/res/layout/widget_preview.xml
similarity index 74%
rename from packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
rename to services/tests/servicestests/res/layout/widget_preview.xml
index e09bf7e..137ff46 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp-land/bools.xml
+++ b/services/tests/servicestests/res/layout/widget_preview.xml
@@ -14,7 +14,8 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<resources>
- <bool name="can_use_one_handed_bouncer">true</bool>
-</resources>
+<TextView android:id="@+id/widget_preview"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="Widget preview" />
\ No newline at end of file
diff --git a/services/tests/servicestests/res/xml/dummy_appwidget_info.xml b/services/tests/servicestests/res/xml/dummy_appwidget_info.xml
index 6546216..72f025d 100644
--- a/services/tests/servicestests/res/xml/dummy_appwidget_info.xml
+++ b/services/tests/servicestests/res/xml/dummy_appwidget_info.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2017 The Android Open Source Project
~
@@ -19,6 +20,7 @@
android:minHeight="40dp"
android:updatePeriodMillis="86400000"
android:previewImage="@drawable/icon1"
+ android:previewLayout="@layout/widget_preview"
android:resizeMode="horizontal|vertical"
android:description="@string/widget_description"
android:widgetCategory="home_screen">
diff --git a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
index 50e7a03..58d6dae 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 @@
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 f7b2492..f0d7006 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.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.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.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 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 @@
@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 @@
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 @@
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/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
index 2a7905a..633957a 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -50,6 +50,7 @@
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.Process;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
@@ -81,6 +82,7 @@
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
@@ -92,6 +94,7 @@
@Presubmit
public class VibratorServiceTest {
+ private static final int TEST_TIMEOUT_MILLIS = 1_000;
private static final int UID = Process.ROOT_UID;
private static final int VIBRATOR_ID = 1;
private static final String PACKAGE_NAME = "package";
@@ -342,8 +345,8 @@
verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any());
// VibrationThread will start this vibration async, so wait before checking it never played.
- Thread.sleep(10);
- assertTrue(mVibratorProvider.getEffects().isEmpty());
+ assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service,
+ /* timeout= */ 20));
}
@Test
@@ -399,8 +402,8 @@
verify(mIInputManagerMock).vibrate(eq(1), any(), any());
// VibrationThread will start this vibration async, so wait before checking it never played.
- Thread.sleep(10);
- assertTrue(mVibratorProvider.getEffects().isEmpty());
+ assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service,
+ /* timeout= */ 20));
}
@Test
@@ -409,16 +412,10 @@
mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS);
-
- // VibrationThread will start this vibration async, so wait before triggering callbacks.
- Thread.sleep(10);
- assertTrue(service.isVibrating());
+ assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
-
- // Wait for callback to cancel vibration.
- Thread.sleep(10);
- assertFalse(service.isVibrating());
+ assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
}
@Test
@@ -427,26 +424,18 @@
mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
vibrate(service, VibrationEffect.createOneShot(1000, 100), RINGTONE_ATTRS);
-
- // VibrationThread will start this vibration async, so wait before triggering callbacks.
- Thread.sleep(10);
- assertTrue(service.isVibrating());
+ assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
-
- // Wait for callback to cancel vibration.
- Thread.sleep(10);
- assertTrue(service.isVibrating());
+ // Settings callback is async, so wait before checking it never got cancelled.
+ assertFalse(waitUntil(s -> !s.isVibrating(), service, /* timeout= */ 20));
}
@Test
public void vibrate_withSettingsChanged_doNotCancelVibration() throws Exception {
VibratorService service = createService();
vibrate(service, VibrationEffect.createOneShot(1000, 100), HAPTIC_FEEDBACK_ATTRS);
-
- // VibrationThread will start this vibration async, so wait before triggering callbacks.
- Thread.sleep(10);
- assertTrue(service.isVibrating());
+ assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_MEDIUM);
@@ -454,9 +443,8 @@
// FakeSettingsProvider don't support testing triggering ContentObserver yet.
service.updateVibrators();
- // Wait for callback to cancel vibration.
- Thread.sleep(10);
- assertTrue(service.isVibrating());
+ // Settings callback is async, so wait before checking it never got cancelled.
+ assertFalse(waitUntil(s -> !s.isVibrating(), service, /* timeout= */ 20));
}
@Test
@@ -488,8 +476,8 @@
inOrderVerifier.verify(mIInputManagerMock).vibrate(eq(2), eq(effect), any());
// VibrationThread will start this vibration async, so wait before checking it never played.
- Thread.sleep(10);
- assertTrue(mVibratorProvider.getEffects().isEmpty());
+ assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service,
+ /* timeout= */ 20));
}
@Test
@@ -521,8 +509,8 @@
verify(mIInputManagerMock).vibrate(eq(1), eq(effect), any());
// VibrationThread will start this vibration async, so wait before checking it never played.
- Thread.sleep(10);
- assertTrue(mVibratorProvider.getEffects().isEmpty());
+ assertFalse(waitUntil(s -> !mVibratorProvider.getEffects().isEmpty(), service,
+ /* timeout= */ 20));
}
@Test
@@ -531,18 +519,12 @@
VibratorService service = createService();
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
-
- // VibrationThread will start this vibration async, so wait before triggering callbacks.
- Thread.sleep(10);
- assertTrue(service.isVibrating());
+ assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
// Trigger callbacks from controller.
mTestLooper.moveTimeForward(50);
mTestLooper.dispatchAll();
-
- // VibrationThread needs some time to react to native callbacks and stop the vibrator.
- Thread.sleep(10);
- assertFalse(service.isVibrating());
+ assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
}
@Test
@@ -550,16 +532,10 @@
VibratorService service = createService();
vibrate(service, VibrationEffect.createOneShot(100, 100), ALARM_ATTRS);
-
- // VibrationThread will start this vibration async, so wait before checking.
- Thread.sleep(10);
- assertTrue(service.isVibrating());
+ assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
service.cancelVibrate(service);
-
- // VibrationThread will stop this vibration async, so wait before checking.
- Thread.sleep(10);
- assertFalse(service.isVibrating());
+ assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
}
@Test
@@ -584,17 +560,13 @@
service.registerVibratorStateListener(mVibratorStateListenerMock);
vibrate(service, VibrationEffect.createOneShot(30, 100), ALARM_ATTRS);
-
- // VibrationThread will start this vibration async, so wait before triggering callbacks.
- Thread.sleep(10);
- assertTrue(service.isVibrating());
+ assertTrue(waitUntil(s -> s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
service.unregisterVibratorStateListener(mVibratorStateListenerMock);
// Trigger callbacks from controller.
mTestLooper.moveTimeForward(50);
mTestLooper.dispatchAll();
- Thread.sleep(20);
- assertFalse(service.isVibrating());
+ assertTrue(waitUntil(s -> !s.isVibrating(), service, TEST_TIMEOUT_MILLIS));
InOrder inOrderVerifier = inOrder(mVibratorStateListenerMock);
// First notification done when listener is registered.
@@ -771,4 +743,15 @@
private void setGlobalSetting(String settingName, int value) {
Settings.Global.putInt(mContextSpy.getContentResolver(), settingName, value);
}
+
+ private boolean waitUntil(Predicate<VibratorService> predicate,
+ VibratorService service, long timeout) throws InterruptedException {
+ long timeoutTimestamp = SystemClock.uptimeMillis() + timeout;
+ boolean predicateResult = false;
+ while (!predicateResult && SystemClock.uptimeMillis() < timeoutTimestamp) {
+ Thread.sleep(10);
+ predicateResult = predicate.test(service);
+ }
+ return predicateResult;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index 27edfd4..6963a1a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -110,8 +110,6 @@
mMockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class);
when(mMockIBinder.queryLocalInterface(any())).thenReturn(mMockServiceClient);
- when(mMockWindowManagerInternal.isTouchableDisplay(Display.DEFAULT_DISPLAY)).thenReturn(
- true);
mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext,
COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(),
@@ -197,8 +195,9 @@
}
@Test
- public void sendGesture_touchableDisplay_injectEvents()
+ public void sendGesture_touchableDevice_injectEvents()
throws RemoteException {
+ when(mMockWindowManagerInternal.isTouchOrFaketouchDevice()).thenReturn(true);
setServiceBinding(COMPONENT_NAME);
mConnection.bindLocked();
mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
@@ -213,10 +212,9 @@
}
@Test
- public void sendGesture_untouchableDisplay_performGestureResultFailed()
+ public void sendGesture_untouchableDevice_performGestureResultFailed()
throws RemoteException {
- when(mMockWindowManagerInternal.isTouchableDisplay(Display.DEFAULT_DISPLAY)).thenReturn(
- false);
+ when(mMockWindowManagerInternal.isTouchOrFaketouchDevice()).thenReturn(false);
setServiceBinding(COMPONENT_NAME);
mConnection.bindLocked();
mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
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 caa46da..d039a9d 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 @@
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 @@
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 @@
*/
@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 @@
*/
@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 @@
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/appwidget/AppWidgetServiceImplTest.java b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
index 96f4344..78eb2df 100644
--- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
@@ -16,6 +16,8 @@
package com.android.server.appwidget;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -44,6 +46,7 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.widget.RemoteViews;
+import com.android.frameworks.servicestests.R;
import com.android.internal.appwidget.IAppWidgetHost;
import com.android.server.LocalServices;
@@ -293,6 +296,13 @@
}
}
+ public void testGetPreviewLayout() {
+ AppWidgetProviderInfo info =
+ mManager.getInstalledProvidersForPackage(mPkgName, null).get(0);
+
+ assertThat(info.previewLayout).isEqualTo(R.layout.widget_preview);
+ }
+
private int setupHostAndWidget() {
List<PendingHostUpdate> updates = mService.startListening(
mMockHost, mPkgName, HOST_ID, new int[0]).getList();
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 8d890b9c..7a4b901 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 @@
@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 @@
@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 @@
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 @@
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 @@
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 2d45726..7dd0734 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.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 @@
// 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 @@
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/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 8c853cc..7597cbf 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5616,43 +5616,52 @@
public void testDisallowSharingIntoProfileSetRestriction() {
when(mServiceContext.resources.getString(R.string.config_managed_provisioning_package))
.thenReturn("com.android.managedprovisioning");
+ when(getServices().userManagerInternal.getProfileParentId(anyInt()))
+ .thenReturn(UserHandle.USER_SYSTEM);
+ mServiceContext.binder.callingPid = DpmMockContext.SYSTEM_PID;
+ mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
Bundle restriction = new Bundle();
restriction.putBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, true);
- mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
- RestrictionsListener listener = new RestrictionsListener(mContext);
+ RestrictionsListener listener = new RestrictionsListener(
+ mServiceContext, getServices().userManagerInternal, dpms);
listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, restriction, new Bundle());
- verifyDataSharingChangedBroadcast();
+
+ verifyDataSharingAppliedBroadcast();
}
@Test
public void testDisallowSharingIntoProfileClearRestriction() {
when(mServiceContext.resources.getString(R.string.config_managed_provisioning_package))
.thenReturn("com.android.managedprovisioning");
+ when(getServices().userManagerInternal.getProfileParentId(anyInt()))
+ .thenReturn(UserHandle.USER_SYSTEM);
+ mServiceContext.binder.callingPid = DpmMockContext.SYSTEM_PID;
+ mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
Bundle restriction = new Bundle();
restriction.putBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, true);
- mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
- RestrictionsListener listener = new RestrictionsListener(mContext);
+ RestrictionsListener listener = new RestrictionsListener(
+ mServiceContext, getServices().userManagerInternal, dpms);
listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, new Bundle(), restriction);
- verifyDataSharingChangedBroadcast();
+
+ verifyDataSharingAppliedBroadcast();
}
@Test
public void testDisallowSharingIntoProfileUnchanged() {
- RestrictionsListener listener = new RestrictionsListener(mContext);
+ RestrictionsListener listener = new RestrictionsListener(
+ mContext, getServices().userManagerInternal, dpms);
listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, new Bundle(), new Bundle());
verify(mContext.spiedContext, never()).sendBroadcastAsUser(any(), any());
}
- private void verifyDataSharingChangedBroadcast() {
+ private void verifyDataSharingAppliedBroadcast() {
Intent expectedIntent = new Intent(
- DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED);
- expectedIntent.setPackage("com.android.managedprovisioning");
- expectedIntent.putExtra(Intent.EXTRA_USER_ID, CALLER_USER_HANDLE);
+ DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED);
verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
MockUtils.checkIntent(expectedIntent),
- MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+ MockUtils.checkUserHandle(CALLER_USER_HANDLE));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 54da643..1a22661 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -19,10 +19,14 @@
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertThrows;
+import android.hardware.devicestate.DeviceStateRequest;
import android.hardware.devicestate.IDeviceStateManagerCallback;
+import android.os.Binder;
+import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -33,6 +37,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.HashMap;
import java.util.Optional;
import javax.annotation.Nullable;
@@ -61,87 +66,69 @@
}
@Test
- public void requestStateChange() {
+ public void baseStateChanged() {
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
- mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
+ mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
OTHER_DEVICE_STATE.getIdentifier());
}
@Test
- public void requestStateChange_pendingState() {
+ public void baseStateChanged_withStatePendingPolicyCallback() {
mPolicy.blockConfigure();
- mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
+ mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
- assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
OTHER_DEVICE_STATE.getIdentifier());
- mProvider.notifyRequestState(DEFAULT_DEVICE_STATE.getIdentifier());
+ mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState().get(), OTHER_DEVICE_STATE);
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
OTHER_DEVICE_STATE.getIdentifier());
mPolicy.resumeConfigure();
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
- public void requestStateChange_unsupportedState() {
- mProvider.notifyRequestState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
+ public void baseStateChanged_unsupportedState() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ mProvider.setState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
+ });
+
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
}
@Test
- public void requestStateChange_invalidState() {
+ public void baseStateChanged_invalidState() {
assertThrows(IllegalArgumentException.class, () -> {
- mProvider.notifyRequestState(INVALID_DEVICE_STATE);
+ mProvider.setState(INVALID_DEVICE_STATE);
});
- }
- @Test
- public void requestOverrideState() {
- mService.setOverrideState(OTHER_DEVICE_STATE.getIdentifier());
- // Committed state changes as there is a requested override.
- assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
- OTHER_DEVICE_STATE.getIdentifier());
-
- // Committed state is set back to the requested state once the override is cleared.
- mService.clearOverrideState();
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
- DEFAULT_DEVICE_STATE.getIdentifier());
- }
-
- @Test
- public void requestOverrideState_unsupportedState() {
- mService.setOverrideState(UNSUPPORTED_DEVICE_STATE.getIdentifier());
- // Committed state remains the same as the override state is unsupported.
- assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getPendingState(), Optional.empty());
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
DEFAULT_DEVICE_STATE.getIdentifier());
}
@@ -150,7 +137,7 @@
public void supportedStatesChanged() {
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
@@ -158,46 +145,27 @@
// supported.
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
}
@Test
- public void supportedStatesChanged_unsupportedRequestedState() {
+ public void supportedStatesChanged_unsupportedBaseState() {
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
mProvider.notifySupportedDeviceStates(new DeviceState[]{ OTHER_DEVICE_STATE });
// The current requested state is cleared because it is no longer supported.
assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getRequestedState(), Optional.empty());
+ assertEquals(mService.getBaseState(), Optional.empty());
- mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
+ mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
assertEquals(mService.getPendingState(), Optional.empty());
- assertEquals(mService.getRequestedState().get(), OTHER_DEVICE_STATE);
- }
-
- @Test
- public void supportedStatesChanged_unsupportedOverrideState() {
- mService.setOverrideState(OTHER_DEVICE_STATE.getIdentifier());
- // Committed state changes as there is a requested override.
- assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
- OTHER_DEVICE_STATE.getIdentifier());
-
- mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
-
- // Committed state is set back to the requested state as the override state is no longer
- // supported.
- assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
- assertEquals(mService.getRequestedState().get(), DEFAULT_DEVICE_STATE);
- assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
- DEFAULT_DEVICE_STATE.getIdentifier());
+ assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
}
@Test
@@ -205,17 +173,17 @@
TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
mService.getBinderService().registerCallback(callback);
- mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
+ mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
assertNotNull(callback.getLastNotifiedValue());
assertEquals(callback.getLastNotifiedValue().intValue(),
OTHER_DEVICE_STATE.getIdentifier());
- mProvider.notifyRequestState(DEFAULT_DEVICE_STATE.getIdentifier());
+ mProvider.setState(DEFAULT_DEVICE_STATE.getIdentifier());
assertEquals(callback.getLastNotifiedValue().intValue(),
DEFAULT_DEVICE_STATE.getIdentifier());
mPolicy.blockConfigure();
- mProvider.notifyRequestState(OTHER_DEVICE_STATE.getIdentifier());
+ mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
// The callback should not have been notified of the state change as the policy is still
// pending callback.
assertEquals(callback.getLastNotifiedValue().intValue(),
@@ -237,6 +205,148 @@
DEFAULT_DEVICE_STATE.getIdentifier());
}
+ @Test
+ public void getSupportedDeviceStates() throws RemoteException {
+ final int[] expectedStates = new int[] { 0, 1 };
+ assertEquals(mService.getBinderService().getSupportedDeviceStates(), expectedStates);
+ }
+
+ @Test
+ public void requestState() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ final IBinder token = new Binder();
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+
+ mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_ACTIVE);
+ // Committed state changes as there is a requested override.
+ assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+
+
+ mService.getBinderService().cancelRequest(token);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_CANCELED);
+ // Committed state is set back to the requested state once the override is cleared.
+ assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
+ }
+
+ @Test
+ public void requestState_flagCancelWhenBaseChanges() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ final IBinder token = new Binder();
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+
+ mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
+ DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_ACTIVE);
+
+ // Committed state changes as there is a requested override.
+ assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ mProvider.setState(OTHER_DEVICE_STATE.getIdentifier());
+
+ // Request is canceled because the base state changed.
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_CANCELED);
+ // Committed state is set back to the requested state once the override is cleared.
+ assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), OTHER_DEVICE_STATE);
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+ }
+
+ @Test
+ public void requestState_becomesUnsupported() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ final IBinder token = new Binder();
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+
+ mService.getBinderService().requestState(token, OTHER_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_ACTIVE);
+ // Committed state changes as there is a requested override.
+ assertEquals(mService.getCommittedState(), OTHER_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getOverrideState().get(), OTHER_DEVICE_STATE);
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ mProvider.notifySupportedDeviceStates(new DeviceState[]{ DEFAULT_DEVICE_STATE });
+
+ // Request is canceled because the state is no longer supported.
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_CANCELED);
+ // Committed state is set back to the requested state as the override state is no longer
+ // supported.
+ assertEquals(mService.getCommittedState(), DEFAULT_DEVICE_STATE);
+ assertEquals(mService.getBaseState().get(), DEFAULT_DEVICE_STATE);
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
+ }
+
+ @Test
+ public void requestState_unsupportedState() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ final IBinder token = new Binder();
+ mService.getBinderService().requestState(token,
+ UNSUPPORTED_DEVICE_STATE.getIdentifier(), 0 /* flags */);
+ });
+ }
+
+ @Test
+ public void requestState_invalidState() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ final IBinder token = new Binder();
+ mService.getBinderService().requestState(token, INVALID_DEVICE_STATE, 0 /* flags */);
+ });
+ }
+
+ @Test
+ public void requestState_beforeRegisteringCallback() {
+ assertThrows(IllegalStateException.class, () -> {
+ final IBinder token = new Binder();
+ mService.getBinderService().requestState(token, DEFAULT_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+ });
+ }
+
private static final class TestDeviceStatePolicy implements DeviceStatePolicy {
private final DeviceStateProvider mProvider;
private int mLastDeviceStateRequestedToConfigure = INVALID_DEVICE_STATE;
@@ -306,23 +416,48 @@
mListener.onSupportedDeviceStatesChanged(supportedDeviceStates);
}
- public void notifyRequestState(int identifier) {
+ public void setState(int identifier) {
mListener.onStateChanged(identifier);
}
}
private static final class TestDeviceStateManagerCallback extends
IDeviceStateManagerCallback.Stub {
- Integer mLastNotifiedValue;
+ public static final int STATUS_UNKNOWN = 0;
+ public static final int STATUS_ACTIVE = 1;
+ public static final int STATUS_SUSPENDED = 2;
+ public static final int STATUS_CANCELED = 3;
+
+ private Integer mLastNotifiedValue;
+ private final HashMap<IBinder, Integer> mLastNotifiedStatus = new HashMap<>();
@Override
public void onDeviceStateChanged(int deviceState) {
mLastNotifiedValue = deviceState;
}
+ @Override
+ public void onRequestActive(IBinder token) {
+ mLastNotifiedStatus.put(token, STATUS_ACTIVE);
+ }
+
+ @Override
+ public void onRequestSuspended(IBinder token) {
+ mLastNotifiedStatus.put(token, STATUS_SUSPENDED);
+ }
+
+ @Override
+ public void onRequestCanceled(IBinder token) {
+ mLastNotifiedStatus.put(token, STATUS_CANCELED);
+ }
+
@Nullable
Integer getLastNotifiedValue() {
return mLastNotifiedValue;
}
+
+ int getLastNotifiedStatus(IBinder requestToken) {
+ return mLastNotifiedStatus.getOrDefault(requestToken, STATUS_UNKNOWN);
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/job/GracePeriodObserverTest.java b/services/tests/servicestests/src/com/android/server/job/GracePeriodObserverTest.java
new file mode 100644
index 0000000..1915b8c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/job/GracePeriodObserverTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.job;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.app.ActivityManagerInternal;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.SystemClock;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.job.JobConcurrencyManager.GracePeriodObserver;
+import com.android.server.pm.UserManagerInternal;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.time.ZoneOffset;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class GracePeriodObserverTest {
+ private GracePeriodObserver mGracePeriodObserver;
+ private UserManagerInternal mUserManagerInternal;
+ private static final int FIRST_USER = 0;
+
+ @BeforeClass
+ public static void setUpOnce() {
+ UserManagerInternal userManagerInternal = mock(UserManagerInternal.class);
+ LocalServices.addService(UserManagerInternal.class, userManagerInternal);
+ ActivityManagerInternal activityManagerInternal = mock(ActivityManagerInternal.class);
+ LocalServices.addService(ActivityManagerInternal.class, activityManagerInternal);
+ }
+
+ @AfterClass
+ public static void tearDownOnce() {
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ }
+
+ @Before
+ public void setUp() {
+ final Context context = ApplicationProvider.getApplicationContext();
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
+ doReturn(FIRST_USER)
+ .when(LocalServices.getService(ActivityManagerInternal.class)).getCurrentUserId();
+ mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+ doReturn(true).when(mUserManagerInternal).exists(FIRST_USER);
+ mGracePeriodObserver = new GracePeriodObserver(context);
+ }
+
+ @Test
+ public void testGracePeriod() throws RemoteException {
+ final int oldUser = FIRST_USER;
+ final int newUser = 10;
+ doReturn(true).when(mUserManagerInternal).exists(newUser);
+ mGracePeriodObserver.onUserSwitchComplete(newUser);
+ assertTrue(mGracePeriodObserver.isWithinGracePeriodForUser(oldUser));
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.offset(JobSchedulerService.sElapsedRealtimeClock,
+ Duration.ofMillis(mGracePeriodObserver.mGracePeriod));
+ assertFalse(mGracePeriodObserver.isWithinGracePeriodForUser(oldUser));
+ }
+
+ @Test
+ public void testCleanUp() throws RemoteException {
+ final int removedUser = FIRST_USER;
+ final int newUser = 10;
+ mGracePeriodObserver.onUserSwitchComplete(newUser);
+
+ final int sizeBefore = mGracePeriodObserver.mGracePeriodExpiration.size();
+ doReturn(false).when(mUserManagerInternal).exists(removedUser);
+
+ mGracePeriodObserver.onUserRemoved(removedUser);
+ assertEquals(sizeBefore - 1, mGracePeriodObserver.mGracePeriodExpiration.size());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
index 263cf48..353ac4b 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
@@ -16,7 +16,10 @@
package com.android.server.job;
+import static com.android.server.job.JobConcurrencyManager.NUM_WORK_TYPES;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
@@ -47,6 +50,10 @@
public class WorkCountTrackerTest {
private static final String TAG = "WorkerCountTrackerTest";
+ private static final double[] EQUAL_PROBABILITY_CDF =
+ buildCdf(1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES, 1.0 / NUM_WORK_TYPES,
+ 1.0 / NUM_WORK_TYPES);
+
private Random mRandom;
private WorkCountTracker mWorkCountTracker;
@@ -56,6 +63,47 @@
mWorkCountTracker = new WorkCountTracker();
}
+ @NonNull
+ private static double[] buildCdf(double pTop, double pEj, double pBg, double pBgUser) {
+ double[] cdf = new double[JobConcurrencyManager.NUM_WORK_TYPES];
+ double sum = 0;
+
+ sum += pTop;
+ cdf[0] = sum;
+ sum += pEj;
+ cdf[1] = sum;
+ sum += pBg;
+ cdf[2] = sum;
+ sum += pBgUser;
+ cdf[3] = sum;
+
+ if (Double.compare(1, sum) != 0) {
+ throw new IllegalArgumentException("probabilities don't sum to one: " + sum);
+ }
+ return cdf;
+ }
+
+ @JobConcurrencyManager.WorkType
+ static int getRandomWorkType(double[] cdf, double rand) {
+ for (int i = cdf.length - 1; i >= 0; --i) {
+ if (rand < cdf[i] && (i == 0 || rand > cdf[i - 1])) {
+ switch (i) {
+ case 0:
+ return WORK_TYPE_TOP;
+ case 1:
+ return WORK_TYPE_EJ;
+ case 2:
+ return WORK_TYPE_BG;
+ case 3:
+ return WORK_TYPE_BGUSER;
+ default:
+ throw new IllegalStateException("Unknown work type");
+ }
+ }
+ }
+ throw new IllegalStateException("Couldn't pick random work type");
+ }
+
/**
* Represents running and pending jobs.
*/
@@ -63,25 +111,22 @@
public final SparseIntArray running = new SparseIntArray();
public final SparseIntArray pending = new SparseIntArray();
- public void maybeEnqueueJobs(double startRatio, double fgJobRatio) {
- while (mRandom.nextDouble() < startRatio) {
- if (mRandom.nextDouble() < fgJobRatio) {
- pending.put(WORK_TYPE_TOP, pending.get(WORK_TYPE_TOP) + 1);
- } else {
- pending.put(WORK_TYPE_BG, pending.get(WORK_TYPE_BG) + 1);
- }
+ public void maybeEnqueueJobs(double probStart, double[] typeCdf) {
+ while (mRandom.nextDouble() < probStart) {
+ final int workType = getRandomWorkType(typeCdf, mRandom.nextDouble());
+ pending.put(workType, pending.get(workType) + 1);
}
}
- public void maybeFinishJobs(double stopRatio) {
+ public void maybeFinishJobs(double probStop) {
for (int i = running.get(WORK_TYPE_BG); i > 0; i--) {
- if (mRandom.nextDouble() < stopRatio) {
+ if (mRandom.nextDouble() < probStop) {
running.put(WORK_TYPE_BG, running.get(WORK_TYPE_BG) - 1);
mWorkCountTracker.onJobFinished(WORK_TYPE_BG);
}
}
for (int i = running.get(WORK_TYPE_TOP); i > 0; i--) {
- if (mRandom.nextDouble() < stopRatio) {
+ if (mRandom.nextDouble() < probStop) {
running.put(WORK_TYPE_TOP, running.get(WORK_TYPE_TOP) - 1);
mWorkCountTracker.onJobFinished(WORK_TYPE_TOP);
}
@@ -116,29 +161,27 @@
mWorkCountTracker.onCountDone();
}
- private void startPendingJobs(Jobs jobs) {
- while ((jobs.pending.get(WORK_TYPE_TOP) > 0
- && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE)
- || (jobs.pending.get(WORK_TYPE_BG) > 0
- && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE)) {
- final boolean isStartingFg = mRandom.nextBoolean();
+ private boolean hasStartablePendingJob(Jobs jobs) {
+ for (int i = 0; i < jobs.pending.size(); ++i) {
+ if (jobs.pending.valueAt(i) > 0
+ && mWorkCountTracker.canJobStart(jobs.pending.keyAt(i)) != WORK_TYPE_NONE) {
+ return true;
+ }
+ }
+ return false;
+ }
- if (isStartingFg) {
- if (jobs.pending.get(WORK_TYPE_TOP) > 0
- && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) {
- jobs.pending.put(WORK_TYPE_TOP, jobs.pending.get(WORK_TYPE_TOP) - 1);
- jobs.running.put(WORK_TYPE_TOP, jobs.running.get(WORK_TYPE_TOP) + 1);
- mWorkCountTracker.stageJob(WORK_TYPE_TOP);
- mWorkCountTracker.onJobStarted(WORK_TYPE_TOP);
- }
- } else {
- if (jobs.pending.get(WORK_TYPE_BG) > 0
- && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE) {
- jobs.pending.put(WORK_TYPE_BG, jobs.pending.get(WORK_TYPE_BG) - 1);
- jobs.running.put(WORK_TYPE_BG, jobs.running.get(WORK_TYPE_BG) + 1);
- mWorkCountTracker.stageJob(WORK_TYPE_BG);
- mWorkCountTracker.onJobStarted(WORK_TYPE_BG);
- }
+ private void startPendingJobs(Jobs jobs) {
+ while (hasStartablePendingJob(jobs)) {
+ final int startingWorkType =
+ getRandomWorkType(EQUAL_PROBABILITY_CDF, mRandom.nextDouble());
+
+ if (jobs.pending.get(startingWorkType) > 0
+ && mWorkCountTracker.canJobStart(startingWorkType) != WORK_TYPE_NONE) {
+ jobs.pending.put(startingWorkType, jobs.pending.get(startingWorkType) - 1);
+ jobs.running.put(startingWorkType, jobs.running.get(startingWorkType) + 1);
+ mWorkCountTracker.stageJob(startingWorkType);
+ mWorkCountTracker.onJobStarted(startingWorkType);
}
}
}
@@ -149,10 +192,10 @@
private void checkRandom(Jobs jobs, int numTests, int totalMax,
@NonNull List<Pair<Integer, Integer>> minLimits,
@NonNull List<Pair<Integer, Integer>> maxLimits,
- double startRatio, double fgJobRatio, double stopRatio) {
+ double probStart, double[] typeCdf, double probStop) {
for (int i = 0; i < numTests; i++) {
- jobs.maybeFinishJobs(stopRatio);
- jobs.maybeEnqueueJobs(startRatio, fgJobRatio);
+ jobs.maybeFinishJobs(probStop);
+ jobs.maybeEnqueueJobs(probStart, typeCdf);
recount(jobs, totalMax, minLimits, maxLimits);
startPendingJobs(jobs);
@@ -178,18 +221,19 @@
*/
@Test
public void testRandom1() {
+ assertThat(EQUAL_PROBABILITY_CDF.length).isEqualTo(NUM_WORK_TYPES);
+
final Jobs jobs = new Jobs();
final int numTests = 5000;
final int totalMax = 6;
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
- final double stopRatio = 0.1;
- final double fgJobRatio = 0.5;
- final double startRatio = 0.1;
+ final double probStop = 0.1;
+ final double probStart = 0.1;
- checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
- startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+ EQUAL_PROBABILITY_CDF, probStop);
}
@Test
@@ -198,14 +242,14 @@
final int numTests = 5000;
final int totalMax = 2;
- final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final List<Pair<Integer, Integer>> minLimits = List.of();
- final double stopRatio = 0.5;
- final double fgJobRatio = 0.5;
- final double startRatio = 0.5;
+ final double probStop = 0.5;
+ final double[] cdf = buildCdf(0.5, 0, 0.5, 0);
+ final double probStart = 0.5;
- checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
- startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
@Test
@@ -214,14 +258,14 @@
final int numTests = 5000;
final int totalMax = 2;
- final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
- final double stopRatio = 0.5;
- final double fgJobRatio = 0.5;
- final double startRatio = 0.5;
+ final double probStop = 0.5;
+ final double[] cdf = buildCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3);
+ final double probStart = 0.5;
- checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
- startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
@Test
@@ -230,14 +274,14 @@
final int numTests = 5000;
final int totalMax = 10;
- final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
final List<Pair<Integer, Integer>> minLimits = List.of();
- final double stopRatio = 0.5;
- final double fgJobRatio = 0.5;
- final double startRatio = 0.5;
+ final double probStop = 0.5;
+ final double[] cdf = buildCdf(1.0 / 3, 0, 1.0 / 3, 1.0 / 3);
+ final double probStart = 0.5;
- checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
- startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
@Test
@@ -246,14 +290,14 @@
final int numTests = 5000;
final int totalMax = 6;
- final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
- final double stopRatio = 0.5;
- final double fgJobRatio = 0.1;
- final double startRatio = 0.5;
+ final double probStop = 0.5;
+ final double[] cdf = buildCdf(0.1, 0, 0.8, .1);
+ final double probStart = 0.5;
- checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
- startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
@Test
@@ -262,14 +306,14 @@
final int numTests = 5000;
final int totalMax = 6;
- final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
- final double stopRatio = 0.5;
- final double fgJobRatio = 0.9;
- final double startRatio = 0.5;
+ final double probStop = 0.5;
+ final double[] cdf = buildCdf(0.9, 0, 0.1, 0);
+ final double probStart = 0.5;
- checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
- startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
@Test
@@ -278,14 +322,14 @@
final int numTests = 5000;
final int totalMax = 6;
- final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
- final double stopRatio = 0.4;
- final double fgJobRatio = 0.1;
- final double startRatio = 0.5;
+ final double probStop = 0.4;
+ final double[] cdf = buildCdf(0.1, 0, 0.1, .8);
+ final double probStart = 0.5;
- checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
- startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
@Test
@@ -294,14 +338,136 @@
final int numTests = 5000;
final int totalMax = 6;
- final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
- final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
- final double stopRatio = 0.4;
- final double fgJobRatio = 0.9;
- final double startRatio = 0.5;
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
+ final List<Pair<Integer, Integer>> minLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
+ final double probStop = 0.4;
+ final double[] cdf = buildCdf(0.9, 0, 0.05, 0.05);
+ final double probStart = 0.5;
- checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
- startRatio, fgJobRatio, stopRatio);
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+ }
+
+ @Test
+ public void testRandom9() {
+ final Jobs jobs = new Jobs();
+
+ final int numTests = 5000;
+ final int totalMax = 6;
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
+ final List<Pair<Integer, Integer>> minLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
+ final double probStop = 0.5;
+ final double[] cdf = buildCdf(0, 0, 0.5, 0.5);
+ final double probStart = 0.5;
+
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+ }
+
+ @Test
+ public void testRandom10() {
+ final Jobs jobs = new Jobs();
+
+ final int numTests = 5000;
+ final int totalMax = 6;
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
+ final List<Pair<Integer, Integer>> minLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
+ final double probStop = 0.5;
+ final double[] cdf = buildCdf(0, 0, 0.1, 0.9);
+ final double probStart = 0.5;
+
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+ }
+
+ @Test
+ public void testRandom11() {
+ final Jobs jobs = new Jobs();
+
+ final int numTests = 5000;
+ final int totalMax = 6;
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
+ final List<Pair<Integer, Integer>> minLimits =
+ List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
+ final double probStop = 0.5;
+ final double[] cdf = buildCdf(0, 0, 0.9, 0.1);
+ final double probStart = 0.5;
+
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+ }
+
+ @Test
+ public void testRandom12() {
+ final Jobs jobs = new Jobs();
+
+ final int numTests = 5000;
+ final int totalMax = 6;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> minLimits =
+ List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2));
+ final double probStop = 0.4;
+ final double[] cdf = buildCdf(0.5, 0.5, 0, 0);
+ final double probStart = 0.5;
+
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+ }
+
+ @Test
+ public void testRandom13() {
+ assertThat(EQUAL_PROBABILITY_CDF.length).isEqualTo(NUM_WORK_TYPES);
+
+ final Jobs jobs = new Jobs();
+
+ final int numTests = 5000;
+ final int totalMax = 13;
+ final List<Pair<Integer, Integer>> maxLimits = List.of(
+ Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4),
+ Pair.create(WORK_TYPE_BGUSER, 3));
+ final List<Pair<Integer, Integer>> minLimits =
+ List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 1));
+ final double probStop = 0.01;
+ final double probStart = 0.99;
+
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart,
+ EQUAL_PROBABILITY_CDF, probStop);
+ }
+
+ @Test
+ public void testRandom14() {
+ final Jobs jobs = new Jobs();
+
+ final int numTests = 5000;
+ final int totalMax = 6;
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4));
+ final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
+ final double probStop = 0.4;
+ final double[] cdf = buildCdf(.1, 0.5, 0.35, 0.05);
+ final double probStart = 0.5;
+
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
+ }
+
+ @Test
+ public void testRandom15() {
+ final Jobs jobs = new Jobs();
+
+ final int numTests = 5000;
+ final int totalMax = 6;
+ final List<Pair<Integer, Integer>> maxLimits =
+ List.of(Pair.create(WORK_TYPE_EJ, 5), Pair.create(WORK_TYPE_BG, 4),
+ Pair.create(WORK_TYPE_BGUSER, 1));
+ final List<Pair<Integer, Integer>> minLimits =
+ List.of(Pair.create(WORK_TYPE_EJ, 3), Pair.create(WORK_TYPE_BG, 2));
+ final double probStop = 0.4;
+ final double[] cdf = buildCdf(0.01, 0.49, 0.1, 0.4);
+ final double probStart = 0.5;
+
+ checkRandom(jobs, numTests, totalMax, minLimits, maxLimits, probStart, cdf, probStop);
}
/** Used by the following tests */
@@ -337,7 +503,7 @@
public void testBasic() {
checkSimple(6,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
- /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
/* run */ List.of(),
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 1)),
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 1)),
@@ -345,7 +511,7 @@
checkSimple(6,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
- /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
/* run */ List.of(),
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10)),
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
@@ -354,7 +520,7 @@
// When there are BG jobs pending, 2 (min-BG) jobs should run.
checkSimple(6,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
- /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
/* run */ List.of(),
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 1)),
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)),
@@ -385,6 +551,16 @@
checkSimple(8,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+ /* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 4)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 49), Pair.create(WORK_TYPE_BG, 49)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 4)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 47), Pair.create(WORK_TYPE_BG, 49))
+ );
+
+
+ checkSimple(8,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 6)),
/* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 4)),
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)),
@@ -407,6 +583,14 @@
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)),
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 8), Pair.create(WORK_TYPE_BG, 49)));
+ checkSimple(8,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
+ /* run */ List.of(Pair.create(WORK_TYPE_BG, 6)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 8), Pair.create(WORK_TYPE_BG, 49)));
+
checkSimple(6,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
@@ -415,6 +599,16 @@
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)));
+ checkSimple(8,
+ /* min */ List.of(Pair.create(WORK_TYPE_EJ, 2), Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
+ /* run */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_EJ, 5),
+ Pair.create(WORK_TYPE_BG, 3)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_EJ, 2)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3),
+ Pair.create(WORK_TYPE_BG, 3)));
+
// This could happen if we lower the effective config due to higher memory pressure after
// we've already started running jobs. We shouldn't stop already running jobs, but also
// shouldn't start new ones.
@@ -425,6 +619,38 @@
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)),
/* resRun */ List.of(Pair.create(WORK_TYPE_BG, 6)),
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)));
+
+ checkSimple(6,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
+ /* run */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10),
+ Pair.create(WORK_TYPE_BG, 3),
+ Pair.create(WORK_TYPE_BGUSER, 3)),
+ /* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
+ /* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 6),
+ Pair.create(WORK_TYPE_BG, 3),
+ Pair.create(WORK_TYPE_BGUSER, 3)));
+
+ checkSimple(6,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 3)),
+ /* run */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 3)),
+ /* resRun */ List.of(
+ Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
+ /* resPen */ List.of(
+ Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)));
+
+ checkSimple(6,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)),
+ /* run */ List.of(Pair.create(WORK_TYPE_BG, 2)),
+ /* pen */ List.of(Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 3)),
+ /* resRun */ List.of(
+ Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)),
+ /* resPen */ List.of(
+ Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 2)));
}
/** Tests that the counter updates properly when jobs are stopped. */
diff --git a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
index c28292f..2288a89 100644
--- a/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
@@ -16,8 +16,11 @@
package com.android.server.job;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_EJ;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import android.annotation.NonNull;
@@ -31,11 +34,9 @@
import com.android.server.job.JobConcurrencyManager.WorkTypeConfig;
import org.junit.After;
-import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@@ -43,9 +44,13 @@
public class WorkTypeConfigTest {
private static final String KEY_MAX_TOTAL = "concurrency_max_total_test";
private static final String KEY_MAX_TOP = "concurrency_max_top_test";
+ private static final String KEY_MAX_EJ = "concurrency_max_ej_test";
private static final String KEY_MAX_BG = "concurrency_max_bg_test";
+ private static final String KEY_MAX_BGUSER = "concurrency_max_bguser_test";
private static final String KEY_MIN_TOP = "concurrency_min_top_test";
+ private static final String KEY_MIN_EJ = "concurrency_min_ej_test";
private static final String KEY_MIN_BG = "concurrency_min_bg_test";
+ private static final String KEY_MIN_BGUSER = "concurrency_min_bguser_test";
@After
public void tearDown() throws Exception {
@@ -56,43 +61,27 @@
// DeviceConfig.resetToDefaults() doesn't work here. Need to reset constants manually.
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOTAL, "", false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOP, "", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_EJ, "", false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, "", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BGUSER, "", false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_TOP, "", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_EJ, "", false);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, "", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BGUSER, "", false);
}
private void check(@Nullable DeviceConfig.Properties config,
int defaultTotal,
- @Nullable Pair<Integer, Integer> defaultTopLimits,
- @Nullable Pair<Integer, Integer> defaultBgLimits,
+ @NonNull List<Pair<Integer, Integer>> defaultMin,
+ @NonNull List<Pair<Integer, Integer>> defaultMax,
boolean expectedValid, int expectedTotal,
- @NonNull Pair<Integer, Integer> expectedTopLimits,
- @NonNull Pair<Integer, Integer> expectedBgLimits) throws Exception {
+ @NonNull List<Pair<Integer, Integer>> expectedMinLimits,
+ @NonNull List<Pair<Integer, Integer>> expectedMaxLimits) throws Exception {
resetConfig();
if (config != null) {
DeviceConfig.setProperties(config);
}
- List<Pair<Integer, Integer>> defaultMin = new ArrayList<>();
- List<Pair<Integer, Integer>> defaultMax = new ArrayList<>();
- Integer val;
- if (defaultTopLimits != null) {
- if ((val = defaultTopLimits.first) != null) {
- defaultMin.add(Pair.create(WORK_TYPE_TOP, val));
- }
- if ((val = defaultTopLimits.second) != null) {
- defaultMax.add(Pair.create(WORK_TYPE_TOP, val));
- }
- }
- if (defaultBgLimits != null) {
- if ((val = defaultBgLimits.first) != null) {
- defaultMin.add(Pair.create(WORK_TYPE_BG, val));
- }
- if ((val = defaultBgLimits.second) != null) {
- defaultMax.add(Pair.create(WORK_TYPE_BG, val));
- }
- }
-
final WorkTypeConfig counts;
try {
counts = new WorkTypeConfig("test",
@@ -112,40 +101,126 @@
counts.update(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
- Assert.assertEquals(expectedTotal, counts.getMaxTotal());
- Assert.assertEquals((int) expectedTopLimits.first, counts.getMinReserved(WORK_TYPE_TOP));
- Assert.assertEquals((int) expectedTopLimits.second, counts.getMax(WORK_TYPE_TOP));
- Assert.assertEquals((int) expectedBgLimits.first, counts.getMinReserved(WORK_TYPE_BG));
- Assert.assertEquals((int) expectedBgLimits.second, counts.getMax(WORK_TYPE_BG));
+ assertEquals(expectedTotal, counts.getMaxTotal());
+ for (Pair<Integer, Integer> min : expectedMinLimits) {
+ assertEquals((int) min.second, counts.getMinReserved(min.first));
+ }
+ for (Pair<Integer, Integer> max : expectedMaxLimits) {
+ assertEquals((int) max.second, counts.getMax(max.first));
+ }
}
@Test
public void test() throws Exception {
// Tests with various combinations.
- check(null, /*default*/ 5, Pair.create(4, null), Pair.create(0, 1),
- /*expected*/ true, 5, Pair.create(4, 5), Pair.create(0, 1));
- check(null, /*default*/ 5, Pair.create(5, null), Pair.create(0, 0),
- /*expected*/ true, 5, Pair.create(5, 5), Pair.create(0, 1));
- check(null, /*default*/ 0, Pair.create(5, null), Pair.create(0, 0),
- /*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1));
- check(null, /*default*/ -1, null, Pair.create(-1, -1),
- /*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1));
- check(null, /*default*/ 5, null, Pair.create(5, 5),
- /*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5));
- check(null, /*default*/ 6, Pair.create(1, null), Pair.create(6, 5),
- /*expected*/ false, 6, Pair.create(1, 6), Pair.create(5, 5));
- check(null, /*default*/ 4, null, Pair.create(6, 5),
- /*expected*/ false, 4, Pair.create(1, 4), Pair.create(3, 4));
- check(null, /*default*/ 5, Pair.create(4, null), Pair.create(1, 1),
- /*expected*/ true, 5, Pair.create(4, 5), Pair.create(1, 1));
- check(null, /*default*/ 15, null, Pair.create(15, 15),
- /*expected*/ true, 15, Pair.create(1, 15), Pair.create(14, 15));
- check(null, /*default*/ 16, null, Pair.create(16, 16),
- /*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16));
- check(null, /*default*/ 20, null, Pair.create(20, 20),
- /*expected*/ false, 16, Pair.create(1, 16), Pair.create(15, 16));
- check(null, /*default*/ 20, null, Pair.create(16, 16),
- /*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16));
+ check(null, /*default*/ 13,
+ /* min */ List.of(),
+ /* max */ List.of(),
+ /*expected*/ true, 13,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_EJ, 0),
+ Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 13), Pair.create(WORK_TYPE_EJ, 13),
+ Pair.create(WORK_TYPE_BG, 13), Pair.create(WORK_TYPE_BGUSER, 13)));
+ check(null, /*default*/ 5,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+ /*expected*/ true, 5,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)));
+ check(null, /*default*/ 5,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+ Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)),
+ /* max */ List.of(
+ Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 1)),
+ /*expected*/ true, 5,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+ Pair.create(WORK_TYPE_BG, 0), Pair.create(WORK_TYPE_BGUSER, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+ Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)));
+ check(null, /*default*/ 0,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 0)),
+ /*expected*/ false, 1,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 1)));
+ check(null, /*default*/ -1,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, -1)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, -1)),
+ /*expected*/ false, 1,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 1)));
+ check(null, /*default*/ 5,
+ /* min */ List.of(
+ Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 0)),
+ /* max */ List.of(
+ Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)),
+ /*expected*/ true, 5,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+ Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+ Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)));
+ check(null, /*default*/ 6,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+ Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 2)),
+ /* max */ List.of(
+ Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 1)),
+ /*expected*/ false, 6,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+ Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 6),
+ Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 1)));
+ check(null, /*default*/ 4,
+ /* min */ List.of(
+ Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 6)),
+ /* max */ List.of(
+ Pair.create(WORK_TYPE_BG, 5), Pair.create(WORK_TYPE_BGUSER, 5)),
+ /*expected*/ false, 4,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+ Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 4),
+ Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 4)));
+ check(null, /*default*/ 5,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+ /*expected*/ true, 5,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)));
+ check(null, /*default*/ 10,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
+ Pair.create(WORK_TYPE_BG, 1)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 1)),
+ /*expected*/ true, 10,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_EJ, 3),
+ Pair.create(WORK_TYPE_BG, 1)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 1)));
+ check(null, /*default*/ 15,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 15)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 15)),
+ /*expected*/ true, 15,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 14)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 15), Pair.create(WORK_TYPE_BG, 15)));
+ check(null, /*default*/ 16,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 16)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 16)),
+ /*expected*/ true, 16,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 15)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16)));
+ check(null, /*default*/ 20,
+ /* min */ List.of(
+ Pair.create(WORK_TYPE_BG, 20), Pair.create(WORK_TYPE_BGUSER, 10)),
+ /* max */ List.of(
+ Pair.create(WORK_TYPE_BG, 20), Pair.create(WORK_TYPE_BGUSER, 20)),
+ /*expected*/ false, 16,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1),
+ Pair.create(WORK_TYPE_BG, 15), Pair.create(WORK_TYPE_BGUSER, 0)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16),
+ Pair.create(WORK_TYPE_BG, 16), Pair.create(WORK_TYPE_BGUSER, 16)));
+ check(null, /*default*/ 20,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 16)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 16)),
+ /*expected*/ true, 16,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 15)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16)));
// Test for overriding with a setting string.
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
@@ -153,26 +228,66 @@
.setInt(KEY_MAX_BG, 4)
.setInt(KEY_MIN_BG, 3)
.build(),
- /*default*/ 9, null, Pair.create(9, 9),
- /*expected*/ true, 5, Pair.create(1, 5), Pair.create(3, 4));
+ /*default*/ 9,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /* max */ List.of(
+ Pair.create(WORK_TYPE_BG, 9), Pair.create(WORK_TYPE_BGUSER, 2)),
+ /*expected*/ true, 5,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 3)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5),
+ Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)));
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MAX_TOTAL, 5).build(),
- /*default*/ 9, null, Pair.create(9, 9),
- /*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5));
+ /*default*/ 9,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /*expected*/ true, 5,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 4)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 5)));
+
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MAX_BG, 4).build(),
- /*default*/ 9, null, Pair.create(9, 9),
- /*expected*/ true, 9, Pair.create(1, 9), Pair.create(4, 4));
+ /*default*/ 9,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /*expected*/ true, 9,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 4)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 9), Pair.create(WORK_TYPE_BG, 4)));
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MIN_BG, 3).build(),
- /*default*/ 9, null, Pair.create(9, 9),
- /*expected*/ true, 9, Pair.create(1, 9), Pair.create(3, 9));
+ /*default*/ 9,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /*expected*/ true, 9,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 3)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 9), Pair.create(WORK_TYPE_BG, 9)));
+ check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
+ .setInt(KEY_MAX_TOTAL, 20)
+ .setInt(KEY_MAX_EJ, 5)
+ .setInt(KEY_MIN_EJ, 2)
+ .setInt(KEY_MAX_BG, 16)
+ .setInt(KEY_MIN_BG, 8)
+ .build(),
+ /*default*/ 9,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /*expected*/ true, 16,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_EJ, 2),
+ Pair.create(WORK_TYPE_BG, 8)),
+ /* max */
+ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_EJ, 5),
+ Pair.create(WORK_TYPE_BG, 16)));
+
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
.setInt(KEY_MAX_TOTAL, 20)
.setInt(KEY_MAX_BG, 20)
.setInt(KEY_MIN_BG, 8)
.build(),
- /*default*/ 9, null, Pair.create(9, 9),
- /*expected*/ true, 16, Pair.create(1, 16), Pair.create(8, 16));
+ /*default*/ 9,
+ /* min */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /* max */ List.of(Pair.create(WORK_TYPE_BG, 9)),
+ /*expected*/ true, 16,
+ /* min */ List.of(Pair.create(WORK_TYPE_TOP, 1), Pair.create(WORK_TYPE_BG, 8)),
+ /* max */ List.of(Pair.create(WORK_TYPE_TOP, 16), Pair.create(WORK_TYPE_BG, 16)));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 58ba907..3ebe4ef 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -19,11 +19,13 @@
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
import static android.Manifest.permission.NETWORK_STACK;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
import static android.net.INetd.FIREWALL_RULE_ALLOW;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -112,8 +114,6 @@
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.net.NetworkState;
@@ -1985,13 +1985,6 @@
return users;
}
- private NetworkInfo buildNetworkInfo() {
- final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_LTE, null, null);
- ni.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
- return ni;
- }
-
private LinkProperties buildLinkProperties(String iface) {
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName(iface);
@@ -2045,13 +2038,12 @@
}
private static NetworkState buildWifi() {
- final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
- info.setDetailedState(DetailedState.CONNECTED, null, null);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+ networkCapabilities.addTransportType(TRANSPORT_WIFI);
networkCapabilities.setSSID(TEST_SSID);
- return new NetworkState(info, prop, networkCapabilities, null, null, TEST_SSID);
+ return new NetworkState(TYPE_WIFI, prop, networkCapabilities, null, null, TEST_SSID);
}
private void expectHasInternetPermission(int uid, boolean hasIt) throws Exception {
@@ -2072,7 +2064,7 @@
when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID)))
.thenReturn(mCarrierConfig);
when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] {
- new NetworkState(buildNetworkInfo(),
+ new NetworkState(TYPE_MOBILE,
buildLinkProperties(TEST_IFACE),
buildNetworkCapabilities(TEST_SUB_ID, roaming),
new Network(TEST_NET_ID), TEST_IMSI, null)
diff --git a/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java
new file mode 100644
index 0000000..764c504
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+import static com.android.server.devicepolicy.DpmTestUtils.assertRestrictions;
+import static com.android.server.devicepolicy.DpmTestUtils.newRestrictions;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.BundleUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest com.android.server.pm.BundleUtilsTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BundleUtilsTest {
+
+ @Test
+ public void testIsEmpty() {
+ assertThat(BundleUtils.isEmpty(null)).isTrue();
+ assertThat(BundleUtils.isEmpty(new Bundle())).isTrue();
+ assertThat(BundleUtils.isEmpty(newRestrictions("a"))).isFalse();
+ }
+
+ @Test
+ public void testClone() {
+ Bundle in = new Bundle();
+ Bundle out = BundleUtils.clone(in);
+ assertThat(in).isNotSameInstanceAs(out);
+ assertRestrictions(out, new Bundle());
+
+ out = BundleUtils.clone(null);
+ assertThat(out).isNotNull();
+ out.putBoolean("a", true); // Should not be Bundle.EMPTY.
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
index 9ba0967..7b9a00d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
@@ -71,12 +71,12 @@
@Before
public void setUp() {
mIncrementalStates = new IncrementalStates();
- assertFalse(mIncrementalStates.isStartable());
+ assertFalse(mIncrementalStates.getIncrementalStatesInfo().isStartable());
mIncrementalStates.setCallback(mCallback);
mIncrementalStates.onCommit(true);
// Test that package is now startable and loading
- assertTrue(mIncrementalStates.isStartable());
- assertTrue(mIncrementalStates.isLoading());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isLoading());
mUnstartableCalled.close();
mFullyLoadedCalled.close();
}
@@ -90,7 +90,7 @@
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
// Test that package is still startable
assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN, mUnstartableReason.get());
}
@@ -104,7 +104,7 @@
IStorageHealthListener.HEALTH_STATUS_READS_PENDING);
// Test that package is still startable
assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
}
/**
@@ -116,7 +116,7 @@
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_STORAGE);
// Test that package is still startable
assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN,
mUnstartableReason.get());
}
@@ -130,7 +130,7 @@
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT);
// Test that package is still startable
assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN,
mUnstartableReason.get());
}
@@ -145,12 +145,12 @@
mIncrementalStates.setProgress(1.0f);
// Test that package is now fully loaded
assertTrue(mFullyLoadedCalled.block(WAIT_TIMEOUT_MILLIS));
- assertFalse(mIncrementalStates.isLoading());
+ assertFalse(mIncrementalStates.getIncrementalStatesInfo().isLoading());
mIncrementalStates.onStorageHealthStatusChanged(
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
// Test that package is still startable
assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
}
/**
@@ -159,6 +159,6 @@
@Test
public void testStartableTransition_AppCrashOrAnr() {
mIncrementalStates.onCrashOrAnr();
- assertTrue(mIncrementalStates.isStartable());
+ assertTrue(mIncrementalStates.getIncrementalStatesInfo().isStartable());
}
}
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 e6a238a..ba60111 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 @@
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 938e4cc..7297622 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 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 @@
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/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
index e6c3d7c..b21b049 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
@@ -18,6 +18,7 @@
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.array;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertContains;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertHaveIds;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertSuccess;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
@@ -25,6 +26,8 @@
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.resultContains;
import android.content.ComponentName;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutManager;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -47,6 +50,9 @@
*/
@SmallTest
public class ShortcutManagerTest7 extends BaseShortcutManagerTest {
+
+ private static final int CACHE_OWNER = LauncherApps.FLAG_CACHE_NOTIFICATION_SHORTCUTS;
+
private List<String> callShellCommand(String... args) throws IOException, RemoteException {
// For reset to work, the current time needs to be incrementing.
@@ -215,11 +221,13 @@
// This command is deprecated. Will remove the test later.
public void testLauncherCommands() throws Exception {
+ prepareGetRoleHoldersAsUser(getSystemLauncher().activityInfo.packageName, USER_0);
prepareGetHomeActivitiesAsUser(
/* preferred */ getSystemLauncher().activityInfo.getComponentName(),
list(getSystemLauncher(), getFallbackLauncher()),
USER_0);
+ prepareGetRoleHoldersAsUser(CALLING_PACKAGE_2, USER_10);
prepareGetHomeActivitiesAsUser(
/* preferred */ cn(CALLING_PACKAGE_2, "name"),
list(getSystemLauncher(), getFallbackLauncher(),
@@ -241,6 +249,7 @@
"Launcher: ComponentInfo{com.android.test.2/name}");
// Change user-0's launcher.
+ prepareGetRoleHoldersAsUser(CALLING_PACKAGE_1, USER_0);
prepareGetHomeActivitiesAsUser(
/* preferred */ cn(CALLING_PACKAGE_1, "name"),
list(ri(CALLING_PACKAGE_1, "name", false, 0)),
@@ -323,6 +332,72 @@
});
}
+ public void testGetShortcuts() throws Exception {
+
+ mRunningUsers.put(USER_10, true);
+
+ // Add two manifests and two dynamics.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_10));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.addDynamicShortcuts(list(
+ makeLongLivedShortcut("s1"), makeShortcut("s2"))));
+ });
+ runWithCaller(LAUNCHER_1, USER_10, () -> {
+ mInjectCheckAccessShortcutsPermission = true;
+ mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_10,
+ CACHE_OWNER);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms2", "s2"), HANDLE_USER_10);
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertWith(getCallerShortcuts())
+ .haveIds("ms1", "ms2", "s1", "s2")
+ .areAllEnabled()
+
+ .selectPinned()
+ .haveIds("ms2", "s2");
+ });
+
+
+ mRunningUsers.put(USER_10, true);
+ mUnlockedUsers.put(USER_10, true);
+
+ mInjectedCallingUid = Process.SHELL_UID;
+
+ assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags",
+ Integer.toString(ShortcutManager.FLAG_MATCH_CACHED), CALLING_PACKAGE_1),
+ "s1");
+
+ assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags",
+ Integer.toString(ShortcutManager.FLAG_MATCH_DYNAMIC), CALLING_PACKAGE_1),
+ "s1", "s2");
+
+ assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags",
+ Integer.toString(ShortcutManager.FLAG_MATCH_MANIFEST), CALLING_PACKAGE_1),
+ "ms1", "ms2");
+
+ assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags",
+ Integer.toString(ShortcutManager.FLAG_MATCH_PINNED), CALLING_PACKAGE_1),
+ "ms2", "s2");
+
+ assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags",
+ Integer.toString(ShortcutManager.FLAG_MATCH_DYNAMIC
+ | ShortcutManager.FLAG_MATCH_PINNED), CALLING_PACKAGE_1),
+ "ms2", "s1", "s2");
+
+ assertHaveIds(callShellCommand("get-shortcuts", "--user", "10", "--flags",
+ Integer.toString(ShortcutManager.FLAG_MATCH_MANIFEST
+ | ShortcutManager.FLAG_MATCH_CACHED), CALLING_PACKAGE_1),
+ "ms1", "ms2", "s1");
+
+ }
+
public void testDumpsysArgs() {
checkDumpsysArgs(null, true, false, false);
checkDumpsysArgs(array("-u"), true, true, false);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index ee30f68..cd98d44 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -72,11 +72,18 @@
@Test
public void testUserTypeBuilder_createUserType() {
final Bundle restrictions = makeRestrictionsBundle("r1", "r2");
+ final Bundle systemSettings = makeSettingsBundle("s1", "s2");
+ final Bundle secureSettings = makeSettingsBundle("secure_s1", "secure_s2");
+ final List<DefaultCrossProfileIntentFilter> filters = List.of(
+ new DefaultCrossProfileIntentFilter.Builder(
+ DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+ /* flags= */0,
+ /* letsPersonalDataIntoProfile= */false).build());
final UserTypeDetails type = new UserTypeDetails.Builder()
.setName("a.name")
.setEnabled(true)
.setMaxAllowed(21)
- .setBaseType(FLAG_FULL)
+ .setBaseType(FLAG_PROFILE)
.setDefaultUserInfoPropertyFlags(FLAG_EPHEMERAL)
.setBadgeLabels(23, 24, 25)
.setBadgeColors(26, 27)
@@ -86,20 +93,45 @@
.setLabel(31)
.setMaxAllowedPerParent(32)
.setDefaultRestrictions(restrictions)
+ .setDefaultSystemSettings(systemSettings)
+ .setDefaultSecureSettings(secureSettings)
+ .setDefaultCrossProfileIntentFilters(filters)
.createUserTypeDetails();
assertEquals("a.name", type.getName());
assertTrue(type.isEnabled());
assertEquals(21, type.getMaxAllowed());
- assertEquals(FLAG_FULL | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags());
+ assertEquals(FLAG_PROFILE | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags());
assertEquals(28, type.getIconBadge());
assertEquals(29, type.getBadgePlain());
assertEquals(30, type.getBadgeNoBackground());
assertEquals(31, type.getLabel());
assertEquals(32, type.getMaxAllowedPerParent());
+
assertTrue(UserRestrictionsUtils.areEqual(restrictions, type.getDefaultRestrictions()));
assertNotSame(restrictions, type.getDefaultRestrictions());
+ assertNotSame(systemSettings, type.getDefaultSystemSettings());
+ assertEquals(systemSettings.size(), type.getDefaultSystemSettings().size());
+ for (String key : systemSettings.keySet()) {
+ assertEquals(
+ systemSettings.getString(key),
+ type.getDefaultSystemSettings().getString(key));
+ }
+
+ assertNotSame(secureSettings, type.getDefaultSecureSettings());
+ assertEquals(secureSettings.size(), type.getDefaultSecureSettings().size());
+ for (String key : secureSettings.keySet()) {
+ assertEquals(
+ secureSettings.getString(key),
+ type.getDefaultSecureSettings().getString(key));
+ }
+
+ assertNotSame(filters, type.getDefaultCrossProfileIntentFilters());
+ assertEquals(filters.size(), type.getDefaultCrossProfileIntentFilters().size());
+ for (int i = 0; i < filters.size(); i++) {
+ assertEquals(filters.get(i), type.getDefaultCrossProfileIntentFilters().get(i));
+ }
assertEquals(23, type.getBadgeLabel(0));
assertEquals(24, type.getBadgeLabel(1));
@@ -135,6 +167,9 @@
assertEquals(Resources.ID_NULL, type.getBadgeColor(0));
assertEquals(Resources.ID_NULL, type.getLabel());
assertTrue(type.getDefaultRestrictions().isEmpty());
+ assertTrue(type.getDefaultSystemSettings().isEmpty());
+ assertTrue(type.getDefaultSecureSettings().isEmpty());
+ assertTrue(type.getDefaultCrossProfileIntentFilters().isEmpty());
assertFalse(type.hasBadge());
}
@@ -416,4 +451,13 @@
}
return bundle;
}
+
+ /** Creates a Bundle of the given settings keys and puts true for the value. */
+ private static Bundle makeSettingsBundle(String ... settings) {
+ final Bundle bundle = new Bundle();
+ for (String setting : settings) {
+ bundle.putBoolean(setting, true);
+ }
+ return bundle;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index dc181a9..ddf0cd0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -48,23 +48,6 @@
assertSame(in, UserRestrictionsUtils.nonNull(in));
}
- public void testIsEmpty() {
- assertTrue(UserRestrictionsUtils.isEmpty(null));
- assertTrue(UserRestrictionsUtils.isEmpty(new Bundle()));
- assertFalse(UserRestrictionsUtils.isEmpty(newRestrictions("a")));
- }
-
- public void testClone() {
- Bundle in = new Bundle();
- Bundle out = UserRestrictionsUtils.clone(in);
- assertNotSame(in, out);
- assertRestrictions(out, new Bundle());
-
- out = UserRestrictionsUtils.clone(null);
- assertNotNull(out);
- out.putBoolean("a", true); // Should not be Bundle.EMPTY.
- }
-
public void testMerge() {
Bundle a = newRestrictions("a", "d");
Bundle b = newRestrictions("b", "d", "e");
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 61252c4..ff0aec7 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 @@
.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 0000000..70d85b6
--- /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 09c8142..9768f17 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 @@
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 d876b05..3ff8e76 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 @@
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.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;
@@ -229,9 +232,9 @@
.compose();
VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
- Thread.sleep(20);
+ assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), vibrationThread,
+ TEST_TIMEOUT_MILLIS));
assertTrue(vibrationThread.isAlive());
- assertTrue(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
// 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.
@@ -254,9 +257,9 @@
VibrationEffect effect = VibrationEffect.createWaveform(new long[]{100}, new int[]{100}, 0);
VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
- Thread.sleep(20);
+ assertTrue(waitUntil(t -> t.getVibrators().get(VIBRATOR_ID).isVibrating(), vibrationThread,
+ TEST_TIMEOUT_MILLIS));
assertTrue(vibrationThread.isAlive());
- assertTrue(vibrationThread.getVibrators().get(VIBRATOR_ID).isVibrating());
// 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.
@@ -388,6 +391,20 @@
}
@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 @@
}
@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 @@
}
@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);
@@ -621,10 +757,9 @@
.combine();
VibrationThread vibrationThread = startThreadAndDispatcher(vibrationId, effect);
- Thread.sleep(10);
+ assertTrue(waitUntil(t -> t.getVibrators().get(2).isVibrating(), vibrationThread,
+ TEST_TIMEOUT_MILLIS));
assertTrue(vibrationThread.isAlive());
- assertTrue(vibrationThread.getVibrators().get(1).isVibrating());
- assertTrue(vibrationThread.getVibrators().get(2).isVibrating());
// 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/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
new file mode 100644
index 0000000..f5d0ca7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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.wm;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.annotation.DurationMillisLong;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorManager;
+import android.hardware.input.InputSensorInfo;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.rotationresolver.RotationResolverInternal;
+import android.view.Surface;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link com.android.server.wm.WindowOrientationListener}
+ */
+public class WindowOrientationListenerTest {
+
+ @Mock
+ private Context mMockContext;
+ @Mock
+ private Handler mMockHandler;
+ @Mock
+ private InputSensorInfo mMockInputSensorInfo;
+ @Mock
+ private SensorManager mMockSensorManager;
+ @Mock
+ private WindowManagerService mMockWindowManagerService;
+
+ private TestableRotationResolver mFakeRotationResolverInternal;
+ private com.android.server.wm.WindowOrientationListener mWindowOrientationListener;
+ private int mFinalizedRotation;
+ private boolean mRotationResolverEnabled;
+ private boolean mCanUseRotationResolver;
+ private SensorEvent mFakeSensorEvent;
+ private Sensor mFakeSensor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mRotationResolverEnabled = true;
+ mCanUseRotationResolver = true;
+
+ mFakeRotationResolverInternal = new TestableRotationResolver();
+ doReturn(mMockSensorManager).when(mMockContext).getSystemService(Context.SENSOR_SERVICE);
+ mWindowOrientationListener = new TestableWindowOrientationListener(mMockContext,
+ mMockHandler, mMockWindowManagerService);
+ mWindowOrientationListener.mRotationResolverService = mFakeRotationResolverInternal;
+
+ mFakeSensor = new Sensor(mMockInputSensorInfo);
+ mFakeSensorEvent = new SensorEvent(mFakeSensor, /* accuracy */ 1, /* timestamp */ 1L,
+ new float[]{(float) Surface.ROTATION_90});
+ }
+
+ @Test
+ public void testOnSensorChanged_rotationResolverDisabled_useSensorResult() {
+ mRotationResolverEnabled = false;
+
+ mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
+
+ assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_90);
+ }
+
+ @Test
+ public void testOnSensorChanged_cannotUseRotationResolver_useSensorResult() {
+ mCanUseRotationResolver = false;
+
+ mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
+
+ assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_90);
+
+ }
+
+ @Test
+ public void testOnSensorChanged_normalCase() {
+ mFakeRotationResolverInternal.mResult = Surface.ROTATION_180;
+
+ mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent);
+
+ assertThat(mFinalizedRotation).isEqualTo(Surface.ROTATION_180);
+ }
+
+ final class TestableRotationResolver extends RotationResolverInternal {
+ @Surface.Rotation
+ int mResult;
+
+ @Override
+ public boolean isRotationResolverSupported() {
+ return true;
+ }
+
+ @Override
+ public void resolveRotation(@NonNull RotationResolverCallbackInternal callback,
+ @Surface.Rotation int proposedRotation, @Surface.Rotation int currentRotation,
+ @DurationMillisLong long timeoutMillis,
+ @NonNull CancellationSignal cancellationSignal) {
+ callback.onSuccess(mResult);
+ }
+ }
+
+ final class TestableWindowOrientationListener extends WindowOrientationListener {
+
+ TestableWindowOrientationListener(Context context, Handler handler,
+ WindowManagerService service) {
+ super(context, handler, service);
+ this.mOrientationJudge = new OrientationSensorJudge();
+ }
+
+ @Override
+ public void onProposedRotationChanged(int rotation) {
+ mFinalizedRotation = rotation;
+ }
+
+ @Override
+ public boolean canUseRotationResolver() {
+ return mCanUseRotationResolver;
+ }
+
+ @Override
+ public boolean isRotationResolverEnabled() {
+ return mRotationResolverEnabled;
+ }
+ }
+}
diff --git a/services/tests/shortcutmanagerutils/Android.bp b/services/tests/shortcutmanagerutils/Android.bp
index c2cb6881..35ca354 100644
--- a/services/tests/shortcutmanagerutils/Android.bp
+++ b/services/tests/shortcutmanagerutils/Android.bp
@@ -22,5 +22,9 @@
"android.test.runner.stubs",
],
+ static_libs: [
+ "compatibility-device-util-axt",
+ ],
+
sdk_version: "test_current",
}
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 907f887..5182b3b 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -15,6 +15,8 @@
*/
package com.android.server.pm.shortcutmanagertest;
+import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -34,6 +36,7 @@
import static org.mockito.Mockito.verify;
import android.app.Instrumentation;
+import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.LocusId;
@@ -50,6 +53,7 @@
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.UserHandle;
+import android.os.UserManager;
import android.test.MoreAsserts;
import android.util.Log;
@@ -136,6 +140,20 @@
return sb.toString();
}
+ public static List<String> extractShortcutIds(List<String> result) {
+ final String prefix = "ShortcutInfo {id=";
+ final String postfix = ", ";
+
+ List<String> ids = new ArrayList<>();
+ for (String line : result) {
+ if (line.contains(prefix)) {
+ ids.add(line.substring(
+ line.indexOf(prefix) + prefix.length(), line.indexOf(postfix)));
+ }
+ }
+ return ids;
+ }
+
public static boolean resultContains(List<String> result, String expected) {
for (String line : result) {
if (line.contains(expected)) {
@@ -160,6 +178,16 @@
return result;
}
+ public static List<String> assertHaveIds(List<String> result, String... expectedIds) {
+ assertSuccess(result);
+
+ final SortedSet<String> expected = new TreeSet<>(list(expectedIds));
+ final SortedSet<String> actual = new TreeSet<>(extractShortcutIds(result));
+ assertEquals(expected, actual);
+
+ return result;
+ }
+
public static List<String> runCommand(Instrumentation instrumentation, String command) {
return runCommand(instrumentation, command, null);
}
@@ -193,30 +221,60 @@
return runShortcutCommand(instrumentation, command, result -> result.contains("Success"));
}
- public static String getDefaultLauncher(Instrumentation instrumentation) {
- final String PREFIX = "Launcher: ComponentInfo{";
- final String POSTFIX = "}";
- final List<String> result = runShortcutCommandForSuccess(
- instrumentation, "get-default-launcher --user "
- + instrumentation.getContext().getUserId());
- for (String s : result) {
- if (s.startsWith(PREFIX) && s.endsWith(POSTFIX)) {
- return s.substring(PREFIX.length(), s.length() - POSTFIX.length());
+ private static UserHandle getParentUser(Context context) {
+ final UserHandle user = context.getUser();
+ final UserManager userManager = context.getSystemService(UserManager.class);
+ if (!userManager.isManagedProfile(user.getIdentifier())) {
+ return user;
+ }
+
+ final List<UserHandle> profiles = userManager.getUserProfiles();
+ for (UserHandle handle : profiles) {
+ if (!userManager.isManagedProfile(handle.getIdentifier())) {
+ return handle;
}
}
- fail("Default launcher not found");
return null;
}
- public static void setDefaultLauncher(Instrumentation instrumentation, String component) {
- runCommand(instrumentation, "cmd package set-home-activity --user "
- + instrumentation.getContext().getUserId() + " " + component,
- result -> result.contains("Success"));
+ public static String getDefaultLauncher(Instrumentation instrumentation) throws Exception {
+ final Context context = instrumentation.getContext();
+ final RoleManager roleManager = context.getSystemService(RoleManager.class);
+ final UserHandle user = getParentUser(context);
+ List<String> roleHolders = callWithShellPermissionIdentity(
+ () -> roleManager.getRoleHoldersAsUser(RoleManager.ROLE_HOME, user));
+ if (roleHolders.size() == 1) {
+ return roleHolders.get(0);
+ }
+ fail("Failed to get the default launcher for user " + context.getUserId());
+ return null;
+ }
+
+ public static void setDefaultLauncher(Instrumentation instrumentation, String packageName) {
+ runCommandForNoOutput(instrumentation, "cmd role add-role-holder --user "
+ + instrumentation.getContext().getUserId() + " " + RoleManager.ROLE_HOME + " "
+ + packageName + " 0");
+ waitUntil("Failed to get shortcut access",
+ () -> hasShortcutAccess(instrumentation, packageName), 20);
}
public static void setDefaultLauncher(Instrumentation instrumentation, Context packageContext) {
- setDefaultLauncher(instrumentation, packageContext.getPackageName()
- + "/android.content.pm.cts.shortcutmanager.packages.Launcher");
+ setDefaultLauncher(instrumentation, packageContext.getPackageName());
+ }
+
+ public static boolean hasShortcutAccess(Instrumentation instrumentation, String packageName) {
+ final List<String> result = runShortcutCommandForSuccess(instrumentation,
+ "has-shortcut-access --user " + instrumentation.getContext().getUserId()
+ + " " + packageName);
+ for (String s : result) {
+ if (s.startsWith("true")) {
+ return true;
+ } else if (s.startsWith("false")) {
+ return false;
+ }
+ }
+ fail("Failed to check shortcut access");
+ return false;
}
public static void overrideConfig(Instrumentation instrumentation, String config) {
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index e5646db..1dd4212 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -60,6 +60,6 @@
"libui",
"libunwindstack",
"libutils",
- "netd_aidl_interface-cpp",
+ "netd_aidl_interface-V5-cpp",
],
}
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 b3116d9..17c9411 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 @@
}
@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/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index cf977b4..ddf284401 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -51,7 +51,7 @@
],
libs: [
- "android.hardware.power-java",
+ "android.hardware.power-V1-java",
"android.test.mock",
"android.test.base",
"android.test.runner",
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 6b69ee9..002859e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -96,6 +96,8 @@
.setTask(mTrampolineActivity.getTask())
.setComponent(createRelative(DEFAULT_COMPONENT_PACKAGE_NAME, "TopActivity"))
.build();
+ // becomes invisible when covered by mTopActivity
+ mTrampolineActivity.mVisibleRequested = false;
}
@After
@@ -230,7 +232,6 @@
@Test
public void testOnActivityLaunchCancelled_finishedBeforeDrawn() {
- mTopActivity.mVisibleRequested = true;
doReturn(true).when(mTopActivity).isReportedDrawn();
// Create an activity with different process that meets process switch.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 42b080e..72b8439 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -679,8 +679,10 @@
new RemoteAnimationAdapter(new Stub() {
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) {
}
@@ -2178,6 +2180,7 @@
activity.setOccludesParent(true);
activity.setVisible(false);
+ activity.mVisibleRequested = false;
// Can not specify orientation if app isn't visible even though it occludes parent.
assertEquals(SCREEN_ORIENTATION_UNSET, activity.getOrientation());
// Can specify orientation if the current orientation candidate is orientation behind.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 22ee886..3a7954b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -1424,6 +1424,9 @@
final ActivityRecord nonTopVisibleActivity =
new ActivityBuilder(mAtm).setTask(task).build();
new ActivityBuilder(mAtm).setTask(task).build();
+ // The scenario we are testing is when the app isn't visible yet.
+ nonTopVisibleActivity.setVisible(false);
+ nonTopVisibleActivity.mVisibleRequested = false;
doReturn(false).when(nonTopVisibleActivity).attachedToProcess();
doReturn(true).when(nonTopVisibleActivity).shouldBeVisibleUnchecked();
doNothing().when(mSupervisor).startSpecificActivity(any(), anyBoolean(),
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 36adf28..37bc23e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -844,6 +844,9 @@
final ActivityRecord singleTaskActivity = createSingleTaskActivityOn(
secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, false /* onTop */));
+ // Activity should start invisible since we are bringing it to front.
+ singleTaskActivity.setVisible(false);
+ singleTaskActivity.mVisibleRequested = false;
// Create another activity on top of the secondary display.
final Task topStack = secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN,
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 91b9449..71f1914 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -36,6 +36,7 @@
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -70,8 +71,10 @@
class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) {
for (RemoteAnimationTarget target : apps) {
assertNotNull(target.startBounds);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index f1e3609..83aca5e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -265,8 +265,10 @@
private class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
boolean mCancelled = false;
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index 2f4c8e2..d13e4dc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -15,7 +15,7 @@
*/
package com.android.server.wm;
-import static android.os.Process.INVALID_UID;
+
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -587,7 +587,7 @@
final WindowToken token = new WindowToken(mWms, mock(IBinder.class),
TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
false /* fromClientToken */, null /* options */);
policy.addWindow(token);
@@ -621,11 +621,11 @@
final WindowToken token1 = new WindowToken(mWms, mock(IBinder.class),
TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
false /* fromClientToken */, null /* options */);
final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class),
TYPE_WALLPAPER, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
false /* fromClientToken */, null /* options */);
policy.addWindow(token1);
policy.addWindow(token2);
@@ -672,15 +672,15 @@
options2.putInt("HIERARCHY_ROOT_ID", mGroupRoot2.mFeatureId);
final WindowToken token0 = new WindowToken(mWms, mock(IBinder.class),
TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
false /* fromClientToken */, null /* options */);
final WindowToken token1 = new WindowToken(mWms, mock(IBinder.class),
TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
false /* fromClientToken */, options1);
final WindowToken token2 = new WindowToken(mWms, mock(IBinder.class),
TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, INVALID_UID, false /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, false /* roundedCornerOverlay */,
false /* fromClientToken */, options2);
policy.addWindow(token0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 4bea9a2..b4fd302 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1379,7 +1379,7 @@
}
@Test
- public void testNoFixedRotationWithPip() {
+ public void testFixedRotationWithPip() {
final DisplayContent displayContent = mDefaultDisplay;
unblockDisplayRotation(displayContent);
// Make resume-top really update the activity state.
@@ -1406,15 +1406,20 @@
assertEquals(homeConfigOrientation, displayConfig.orientation);
clearInvocations(mWm);
- // Leave PiP to fullscreen. The orientation can be updated from
- // ActivityRecord#reportDescendantOrientationChangeIfNeeded.
- pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ // Leave PiP to fullscreen. Simulate the step of PipTaskOrganizer that sets the activity
+ // to fullscreen, so fixed rotation will apply on it.
+ pinnedActivity.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
homeActivity.setState(Task.ActivityState.STOPPED, "test");
- assertFalse(displayContent.hasTopFixedRotationLaunchingApp());
- verify(mWm, atLeastOnce()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
- assertEquals(pinnedConfigOrientation, displayConfig.orientation);
+ assertTrue(displayContent.hasTopFixedRotationLaunchingApp());
+ verify(mWm, never()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
+ assertNotEquals(pinnedConfigOrientation, displayConfig.orientation);
+
+ // Assume the animation of PipTaskOrganizer is done and then commit fullscreen to task.
+ pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ displayContent.continueUpdateOrientationForDiffOrienLaunchingApp();
assertFalse(displayContent.getPinnedStackController().isPipActiveOrWindowingModeChanging());
+ assertEquals(pinnedConfigOrientation, displayConfig.orientation);
clearInvocations(mWm);
// Enter PiP from fullscreen. The orientation can be updated from
@@ -1608,6 +1613,16 @@
}
@Test
+ public void testGetOrCreateRootHomeTask_dontMoveToTop() {
+ DisplayContent display = createNewDisplay();
+ display.mDontMoveToTop = true;
+ TaskDisplayArea taskDisplayArea = display.getDefaultTaskDisplayArea();
+
+ assertNull(taskDisplayArea.getRootHomeTask());
+ assertNull(taskDisplayArea.getOrCreateRootHomeTask());
+ }
+
+ @Test
public void testValidWindowingLayer() {
final SurfaceControl windowingLayer = mDisplayContent.getWindowingLayer();
assertNotNull(windowingLayer);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
index 33e8fc0..3e05c86 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
@@ -217,6 +217,7 @@
SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo);
overrideSettings.mShouldShowSystemDecors = true;
overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL;
+ overrideSettings.mDontMoveToTop = true;
provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings);
assertTrue(mOverrideSettingsStorage.wasWriteSuccessful());
@@ -227,6 +228,8 @@
getStoredDisplayAttributeValue(mOverrideSettingsStorage, "shouldShowSystemDecors"));
assertEquals("Attribute value must be stored", "0",
getStoredDisplayAttributeValue(mOverrideSettingsStorage, "imePolicy"));
+ assertEquals("Attribute value must be stored", "true",
+ getStoredDisplayAttributeValue(mOverrideSettingsStorage, "dontMoveToTop"));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index fa3e3ae..7714a6c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -183,6 +183,10 @@
mColor = Color.GREEN;
assertTrue(mLetterbox.needsApplySurfaceChanges());
+
+ mLetterbox.applySurfaceChanges(mTransaction);
+
+ verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 1, 0});
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 2efd4b5..15e045c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -17,6 +17,9 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -100,15 +103,18 @@
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonApsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonApsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -136,7 +142,7 @@
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
adapter.onAnimationCancelled(mMockLeash);
verify(mMockRunner).onAnimationCancelled();
@@ -149,7 +155,7 @@
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mClock.fastForward(2500);
mHandler.timeAdvance();
@@ -170,7 +176,7 @@
null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mClock.fastForward(2500);
mHandler.timeAdvance();
@@ -190,7 +196,7 @@
@Test
public void testZeroAnimations() {
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_NONE);
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
}
@@ -199,7 +205,7 @@
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
mController.createRemoteAnimationRecord(win.mActivityRecord,
new Point(50, 100), null, new Rect(50, 100, 150, 150), null);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
}
@@ -213,15 +219,18 @@
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
assertEquals(mMockLeash, appsCaptor.getValue()[0].leash);
@@ -235,7 +244,7 @@
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
win.mActivityRecord.removeImmediately();
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
eq(adapter));
@@ -255,15 +264,18 @@
mFinishedCallback);
((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -305,15 +317,18 @@
mFinishedCallback);
((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, appsCaptor.getValue().length);
final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -354,15 +369,18 @@
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, wallpapersCaptor.getValue().length);
} finally {
@@ -383,15 +401,18 @@
new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
mFinishedCallback);
- mController.goodToGo();
+ mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+ final ArgumentCaptor<RemoteAnimationTarget[]> nonAPpsCaptor =
+ ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
- verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+ verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+ appsCaptor.capture(), wallpapersCaptor.capture(), nonAPpsCaptor.capture(),
finishedCaptor.capture());
assertEquals(1, wallpapersCaptor.getValue().length);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 6f775cf..cc4d4ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -39,8 +39,6 @@
import static com.android.server.wm.Task.ActivityState.STOPPED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
-import static com.google.common.truth.Truth.assertThat;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -119,13 +117,13 @@
@Test
public void testKeepBoundsWhenChangingFromFreeformToFullscreen() {
removeGlobalMinSizeRestriction();
- // Create landscape freeform display and a freeform app.
+ // create freeform display and a freeform app
DisplayContent display = new TestDisplayContent.Builder(mAtm, 2000, 1000)
.setCanRotate(false)
.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM).build();
setUpApp(display);
- // Put app window into portrait freeform and then make it a compat app.
+ // Put app window into freeform and then make it a compat app.
final Rect bounds = new Rect(100, 100, 400, 600);
mTask.setBounds(bounds);
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
@@ -138,7 +136,7 @@
final int density = mActivity.getConfiguration().densityDpi;
- // Change display configuration to fullscreen.
+ // change display configuration to fullscreen
Configuration c = new Configuration(display.getRequestedOverrideConfiguration());
c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
display.onRequestedOverrideConfigurationChanged(c);
@@ -148,8 +146,6 @@
assertEquals(bounds.width(), mActivity.getBounds().width());
assertEquals(bounds.height(), mActivity.getBounds().height());
assertEquals(density, mActivity.getConfiguration().densityDpi);
- // Size compat mode is sandboxed at the activity level.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -175,12 +171,6 @@
assertEquals(appBounds.height(), appBounds.width() * aspectRatio, 0.5f /* delta */);
// The decor height should be a part of the effective bounds.
assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight);
- // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
- assertActivityMaxBoundsSandboxedForLetterbox();
- // Activity max bounds ignore notch, since an app can be shown past the notch (although app
- // is currently limited by the notch).
- assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
- .isEqualTo(displayBounds.height());
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
assertFitted();
@@ -190,17 +180,9 @@
assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */);
// The notch is no longer on top.
assertEquals(appBounds, mActivity.getBounds());
- // Activity max bounds are sandboxed.
- assertActivityMaxBoundsSandboxedForLetterbox();
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
- // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
- assertActivityMaxBoundsSandboxedForLetterbox();
- // Activity max bounds ignore notch, since an app can be shown past the notch (although app
- // is currently limited by the notch).
- assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
- .isEqualTo(displayBounds.height());
}
@Test
@@ -228,9 +210,6 @@
assertEquals(originalBounds.width(), mActivity.getBounds().width());
assertEquals(originalBounds.height(), mActivity.getBounds().height());
assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
- // Activity is sandboxed; it is in size compat mode since it is not resizable and has a
- // max aspect ratio.
- assertActivityMaxBoundsSandboxedForSizeCompat();
assertScaled();
}
@@ -238,13 +217,11 @@
public void testFixedScreenBoundsWhenDisplaySizeChanged() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
- final DisplayContent display = mActivity.mDisplayContent;
assertFitted();
- // Activity and task inherit bounds from TaskDisplayArea, since not sandboxed.
- assertMaxBoundsInheritDisplayAreaBounds();
final Rect origBounds = new Rect(mActivity.getBounds());
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
+ final DisplayContent display = mActivity.mDisplayContent;
// Change the size of current display.
resizeDisplay(display, 1000, 2000);
@@ -261,8 +238,6 @@
// The position of configuration bounds should be the same as compat bounds.
assertEquals(mActivity.getBounds().left, currentBounds.left);
assertEquals(mActivity.getBounds().top, currentBounds.top);
- // Activity is sandboxed to the offset size compat bounds.
- assertActivityMaxBoundsSandboxedForSizeCompat();
// Change display size to a different orientation
resizeDisplay(display, 2000, 1000);
@@ -271,8 +246,6 @@
assertEquals(origBounds.height(), currentBounds.height());
assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation);
- // Activity is sandboxed to the offset size compat bounds.
- assertActivityMaxBoundsSandboxedForSizeCompat();
// The previous resize operation doesn't consider the rotation change after size changed.
// These setups apply the requested orientation to rotation as real case that the top fixed
@@ -292,8 +265,6 @@
assertEquals(origBounds.height(), currentBounds.height());
assertEquals(offsetX, currentBounds.left);
assertScaled();
- // Activity is sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -309,8 +280,6 @@
assertEquals(bounds.width(), bounds.height() * maxAspect, 0.0001f /* delta */);
// The position should be horizontal centered.
assertEquals((displayWidth - bounds.width()) / 2, bounds.left);
- // Activity max bounds should be sandboxed since it is letterboxed.
- assertActivityMaxBoundsSandboxedForLetterbox();
mActivity.mDisplayContent.setImeLayeringTarget(addWindowToActivity(mActivity));
// Make sure IME cannot attach to the app, otherwise IME window will also be shifted.
@@ -322,8 +291,6 @@
// It should keep non-attachable because the resolved bounds will be computed according to
// the aspect ratio that won't match its parent bounds.
assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
- // Activity max bounds should be sandboxed since it is letterboxed.
- assertActivityMaxBoundsSandboxedForLetterbox();
}
@Test
@@ -349,13 +316,14 @@
}
@Test
- public void testMoveToDifferentOrientationDisplay() {
+ public void testMoveToDifferentOrientDisplay() {
setUpDisplaySizeWithApp(1000, 2500);
prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
assertFitted();
- final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
- final Rect originalBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
+ final Rect configBounds = mActivity.getWindowConfiguration().getBounds();
+ final int origWidth = configBounds.width();
+ final int origHeight = configBounds.height();
final int notchHeight = 100;
final DisplayContent newDisplay = new TestDisplayContent.Builder(mAtm, 2000, 1000)
@@ -364,44 +332,37 @@
// Move the non-resizable activity to the new display.
mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
// The configuration bounds [820, 0 - 1820, 2500] should keep the same.
- assertEquals(originalBounds.width(), currentBounds.width());
- assertEquals(originalBounds.height(), currentBounds.height());
+ assertEquals(origWidth, configBounds.width());
+ assertEquals(origHeight, configBounds.height());
assertScaled();
- // Activity max bounds are sandboxed due to size compat mode on the new display.
- assertActivityMaxBoundsSandboxedForSizeCompat();
final Rect newDisplayBounds = newDisplay.getWindowConfiguration().getBounds();
// The scaled bounds should exclude notch area (1000 - 100 == 360 * 2500 / 1000 = 900).
assertEquals(newDisplayBounds.height() - notchHeight,
- (int) ((float) mActivity.getBounds().width() * originalBounds.height()
- / originalBounds.width()));
+ (int) ((float) mActivity.getBounds().width() * origHeight / origWidth));
// Recompute the natural configuration in the new display.
mActivity.clearSizeCompatMode();
mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
// Because the display cannot rotate, the portrait activity will fit the short side of
// display with keeping portrait bounds [200, 0 - 700, 1000] in center.
- assertEquals(newDisplayBounds.height(), currentBounds.height());
- assertEquals(currentBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
- currentBounds.width());
+ assertEquals(newDisplayBounds.height(), configBounds.height());
+ assertEquals(configBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
+ configBounds.width());
assertFitted();
// The appBounds should be [200, 100 - 700, 1000].
final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
- assertEquals(currentBounds.width(), appBounds.width());
- assertEquals(currentBounds.height() - notchHeight, appBounds.height());
- // Task max bounds are sandboxed due to letterboxing from orientation mismatch with display.
- assertTaskMaxBoundsSandboxed();
+ assertEquals(configBounds.width(), appBounds.width());
+ assertEquals(configBounds.height() - notchHeight, appBounds.height());
}
@Test
- public void testFixedOrientationRotateCutoutDisplay() {
+ public void testFixedOrientRotateCutoutDisplay() {
// Create a display with a notch/cutout
final int notchHeight = 60;
- final int width = 1000;
- setUpApp(new TestDisplayContent.Builder(mAtm, width, 2500)
+ setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2500)
.setNotch(notchHeight).build());
- // Bounds=[0, 0 - 1000, 1400], AppBounds=[0, 60 - 1000, 1460].
- final float maxAspect = 1.4f;
+ // Bounds=[0, 0 - 1000, 1460], AppBounds=[0, 60 - 1000, 1460].
prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
@@ -409,11 +370,6 @@
final Rect origBounds = new Rect(currentBounds);
final Rect origAppBounds = new Rect(appBounds);
- // Activity is sandboxed, and bounds include the area consumed by the notch.
- assertActivityMaxBoundsSandboxedForLetterbox();
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds().height())
- .isEqualTo(Math.round(width * maxAspect) + notchHeight);
-
// Although the activity is fixed orientation, force rotate the display.
rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation());
@@ -429,13 +385,10 @@
// The position in configuration should be global coordinates.
assertEquals(mActivity.getBounds().left, currentBounds.left);
assertEquals(mActivity.getBounds().top, currentBounds.top);
-
- // Activity max bounds are sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
- public void testFixedAspectRatioOrientationChangeOrientation() {
+ public void testFixedAspOrientChangeOrient() {
setUpDisplaySizeWithApp(1000, 2500);
final float maxAspect = 1.4f;
@@ -447,8 +400,6 @@
final Rect originalAppBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
assertEquals((int) (originalBounds.width() * maxAspect), originalBounds.height());
- // Activity is sandboxed due to fixed aspect ratio.
- assertActivityMaxBoundsSandboxedForLetterbox();
// Change the fixed orientation.
mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -460,8 +411,6 @@
mActivity.getWindowConfiguration().getAppBounds().height());
assertEquals(originalAppBounds.height(),
mActivity.getWindowConfiguration().getAppBounds().width());
- // Activity is sandboxed due to fixed aspect ratio.
- assertActivityMaxBoundsSandboxedForLetterbox();
}
@Test
@@ -510,8 +459,6 @@
// restarted and the override configuration won't be cleared.
verify(mActivity, never()).restartProcessIfVisible();
assertScaled();
- // Activity max bounds are sandboxed due to size compat mode, even if is not visible.
- assertActivityMaxBoundsSandboxedForSizeCompat();
// Change display density
display.mBaseDisplayDensity = (int) (0.7f * display.mBaseDisplayDensity);
@@ -586,16 +533,12 @@
// in multi-window mode.
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
assertFalse(activity.shouldUseSizeCompatMode());
- // Activity and task should not be sandboxed.
- assertMaxBoundsInheritDisplayAreaBounds();
// The non-resizable activity should not be size compat because the display support
// changing windowing mode from fullscreen to freeform.
mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
assertFalse(activity.shouldUseSizeCompatMode());
- // Activity and task should not be sandboxed.
- assertMaxBoundsInheritDisplayAreaBounds();
}
@Test
@@ -659,9 +602,6 @@
// be transparent.
assertFalse(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
- // Activity is sandboxed.
- assertActivityMaxBoundsSandboxedForLetterbox();
-
// Make the activity fill the display.
prepareUnresizable(mActivity, 10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
@@ -671,7 +611,6 @@
// The letterbox should only cover the notch area, so status bar can be transparent.
assertEquals(new Rect(notchHeight, 0, 0, 0), mActivity.getLetterboxInsets());
assertTrue(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
- assertActivityMaxBoundsSandboxedForLetterbox();
}
@Test
@@ -696,8 +635,6 @@
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
assertEquals(taskBounds, activityBounds);
- // Activity inherits max bounds from task, since sandboxing applied to task.
- assertTaskMaxBoundsSandboxed();
// Task bounds should be 700x1400 with the ratio as the display.
assertEquals(displayBounds.height(), taskBounds.height());
@@ -728,8 +665,6 @@
assertScaled();
assertEquals(activityBounds.width(), newActivityBounds.width());
assertEquals(activityBounds.height(), newActivityBounds.height());
- // Activity max bounds are sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -741,30 +676,29 @@
// Portrait fixed app without max aspect.
prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
+ Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ Rect activityBounds = new Rect(mActivity.getBounds());
+
// App should launch in fullscreen.
assertFalse(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- // Activity and task inherit max bounds from TaskDisplayArea.
- assertMaxBoundsInheritDisplayAreaBounds();
+ assertEquals(displayBounds, activityBounds);
// Rotate display to landscape.
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
- final Rect rotatedDisplayBounds = new Rect(mActivity.mDisplayContent.getBounds());
- final Rect rotatedActivityBounds = new Rect(mActivity.getBounds());
- assertTrue(rotatedDisplayBounds.width() > rotatedDisplayBounds.height());
+ displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+ activityBounds = new Rect(mActivity.getBounds());
+ assertTrue(displayBounds.width() > displayBounds.height());
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
- assertThat(mActivity.inSizeCompatMode()).isTrue();
- // Activity max bounds are sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
// App bounds should be 700x1400 with the ratio as the display.
- assertEquals(rotatedDisplayBounds.height(), rotatedActivityBounds.height());
- assertEquals(rotatedDisplayBounds.height() * rotatedDisplayBounds.height()
- / rotatedDisplayBounds.width(), rotatedActivityBounds.width());
+ assertEquals(displayBounds.height(), activityBounds.height());
+ assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+ activityBounds.width());
}
@Test
@@ -797,17 +731,14 @@
final Rect displayBounds = new Rect(display.getBounds());
final Rect taskBounds = new Rect(mTask.getBounds());
final Rect newActivityBounds = new Rect(newActivity.getBounds());
- final float displayAspectRatio = (float) displayBounds.height() / displayBounds.width();
// Task and app bounds should be 700x1400 with the ratio as the display.
assertTrue(mTask.isTaskLetterboxed());
assertFalse(newActivity.inSizeCompatMode());
assertEquals(taskBounds, newActivityBounds);
assertEquals(displayBounds.height(), taskBounds.height());
- assertThat(taskBounds.width())
- .isEqualTo(Math.round(displayBounds.height() * displayAspectRatio));
- // Task max bounds are sandboxed due to letterbox, with the ratio of the display.
- assertTaskMaxBoundsSandboxed();
+ assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+ taskBounds.width());
}
@Test
@@ -847,14 +778,6 @@
assertEquals(displayBounds.height(), taskBounds.height());
assertEquals((long) Math.rint(taskBounds.height() / newActivity.info.maxAspectRatio),
taskBounds.width());
- // New activity max bounds are sandboxed due to letterbox.
- assertThat(newActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(taskBounds);
- // Task max bounds are sandboxed due to letterbox, with the ratio of the display.
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().height())
- .isEqualTo(displayBounds.height());
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().width())
- .isEqualTo(Math.round(displayBounds.height() / newActivity.info.maxAspectRatio));
// App bounds should be fullscreen in Task bounds.
assertFalse(newActivity.inSizeCompatMode());
@@ -883,9 +806,6 @@
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
- assertThat(mActivity.inSizeCompatMode()).isTrue();
- // Activity max bounds are sandboxed due to size compat mode.
- assertActivityMaxBoundsSandboxedForSizeCompat();
final Rect activityBounds = new Rect(mActivity.getBounds());
mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
@@ -896,8 +816,6 @@
assertScaled();
assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
assertEquals(activityBounds, mActivity.getBounds());
- // Activity max bounds are sandboxed due to size compat.
- assertActivityMaxBoundsSandboxedForSizeCompat();
}
@Test
@@ -913,7 +831,6 @@
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- assertTaskMaxBoundsSandboxed();
// Rotate display to portrait.
rotateDisplay(display, ROTATION_90);
@@ -921,7 +838,6 @@
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
- assertActivityMaxBoundsSandboxedForSizeCompat();
// Rotate display to landscape.
rotateDisplay(display, ROTATION_180);
@@ -929,7 +845,6 @@
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- assertTaskMaxBoundsSandboxed();
}
@Test
@@ -947,26 +862,20 @@
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- // Task is letterboxed due to mismatched orientation request.
- assertTaskMaxBoundsSandboxed();
- // Rotate display to landscape.
+ // Rotate display to portrait.
rotateDisplay(display, ROTATION_90);
// App should be in size compat.
assertFalse(mTask.isTaskLetterboxed());
assertScaled();
- // Activity max bounds are sandboxed due to unresizable app.
- assertActivityMaxBoundsSandboxedForSizeCompat();
- // Rotate display to portrait.
+ // Rotate display to landscape.
rotateDisplay(display, ROTATION_180);
// In Task letterbox
assertTrue(mTask.isTaskLetterboxed());
assertFalse(mActivity.inSizeCompatMode());
- // Task is letterboxed, as in first case.
- assertTaskMaxBoundsSandboxed();
}
@Test
@@ -983,18 +892,12 @@
assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
assertEquals(2800, displayBounds.width());
assertEquals(1400, displayBounds.height());
- Rect displayAreaBounds = new Rect(0, 0, 2400, 1000);
- taskDisplayArea.setBounds(displayAreaBounds);
+ taskDisplayArea.setBounds(0, 0, 2400, 1000);
final Rect activityBounds = new Rect(mActivity.getBounds());
assertFalse(mActivity.inSizeCompatMode());
assertEquals(2400, activityBounds.width());
assertEquals(1000, activityBounds.height());
- // Task and activity maximum bounds inherit from TaskDisplayArea bounds.
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(displayAreaBounds);
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(displayAreaBounds);
}
@Test
@@ -1142,48 +1045,6 @@
assertFalse(mActivity.hasSizeCompatBounds());
}
- /** Asserts both the activity and task max bounds inherit from the TaskDisplayArea. */
- private void assertMaxBoundsInheritDisplayAreaBounds() {
- final Rect taskDisplayAreaBounds = mTask.getDisplayArea().getBounds();
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(taskDisplayAreaBounds);
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(taskDisplayAreaBounds);
- }
-
- /**
- * Asserts task-level letterboxing, so both activity and task max bounds
- * are sandboxed to the letterbox bounds.
- */
- private void assertTaskMaxBoundsSandboxed() {
- // Activity inherits max bounds from task, since sandboxing applied to task.
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mTask.getBounds());
- // Task max bounds are sandboxed due to letterbox.
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mTask.getBounds());
- }
-
- /** Asserts activity-level size compat mode, so only activity max bounds are sandboxed. */
- private void assertActivityMaxBoundsSandboxedForSizeCompat() {
- // Activity max bounds are sandboxed due to size compat mode.
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mActivity.getWindowConfiguration().getBounds());
- // Task inherits max bounds from display.
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mTask.getDisplayContent().getBounds());
- }
-
- /** Asserts activity-level letterboxing, so only activity max bounds are sandboxed. */
- private void assertActivityMaxBoundsSandboxedForLetterbox() {
- // Activity is sandboxed due to fixed aspect ratio.
- assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mActivity.getBounds());
- // Task inherits bounds from display.
- assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
- .isEqualTo(mTask.getDisplayContent().getBounds());
- }
-
static Configuration rotateDisplay(DisplayContent display, int rotation) {
final Configuration c = new Configuration();
display.getDisplayRotation().setRotation(rotation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index b8d44f6..6c72249 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -152,12 +152,6 @@
}
@Override
- public SurfaceControl.Transaction reparentChildren(SurfaceControl sc,
- SurfaceControl newParent) {
- return this;
- }
-
- @Override
public SurfaceControl.Transaction reparent(SurfaceControl sc, SurfaceControl newParent) {
return this;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index d83e9c2..e22cda6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -156,6 +156,9 @@
spyOn(mDisplayContent);
doReturn(true).when(mDisplayContent).isTrusted();
+ // Allow child stack to move to top.
+ mDisplayContent.mDontMoveToTop = false;
+
// The display contains pinned stack that was added in {@link #setUp}.
final Task stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
@@ -173,6 +176,65 @@
}
@Test
+ public void testMovingChildTaskOnTop() {
+ // Make sure the display is trusted display which capable to move the stack to top.
+ spyOn(mDisplayContent);
+ doReturn(true).when(mDisplayContent).isTrusted();
+
+ // Allow child stack to move to top.
+ mDisplayContent.mDontMoveToTop = false;
+
+ // The display contains pinned stack that was added in {@link #setUp}.
+ Task stack = createTaskStackOnDisplay(mDisplayContent);
+ Task task = createTaskInStack(stack, 0 /* userId */);
+
+ // Add another display at top.
+ mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
+ false /* includingParents */);
+
+ // Ensure that original display ({@code mDisplayContent}) is not on top.
+ assertEquals("Testing DisplayContent should not be on the top",
+ mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent));
+
+ // Move the task of {@code mDisplayContent} to top.
+ stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
+
+ // Ensure that original display ({@code mDisplayContent}) is now on the top.
+ assertEquals("The testing DisplayContent should be moved to top with task",
+ mWm.mRoot.getChildCount() - 1, mWm.mRoot.mChildren.indexOf(mDisplayContent));
+ }
+
+ @Test
+ public void testDontMovingChildTaskOnTop() {
+ // Make sure the display is trusted display which capable to move the stack to top.
+ spyOn(mDisplayContent);
+ doReturn(true).when(mDisplayContent).isTrusted();
+
+ // Allow child stack to move to top.
+ mDisplayContent.mDontMoveToTop = true;
+
+ // The display contains pinned stack that was added in {@link #setUp}.
+ Task stack = createTaskStackOnDisplay(mDisplayContent);
+ Task task = createTaskInStack(stack, 0 /* userId */);
+
+ // Add another display at top.
+ mWm.mRoot.positionChildAt(WindowContainer.POSITION_TOP, createNewDisplay(),
+ false /* includingParents */);
+
+ // Ensure that original display ({@code mDisplayContent}) is not on top.
+ assertEquals("Testing DisplayContent should not be on the top",
+ mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent));
+
+ // Try moving the task of {@code mDisplayContent} to top.
+ stack.positionChildAt(WindowContainer.POSITION_TOP, task, true /* includingParents */);
+
+ // Ensure that original display ({@code mDisplayContent}) hasn't moved and is not
+ // on the top.
+ assertEquals("The testing DisplayContent should not be moved to top with task",
+ mWm.mRoot.getChildCount() - 2, mWm.mRoot.mChildren.indexOf(mDisplayContent));
+ }
+
+ @Test
public void testReuseTaskAsRootTask() {
final Task candidateTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, mDisplayContent);
@@ -233,7 +295,7 @@
homeActivity.mVisibleRequested = true;
assertFalse(rootHomeTask.isVisible());
- assertEquals(rootWindowContainer.getOrientation(), rootHomeTask.getOrientation());
+ assertEquals(defaultTaskDisplayArea.getOrientation(), rootHomeTask.getOrientation());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index f20513d..0eb8c8d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -1004,6 +1004,26 @@
}
@Test
+ public void testNotSaveLaunchingStateForNonLeafTask() {
+ LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
+ spyOn(persister);
+
+ final Task task = getTestTask();
+ task.setHasBeenVisible(false);
+ task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
+ task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+ final Task leafTask = createTaskInStack(task, 0 /* userId */);
+
+ leafTask.setHasBeenVisible(true);
+ task.setHasBeenVisible(true);
+ task.onConfigurationChanged(task.getParent().getConfiguration());
+
+ verify(persister, never()).saveTask(same(task), any());
+ verify(persister).saveTask(same(leafTask), any());
+ }
+
+ @Test
public void testNotSpecifyOrientationByFloatingTask() {
final Task task = new TaskBuilder(mSupervisor)
.setCreateActivity(true).setCreateParentTask(true).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index d71993d..ae85ceb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -136,11 +136,6 @@
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
final TestDisplayContent newDisplay = createInternal(display);
- // Ensure letterbox aspect ratio is not overridden on any device target.
- // {@link com.android.internal.R.dimen.config_taskLetterboxAspectRatio}, provided by
- // the below method, is set on some device form factors.
- mService.mWindowManager.setTaskLetterboxAspectRatio(0);
-
// disable the normal system decorations
final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy();
spyOn(displayPolicy);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index df5b48a..99c96bd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -66,6 +66,7 @@
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
+import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -905,8 +906,10 @@
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
new IRemoteAnimationRunner.Stub() {
@Override
- public void onAnimationStart(RemoteAnimationTarget[] apps,
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) {
try {
finishedCallback.onAnimationFinished();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index f8a89c6..6d0e510 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -20,7 +20,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.os.Process.INVALID_UID;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
@@ -37,12 +36,10 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.content.pm.PackageManager;
import android.os.IBinder;
@@ -75,7 +72,7 @@
@Test
public void testAddWindowToken() {
IBinder token = mock(IBinder.class);
- mWm.addWindowToken(token, TYPE_TOAST, mDisplayContent.getDisplayId());
+ mWm.addWindowToken(token, TYPE_TOAST, mDisplayContent.getDisplayId(), null /* options */);
WindowToken windowToken = mWm.mRoot.getWindowToken(token);
assertFalse(windowToken.mRoundedCornerOverlay);
@@ -83,32 +80,6 @@
}
@Test
- public void testAddWindowTokenWithOptions() {
- IBinder token = mock(IBinder.class);
- mWm.addWindowTokenWithOptions(token, TYPE_TOAST, mDisplayContent.getDisplayId(),
- null /* options */, null /* options */);
-
- WindowToken windowToken = mWm.mRoot.getWindowToken(token);
- assertFalse(windowToken.mRoundedCornerOverlay);
- assertTrue(windowToken.isFromClient());
- }
-
- @Test(expected = SecurityException.class)
- public void testRemoveWindowToken_ownerUidNotMatch_throwException() {
- IBinder token = mock(IBinder.class);
- mWm.addWindowTokenWithOptions(token, TYPE_TOAST, mDisplayContent.getDisplayId(),
- null /* options */, null /* options */);
-
- spyOn(mWm);
- when(mWm.checkCallingPermission(anyString(), anyString())).thenReturn(false);
- WindowToken windowToken = mWm.mRoot.getWindowToken(token);
- spyOn(windowToken);
- when(windowToken.getOwnerUid()).thenReturn(INVALID_UID);
-
- mWm.removeWindowToken(token, mDisplayContent.getDisplayId());
- }
-
- @Test
public void testTaskFocusChange_stackNotHomeType_focusChanges() throws RemoteException {
DisplayContent display = createNewDisplay();
// Current focused window
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 3492d90..eb6c6ed 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -890,6 +890,7 @@
mTask.moveToFront("createActivity");
}
// Make visible by default...
+ activity.mVisibleRequested = true;
activity.setVisible(true);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index 2d27331..e2585e5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.os.Process.INVALID_UID;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -201,7 +200,7 @@
token = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_TOAST,
true /* persistOnEmpty */, mDisplayContent, true /* ownerCanManageAppTokens */,
- INVALID_UID, true /* roundedCornerOverlay */, true /* fromClientToken */);
+ true /* roundedCornerOverlay */, true /* fromClientToken */, null /* options */);
assertTrue(token.mRoundedCornerOverlay);
assertTrue(token.isFromClient());
}
@@ -215,8 +214,8 @@
public void testSurfaceCreatedForWindowToken() {
final WindowToken fromClientToken = new WindowToken(mDisplayContent.mWmService,
mock(IBinder.class), TYPE_APPLICATION_OVERLAY, true /* persistOnEmpty */,
- mDisplayContent, true /* ownerCanManageAppTokens */, INVALID_UID,
- true /* roundedCornerOverlay */, true /* fromClientToken */);
+ mDisplayContent, true /* ownerCanManageAppTokens */,
+ true /* roundedCornerOverlay */, true /* fromClientToken */, null /* options */);
assertNull(fromClientToken.mSurfaceControl);
createWindow(null, TYPE_APPLICATION_OVERLAY, fromClientToken, "window");
@@ -224,8 +223,8 @@
final WindowToken nonClientToken = new WindowToken(mDisplayContent.mWmService,
mock(IBinder.class), TYPE_TOAST, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
- false /* fromClientToken */);
+ true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */,
+ false /* fromClientToken */, null /* options */);
assertNotNull(nonClientToken.mSurfaceControl);
}
@@ -238,7 +237,7 @@
final WindowToken token1 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class),
TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */,
false /* fromClientToken */, null /* options */);
verify(selectFunc).apply(token1.windowType, null);
@@ -246,7 +245,7 @@
final Bundle options = new Bundle();
final WindowToken token2 = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class),
TYPE_STATUS_BAR, true /* persistOnEmpty */, mDisplayContent,
- true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */,
+ true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */,
false /* fromClientToken */, options /* options */);
verify(selectFunc).apply(token2.windowType, options);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 0cb1255..9e419d4 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 @@
@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 boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(
- Binder.getCallingUid(), UserHandle.getCallingUserId());
+ final int callingUid = Binder.getCallingUid();
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid,
+ userId, false, true, "queryUsageStats", callingPackage);
- final int userId = UserHandle.getCallingUserId();
+ // Check the caller's userId for obfuscation decision, not the user being queried
+ final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(
+ callingUid, 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 be36f82..f687e4b 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 @@
}
}
- 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 @@
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/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 9e8f8eaa..04dea3f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -163,8 +163,13 @@
mValid = true;
mSessionComponentName = new ComponentName(service.getPackageName(),
mInfo.getSessionService());
- // TODO : Need to get the hotword detection service from the xml metadata
- mHotwordDetectionComponentName = null;
+ final String hotwordDetectionServiceName = mInfo.getHotwordDetectionService();
+ if (hotwordDetectionServiceName != null) {
+ mHotwordDetectionComponentName = new ComponentName(service.getPackageName(),
+ hotwordDetectionServiceName);
+ } else {
+ mHotwordDetectionComponentName = null;
+ }
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
IntentFilter filter = new IntentFilter();
@@ -388,7 +393,11 @@
if (DEBUG) {
Slog.d(TAG, "setHotwordDetectionConfigLocked");
}
-
+ if (mHotwordDetectionComponentName == null) {
+ Slog.e(TAG, "Calling setHotwordDetectionConfigLocked, but hotword detection service"
+ + " name not found");
+ return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
+ }
if (!isIsolatedProcessLocked(mHotwordDetectionComponentName)) {
return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index cfdc568..84f4f6a 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -165,7 +165,8 @@
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, new UserHandle(mUser));
if (mBound) {
try {
- mIWindowManager.addWindowToken(mToken, TYPE_VOICE_INTERACTION, DEFAULT_DISPLAY);
+ mIWindowManager.addWindowToken(mToken, TYPE_VOICE_INTERACTION, DEFAULT_DISPLAY,
+ null /* options */);
} catch (RemoteException e) {
Slog.w(TAG, "Failed adding window token", e);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ac584c1..21cf3e5 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4748,6 +4748,21 @@
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 @@
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/SignalThresholdInfo.java b/telephony/java/android/telephony/SignalThresholdInfo.java
index 0059ad6..ae7d209 100644
--- a/telephony/java/android/telephony/SignalThresholdInfo.java
+++ b/telephony/java/android/telephony/SignalThresholdInfo.java
@@ -402,29 +402,27 @@
* @see #getThresholds() for more details on signal strength thresholds
*/
public @NonNull Builder setThresholds(@NonNull int[] thresholds) {
- Objects.requireNonNull(thresholds, "thresholds must not be null");
- if (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
- || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED) {
- throw new IllegalArgumentException(
- "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
- + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED);
- }
- mThresholds = thresholds.clone();
- Arrays.sort(mThresholds);
- return this;
+ return setThresholds(thresholds, false /*isSystem*/);
}
/**
- * Set the signal strength thresholds for the corresponding signal measurement type without
- * the length limitation.
+ * Set the signal strength thresholds for the corresponding signal measurement type.
*
* @param thresholds array of integer as the signal threshold values
+ * @param isSystem true is the caller is system which does not have restrictions on
+ * the length of thresholds array.
* @return the builder to facilitate the chaining
*
* @hide
*/
- public @NonNull Builder setThresholdsUnlimited(@NonNull int[] thresholds) {
+ public @NonNull Builder setThresholds(@NonNull int[] thresholds, boolean isSystem) {
Objects.requireNonNull(thresholds, "thresholds must not be null");
+ if (!isSystem && (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
+ || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED)) {
+ throw new IllegalArgumentException(
+ "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
+ + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED);
+ }
mThresholds = thresholds.clone();
Arrays.sort(mThresholds);
return this;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2734ad1..1473b7a 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -47,6 +47,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelUuid;
@@ -150,6 +151,22 @@
public static final String CACHE_KEY_SLOT_INDEX_PROPERTY =
"cache_key.telephony.get_slot_index";
+ /** @hide */
+ public static final String GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME = "getSimSpecificSettings";
+
+ /** @hide */
+ public static final String RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME =
+ "restoreSimSpecificSettings";
+
+ /**
+ * Key to the backup & restore data byte array in the Bundle that is returned by {@link
+ * #getAllSimSpecificSettingsForBackup()} or to be pass in to {@link
+ * #restoreAllSimSpecificSettings()}.
+ *
+ * @hide
+ */
+ public static final String KEY_SIM_SPECIFIC_SETTINGS_DATA = "KEY_SIM_SPECIFIC_SETTINGS_DATA";
+
private static final int MAX_CACHE_SIZE = 4;
private static class VoidPropertyInvalidatedCache<T>
@@ -372,6 +389,28 @@
public static final Uri WFC_ROAMING_ENABLED_CONTENT_URI = Uri.withAppendedPath(
CONTENT_URI, "wfc_roaming_enabled");
+
+ /**
+ * A content {@link uri} used to call the appropriate backup or restore method for sim-specific
+ * settings
+ * <p>
+ * See {@link #GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME} and {@link
+ * #RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME} for information on what method to call.
+ * @hide
+ */
+ @NonNull
+ public static final Uri SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI = Uri.withAppendedPath(
+ CONTENT_URI, "backup_and_restore");
+
+ /**
+ * A content {@link uri} used to notify contentobservers listening to siminfo restore during
+ * SuW.
+ * @hide
+ */
+ @NonNull
+ public static final Uri SIM_INFO_SUW_RESTORE_CONTENT_URI = Uri.withAppendedPath(
+ SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, "suw_restore");
+
/**
* A content {@link Uri} used to receive updates on cross sim enabled user setting.
* <p>
@@ -3455,4 +3494,71 @@
sSlotIndexCache.clear();
sPhoneIdCache.clear();
}
+
+ /**
+ * Called to retrieve SIM-specific settings data to be backed up.
+ *
+ * @return data in byte[] to be backed up.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public byte[] getAllSimSpecificSettingsForBackup() {
+ Bundle bundle = mContext.getContentResolver().call(
+ SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+ GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME, null, null);
+ return bundle.getByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA);
+ }
+
+ /**
+ * Called to attempt to restore the backed up sim-specific configs to device for specific sim.
+ * This will try to restore the data that was stored internally when {@link
+ * #restoreAllSimSpecificSettingsFromBackup(byte[] data)} was called during setup wizard.
+ * End result is SimInfoDB is modified to match any backed up configs for the requested
+ * inserted sim.
+ *
+ * <p>
+ * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any SimInfoDB
+ * entry is updated as the result of this method call.
+ *
+ * @param iccId of the sim that a restore is requested for.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void restoreSimSpecificSettingsForIccIdFromBackup(@NonNull String iccId) {
+ mContext.getContentResolver().call(
+ SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+ RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+ iccId, null);
+ }
+
+ /**
+ * Called during setup wizard restore flow to attempt to restore the backed up sim-specific
+ * configs to device for all existing SIMs in SimInfoDB. Internally, it will store the backup
+ * data in an internal file. This file will persist on device for device's lifetime and will be
+ * used later on when a SIM is inserted to restore that specific SIM's settings by calling
+ * {@link #restoreSimSpecificSettingsForIccIdFromBackup(String iccId)}. End result is
+ * SimInfoDB is modified to match any backed up configs for the appropriate inserted SIMs.
+ *
+ * <p>
+ * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any SimInfoDB
+ * entry is updated as the result of this method call.
+ *
+ * @param data with the sim specific configs to be backed up.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+ public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[] data) {
+ Bundle bundle = new Bundle();
+ bundle.putByteArray(KEY_SIM_SPECIFIC_SETTINGS_DATA, data);
+ mContext.getContentResolver().call(
+ SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+ RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+ null, bundle);
+ }
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 65b8de2..16ffd9e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -11255,16 +11255,21 @@
*
* <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 @@
}
return THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR;
}
+
/**
* Registers a listener object to receive notification of changes
* in specified telephony states.
@@ -15350,4 +15356,66 @@
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/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index f48ddb0..bd4bf07 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -136,7 +136,7 @@
private final @HandoverFailureMode int mHandoverFailureMode;
private final int mPduSessionId;
private final Qos mDefaultQos;
- private final List<QosSession> mQosSessions;
+ private final List<QosBearerSession> mQosBearerSessions;
private final SliceInfo mSliceInfo;
/**
@@ -187,7 +187,7 @@
mHandoverFailureMode = HANDOVER_FAILURE_MODE_LEGACY;
mPduSessionId = PDU_SESSION_ID_NOT_SET;
mDefaultQos = null;
- mQosSessions = new ArrayList<>();
+ mQosBearerSessions = new ArrayList<>();
mSliceInfo = null;
}
@@ -197,7 +197,7 @@
@Nullable List<InetAddress> dnsAddresses, @Nullable List<InetAddress> gatewayAddresses,
@Nullable List<InetAddress> pcscfAddresses, int mtu, int mtuV4, int mtuV6,
@HandoverFailureMode int handoverFailureMode, int pduSessionId,
- @Nullable Qos defaultQos, @Nullable List<QosSession> qosSessions,
+ @Nullable Qos defaultQos, @Nullable List<QosBearerSession> qosBearerSessions,
@Nullable SliceInfo sliceInfo) {
mCause = cause;
mSuggestedRetryTime = suggestedRetryTime;
@@ -219,7 +219,7 @@
mHandoverFailureMode = handoverFailureMode;
mPduSessionId = pduSessionId;
mDefaultQos = defaultQos;
- mQosSessions = qosSessions;
+ mQosBearerSessions = qosBearerSessions;
mSliceInfo = sliceInfo;
}
@@ -246,8 +246,8 @@
mHandoverFailureMode = source.readInt();
mPduSessionId = source.readInt();
mDefaultQos = source.readParcelable(Qos.class.getClassLoader());
- mQosSessions = new ArrayList<>();
- source.readList(mQosSessions, QosSession.class.getClassLoader());
+ mQosBearerSessions = new ArrayList<>();
+ source.readList(mQosBearerSessions, QosBearerSession.class.getClassLoader());
mSliceInfo = source.readParcelable(SliceInfo.class.getClassLoader());
}
@@ -394,8 +394,8 @@
* @hide
*/
@NonNull
- public List<QosSession> getQosSessions() {
- return mQosSessions;
+ public List<QosBearerSession> getQosBearerSessions() {
+ return mQosBearerSessions;
}
/**
@@ -427,7 +427,7 @@
.append(" handoverFailureMode=").append(failureModeToString(mHandoverFailureMode))
.append(" pduSessionId=").append(getPduSessionId())
.append(" defaultQos=").append(mDefaultQos)
- .append(" qosSessions=").append(mQosSessions)
+ .append(" qosBearerSessions=").append(mQosBearerSessions)
.append(" sliceInfo=").append(mSliceInfo)
.append("}");
return sb.toString();
@@ -447,10 +447,10 @@
mDefaultQos == other.mDefaultQos :
mDefaultQos.equals(other.mDefaultQos);
- final boolean isQosSessionsSame = (mQosSessions == null || mQosSessions == null) ?
- mQosSessions == other.mQosSessions :
- mQosSessions.size() == other.mQosSessions.size()
- && mQosSessions.containsAll(other.mQosSessions);
+ final boolean isQosBearerSessionsSame = (mQosBearerSessions == null || mQosBearerSessions == null) ?
+ mQosBearerSessions == other.mQosBearerSessions :
+ mQosBearerSessions.size() == other.mQosBearerSessions.size()
+ && mQosBearerSessions.containsAll(other.mQosBearerSessions);
return mCause == other.mCause
&& mSuggestedRetryTime == other.mSuggestedRetryTime
@@ -472,7 +472,7 @@
&& mHandoverFailureMode == other.mHandoverFailureMode
&& mPduSessionId == other.mPduSessionId
&& isQosSame
- && isQosSessionsSame
+ && isQosBearerSessionsSame
&& Objects.equals(mSliceInfo, other.mSliceInfo);
}
@@ -481,7 +481,7 @@
return Objects.hash(mCause, mSuggestedRetryTime, mId, mLinkStatus, mProtocolType,
mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses, mPcscfAddresses,
mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId, mDefaultQos,
- mQosSessions, mSliceInfo);
+ mQosBearerSessions, mSliceInfo);
}
@Override
@@ -515,7 +515,7 @@
} else {
dest.writeParcelable(null, flags);
}
- dest.writeList(mQosSessions);
+ dest.writeList(mQosBearerSessions);
dest.writeParcelable(mSliceInfo, flags);
}
@@ -598,7 +598,7 @@
private Qos mDefaultQos;
- private List<QosSession> mQosSessions = new ArrayList<>();
+ private List<QosBearerSession> mQosBearerSessions = new ArrayList<>();
private SliceInfo mSliceInfo;
@@ -812,15 +812,16 @@
/**
* Set the dedicated bearer QOS sessions for this data connection.
*
- * @param qosSessions Dedicated bearer QOS (Quality Of Service) sessions received
+ * @param qosBearerSessions Dedicated bearer QOS (Quality Of Service) sessions received
* from network.
*
* @return The same instance of the builder.
*
* @hide
*/
- public @NonNull Builder setQosSessions(@NonNull List<QosSession> qosSessions) {
- mQosSessions = qosSessions;
+ public @NonNull Builder setQosBearerSessions(
+ @NonNull List<QosBearerSession> qosBearerSessions) {
+ mQosBearerSessions = qosBearerSessions;
return this;
}
@@ -848,7 +849,7 @@
return new DataCallResponse(mCause, mSuggestedRetryTime, mId, mLinkStatus,
mProtocolType, mInterfaceName, mAddresses, mDnsAddresses, mGatewayAddresses,
mPcscfAddresses, mMtu, mMtuV4, mMtuV6, mHandoverFailureMode, mPduSessionId,
- mDefaultQos, mQosSessions, mSliceInfo);
+ mDefaultQos, mQosBearerSessions, mSliceInfo);
}
}
}
diff --git a/telephony/java/android/telephony/data/EpsQos.java b/telephony/java/android/telephony/data/EpsQos.java
index ad43068..22c8b0a 100644
--- a/telephony/java/android/telephony/data/EpsQos.java
+++ b/telephony/java/android/telephony/data/EpsQos.java
@@ -48,6 +48,10 @@
qosClassId = source.readInt();
}
+ public int getQci() {
+ return qosClassId;
+ }
+
public static @NonNull EpsQos createFromParcelBody(@NonNull Parcel in) {
return new EpsQos(in);
}
diff --git a/telephony/java/android/telephony/data/Qos.java b/telephony/java/android/telephony/data/Qos.java
index c8bb91e..c286c621 100644
--- a/telephony/java/android/telephony/data/Qos.java
+++ b/telephony/java/android/telephony/data/Qos.java
@@ -56,7 +56,15 @@
this.uplink = new QosBandwidth(uplink.maxBitrateKbps, uplink.guaranteedBitrateKbps);
}
- static class QosBandwidth implements Parcelable {
+ public QosBandwidth getDownlinkBandwidth() {
+ return downlink;
+ }
+
+ public QosBandwidth getUplinkBandwidth() {
+ return uplink;
+ }
+
+ public static class QosBandwidth implements Parcelable {
int maxBitrateKbps;
int guaranteedBitrateKbps;
@@ -73,6 +81,14 @@
guaranteedBitrateKbps = source.readInt();
}
+ public int getMaxBitrateKbps() {
+ return maxBitrateKbps;
+ }
+
+ public int getGuaranteedBitrateKbps() {
+ return guaranteedBitrateKbps;
+ }
+
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(maxBitrateKbps);
diff --git a/telephony/java/android/telephony/data/QosFilter.java b/telephony/java/android/telephony/data/QosBearerFilter.java
similarity index 89%
rename from telephony/java/android/telephony/data/QosFilter.java
rename to telephony/java/android/telephony/data/QosBearerFilter.java
index 6927744..6c1c653 100644
--- a/telephony/java/android/telephony/data/QosFilter.java
+++ b/telephony/java/android/telephony/data/QosBearerFilter.java
@@ -38,7 +38,7 @@
*
* @hide
*/
-public final class QosFilter implements Parcelable {
+public final class QosBearerFilter implements Parcelable {
private List<LinkAddress> localAddresses;
private List<LinkAddress> remoteAddresses;
@@ -74,7 +74,7 @@
@IntDef(prefix = "QOS_FILTER_DIRECTION_",
value = {QOS_FILTER_DIRECTION_DOWNLINK, QOS_FILTER_DIRECTION_UPLINK,
QOS_FILTER_DIRECTION_BIDIRECTIONAL})
- public @interface QosFilterDirection {}
+ public @interface QosBearerFilterDirection {}
public static final int QOS_FILTER_DIRECTION_DOWNLINK =
android.hardware.radio.V1_6.QosFilterDirection.DOWNLINK;
@@ -83,7 +83,7 @@
public static final int QOS_FILTER_DIRECTION_BIDIRECTIONAL =
android.hardware.radio.V1_6.QosFilterDirection.BIDIRECTIONAL;
- @QosFilterDirection
+ @QosBearerFilterDirection
private int filterDirection;
/**
@@ -92,7 +92,7 @@
*/
private int precedence;
- QosFilter() {
+ QosBearerFilter() {
localAddresses = new ArrayList<>();
remoteAddresses = new ArrayList<>();
localPort = new PortRange();
@@ -101,7 +101,7 @@
filterDirection = QOS_FILTER_DIRECTION_BIDIRECTIONAL;
}
- public QosFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses,
+ public QosBearerFilter(List<LinkAddress> localAddresses, List<LinkAddress> remoteAddresses,
PortRange localPort, PortRange remotePort, int protocol, int tos,
long flowLabel, long spi, int direction, int precedence) {
this.localAddresses = localAddresses;
@@ -116,10 +116,30 @@
this.precedence = precedence;
}
+ public List<LinkAddress> getLocalAddresses() {
+ return localAddresses;
+ }
+
+ public List<LinkAddress> getRemoteAddresses() {
+ return remoteAddresses;
+ }
+
+ public PortRange getLocalPortRange() {
+ return localPort;
+ }
+
+ public PortRange getRemotePortRange() {
+ return remotePort;
+ }
+
+ public int getPrecedence() {
+ return precedence;
+ }
+
/** @hide */
- public static @NonNull QosFilter create(
+ public static @NonNull QosBearerFilter create(
@NonNull android.hardware.radio.V1_6.QosFilter qosFilter) {
- QosFilter ret = new QosFilter();
+ QosBearerFilter ret = new QosBearerFilter();
String[] localAddresses = qosFilter.localAddresses.stream().toArray(String[]::new);
if (localAddresses != null) {
@@ -202,6 +222,14 @@
this.end = end;
}
+ public int getStart() {
+ return start;
+ }
+
+ public int getEnd() {
+ return end;
+ }
+
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(start);
@@ -254,7 +282,7 @@
@Override
public String toString() {
- return "QosFilter {"
+ return "QosBearerFilter {"
+ " localAddresses=" + localAddresses
+ " remoteAddresses=" + remoteAddresses
+ " localPort=" + localPort
@@ -278,11 +306,11 @@
public boolean equals(Object o) {
if (this == o) return true;
- if (o == null || !(o instanceof QosFilter)) {
+ if (o == null || !(o instanceof QosBearerFilter)) {
return false;
}
- QosFilter other = (QosFilter) o;
+ QosBearerFilter other = (QosBearerFilter) o;
return localAddresses.size() == other.localAddresses.size()
&& localAddresses.containsAll(other.localAddresses)
@@ -324,7 +352,7 @@
LinkAddress.LIFETIME_UNKNOWN, LinkAddress.LIFETIME_UNKNOWN);
}
- private QosFilter(Parcel source) {
+ private QosBearerFilter(Parcel source) {
localAddresses = new ArrayList<>();
source.readList(localAddresses, LinkAddress.class.getClassLoader());
remoteAddresses = new ArrayList<>();
@@ -358,16 +386,16 @@
return 0;
}
- public static final @NonNull Parcelable.Creator<QosFilter> CREATOR =
- new Parcelable.Creator<QosFilter>() {
+ public static final @NonNull Parcelable.Creator<QosBearerFilter> CREATOR =
+ new Parcelable.Creator<QosBearerFilter>() {
@Override
- public QosFilter createFromParcel(Parcel source) {
- return new QosFilter(source);
+ public QosBearerFilter createFromParcel(Parcel source) {
+ return new QosBearerFilter(source);
}
@Override
- public QosFilter[] newArray(int size) {
- return new QosFilter[size];
+ public QosBearerFilter[] newArray(int size) {
+ return new QosBearerFilter[size];
}
};
}
diff --git a/telephony/java/android/telephony/data/QosBearerSession.java b/telephony/java/android/telephony/data/QosBearerSession.java
new file mode 100644
index 0000000..30effc9
--- /dev/null
+++ b/telephony/java/android/telephony/data/QosBearerSession.java
@@ -0,0 +1,137 @@
+/**
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.data;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+
+/**
+ * Class that stores information specific to QOS session.
+ *
+ * @hide
+ */
+public final class QosBearerSession implements Parcelable{
+
+ final int qosBearerSessionId;
+ final Qos qos;
+ final List<QosBearerFilter> qosBearerFilterList;
+
+ public QosBearerSession(int qosBearerSessionId, @NonNull Qos qos, @NonNull List<QosBearerFilter> qosBearerFilterList) {
+ this.qosBearerSessionId = qosBearerSessionId;
+ this.qos = qos;
+ this.qosBearerFilterList = qosBearerFilterList;
+ }
+
+ private QosBearerSession(Parcel source) {
+ qosBearerSessionId = source.readInt();
+ qos = source.readParcelable(Qos.class.getClassLoader());
+ qosBearerFilterList = new ArrayList<>();
+ source.readList(qosBearerFilterList, QosBearerFilter.class.getClassLoader());
+ }
+
+ public int getQosBearerSessionId() {
+ return qosBearerSessionId;
+ }
+
+ public Qos getQos() {
+ return qos;
+ }
+
+ public List<QosBearerFilter> getQosBearerFilterList() {
+ return qosBearerFilterList;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(qosBearerSessionId);
+ if (qos.getType() == Qos.QOS_TYPE_EPS) {
+ dest.writeParcelable((EpsQos)qos, flags);
+ } else {
+ dest.writeParcelable((NrQos)qos, flags);
+ }
+ dest.writeList(qosBearerFilterList);
+ }
+
+ public static @NonNull QosBearerSession create(
+ @NonNull android.hardware.radio.V1_6.QosSession qosSession) {
+ List<QosBearerFilter> qosBearerFilters = new ArrayList<>();
+
+ if (qosSession.qosFilters != null) {
+ for (android.hardware.radio.V1_6.QosFilter filter : qosSession.qosFilters) {
+ qosBearerFilters.add(QosBearerFilter.create(filter));
+ }
+ }
+
+ return new QosBearerSession(
+ qosSession.qosSessionId,
+ Qos.create(qosSession.qos),
+ qosBearerFilters);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "QosBearerSession {"
+ + " qosBearerSessionId=" + qosBearerSessionId
+ + " qos=" + qos
+ + " qosBearerFilterList=" + qosBearerFilterList + "}";
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(qosBearerSessionId, qos, qosBearerFilterList);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ if (o == null || !(o instanceof QosBearerSession)) {
+ return false;
+ }
+
+ QosBearerSession other = (QosBearerSession) o;
+ return this.qosBearerSessionId == other.qosBearerSessionId
+ && this.qos.equals(other.qos)
+ && this.qosBearerFilterList.size() == other.qosBearerFilterList.size()
+ && this.qosBearerFilterList.containsAll(other.qosBearerFilterList);
+ }
+
+
+ public static final @NonNull Parcelable.Creator<QosBearerSession> CREATOR =
+ new Parcelable.Creator<QosBearerSession>() {
+ @Override
+ public QosBearerSession createFromParcel(Parcel source) {
+ return new QosBearerSession(source);
+ }
+
+ @Override
+ public QosBearerSession[] newArray(int size) {
+ return new QosBearerSession[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/data/QosSession.java b/telephony/java/android/telephony/data/QosSession.java
deleted file mode 100644
index f07b6a9..0000000
--- a/telephony/java/android/telephony/data/QosSession.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/**
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.data;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-
-/**
- * Class that stores information specific to QOS session.
- *
- * @hide
- */
-public final class QosSession implements Parcelable{
-
- final int qosSessionId;
- final Qos qos;
- final List<QosFilter> qosFilterList;
-
- public QosSession(int qosSessionId, @NonNull Qos qos, @NonNull List<QosFilter> qosFilterList) {
- this.qosSessionId = qosSessionId;
- this.qos = qos;
- this.qosFilterList = qosFilterList;
- }
-
- private QosSession(Parcel source) {
- qosSessionId = source.readInt();
- qos = source.readParcelable(Qos.class.getClassLoader());
- qosFilterList = new ArrayList<>();
- source.readList(qosFilterList, QosFilter.class.getClassLoader());
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(qosSessionId);
- if (qos.getType() == Qos.QOS_TYPE_EPS) {
- dest.writeParcelable((EpsQos)qos, flags);
- } else {
- dest.writeParcelable((NrQos)qos, flags);
- }
- dest.writeList(qosFilterList);
- }
-
- public static @NonNull QosSession create(
- @NonNull android.hardware.radio.V1_6.QosSession qosSession) {
- List<QosFilter> qosFilters = new ArrayList<>();
-
- if (qosSession.qosFilters != null) {
- for (android.hardware.radio.V1_6.QosFilter filter : qosSession.qosFilters) {
- qosFilters.add(QosFilter.create(filter));
- }
- }
-
- return new QosSession(
- qosSession.qosSessionId,
- Qos.create(qosSession.qos),
- qosFilters);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public String toString() {
- return "QosSession {"
- + " qosSessionId=" + qosSessionId
- + " qos=" + qos
- + " qosFilterList=" + qosFilterList + "}";
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(qosSessionId, qos, qosFilterList);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
-
- if (o == null || !(o instanceof QosSession)) {
- return false;
- }
-
- QosSession other = (QosSession) o;
- return this.qosSessionId == other.qosSessionId
- && this.qos.equals(other.qos)
- && this.qosFilterList.size() == other.qosFilterList.size()
- && this.qosFilterList.containsAll(other.qosFilterList);
- }
-
-
- public static final @NonNull Parcelable.Creator<QosSession> CREATOR =
- new Parcelable.Creator<QosSession>() {
- @Override
- public QosSession createFromParcel(Parcel source) {
- return new QosSession(source);
- }
-
- @Override
- public QosSession[] newArray(int size) {
- return new QosSession[size];
- }
- };
-}
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index f39e30b..814ce18 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
@@ -39,6 +40,8 @@
import com.android.internal.telephony.IIntegerConsumer;
+import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -77,55 +80,13 @@
"android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN";
/**
- * Receives RCS Feature availability status updates from the ImsService.
- *
- * @see #isAvailable(int)
- * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback)
- * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback)
+ * An application can use {@link #addOnAvailabilityChangedListener} to register a
+ * {@link OnAvailabilityChangedListener}, which will notify the user when the RCS feature
+ * availability status updates from the ImsService.
* @hide
*/
- public static class AvailabilityCallback {
-
- private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
-
- private final AvailabilityCallback mLocalCallback;
- private Executor mExecutor;
-
- CapabilityBinder(AvailabilityCallback c) {
- mLocalCallback = c;
- }
-
- @Override
- public void onCapabilitiesStatusChanged(int config) {
- if (mLocalCallback == null) return;
-
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- mExecutor.execute(() -> mLocalCallback.onAvailabilityChanged(config));
- } finally {
- restoreCallingIdentity(callingIdentity);
- }
- }
-
- @Override
- public void onQueryCapabilityConfiguration(int capability, int radioTech,
- boolean isEnabled) {
- // This is not used for public interfaces.
- }
-
- @Override
- public void onChangeCapabilityConfigurationError(int capability, int radioTech,
- @ImsFeature.ImsCapabilityError int reason) {
- // This is not used for public interfaces
- }
-
- private void setExecutor(Executor executor) {
- mExecutor = executor;
- }
- }
-
- private final CapabilityBinder mBinder = new CapabilityBinder(this);
-
+ @SystemApi
+ public interface OnAvailabilityChangedListener {
/**
* The availability of the feature's capabilities has changed to either available or
* unavailable.
@@ -136,22 +97,68 @@
*
* @param capabilities The new availability of the capabilities.
*/
- public void onAvailabilityChanged(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
+ void onAvailabilityChanged(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities);
+ }
+
+ /**
+ * Receive the availability status changed from the ImsService and pass the status change to
+ * the associated {@link OnAvailabilityChangedListener}
+ */
+ private static class AvailabilityCallbackAdapter {
+
+ private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
+ private final OnAvailabilityChangedListener mOnAvailabilityChangedListener;
+ private final Executor mExecutor;
+
+ CapabilityBinder(OnAvailabilityChangedListener listener, Executor executor) {
+ mExecutor = executor;
+ mOnAvailabilityChangedListener = listener;
+ }
+
+ @Override
+ public void onCapabilitiesStatusChanged(int config) {
+ if (mOnAvailabilityChangedListener == null) return;
+
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() ->
+ mOnAvailabilityChangedListener.onAvailabilityChanged(config));
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
+ public void onQueryCapabilityConfiguration(int capability, int radioTech,
+ boolean isEnabled) {
+ // This is not used.
+ }
+
+ @Override
+ public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+ @ImsFeature.ImsCapabilityError int reason) {
+ // This is not used.
+ }
+ }
+
+ private final CapabilityBinder mBinder;
+
+ AvailabilityCallbackAdapter(@NonNull Executor executor,
+ @NonNull OnAvailabilityChangedListener listener) {
+ mBinder = new CapabilityBinder(listener, executor);
}
/**@hide*/
public final IImsCapabilityCallback getBinder() {
return mBinder;
}
-
- private void setExecutor(Executor executor) {
- mBinder.setExecutor(executor);
- }
}
private final int mSubId;
private final Context mContext;
private final BinderCacheManager<IImsRcsController> mBinderCache;
+ private final Map<OnAvailabilityChangedListener, AvailabilityCallbackAdapter>
+ mAvailabilityChangedCallbacks;
/**
* Use {@link ImsManager#getImsRcsManager(int)} to create an instance of this class.
@@ -162,6 +169,7 @@
mSubId = subId;
mContext = context;
mBinderCache = binderCache;
+ mAvailabilityChangedCallbacks = new HashMap<>();
}
/**
@@ -174,10 +182,23 @@
}
/**
- * @hide
+ * Registers a {@link RegistrationManager.RegistrationCallback} with the system. When the
+ * callback is registered, it will initiate the callback c to be called with the current
+ * registration state.
+ *
+ * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param executor The executor the callback events should be run on.
+ * @param c The {@link RegistrationManager.RegistrationCallback} to be added.
+ * @see #unregisterImsRegistrationCallback(RegistrationManager.RegistrationCallback)
+ * @throws ImsException if the subscription associated with this callback is valid, but
+ * the {@link ImsService} associated with the subscription is not available. This can happen if
+ * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+ * reason.
*/
- // @Override add back to RegistrationManager interface once public.
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
public void registerImsRegistrationCallback(
@NonNull @CallbackExecutor Executor executor,
@NonNull RegistrationManager.RegistrationCallback c)
@@ -191,7 +212,7 @@
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "Register registration callback: IImsRcsController is null");
+ Log.w(TAG, "Register registration callback: IImsRcsController is null");
throw new ImsException("Cannot find remote IMS service",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
@@ -207,10 +228,21 @@
}
/**
- * @hide
+ * Removes an existing {@link RegistrationManager.RegistrationCallback}.
+ *
+ * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
+ * etc...), this callback will automatically be removed. If this method is called for an
+ * inactive subscription, it will result in a no-op.
+ *
+ * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param c The {@link RegistrationManager.RegistrationCallback} to be removed.
+ * @see android.telephony.SubscriptionManager.OnSubscriptionsChangedListener
+ * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
*/
- // @Override add back to RegistrationManager interface once public.
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
public void unregisterImsRegistrationCallback(
@NonNull RegistrationManager.RegistrationCallback c) {
if (c == null) {
@@ -219,7 +251,7 @@
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "Unregister registration callback: IImsRcsController is null");
+ Log.w(TAG, "Unregister registration callback: IImsRcsController is null");
throw new IllegalStateException("Cannot find remote IMS service");
}
@@ -231,10 +263,21 @@
}
/**
- * @hide
+ * Gets the registration state of the IMS service.
+ *
+ * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param executor The {@link Executor} that will be used to call the IMS registration state
+ * callback.
+ * @param stateCallback A callback called on the supplied {@link Executor} that will contain the
+ * registration state of the IMS service, which will be one of the
+ * following: {@link RegistrationManager#REGISTRATION_STATE_NOT_REGISTERED},
+ * {@link RegistrationManager#REGISTRATION_STATE_REGISTERING}, or
+ * {@link RegistrationManager#REGISTRATION_STATE_REGISTERED}.
*/
- // @Override add back to RegistrationManager interface once public.
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
public void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
@NonNull @RegistrationManager.ImsRegistrationState Consumer<Integer> stateCallback) {
if (stateCallback == null) {
@@ -246,7 +289,7 @@
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "Get registration state error: IImsRcsController is null");
+ Log.w(TAG, "Get registration state error: IImsRcsController is null");
throw new IllegalStateException("Cannot find remote IMS service");
}
@@ -263,9 +306,20 @@
}
/**
- * @hide
+ * Gets the Transport Type associated with the current IMS registration.
+ *
+ * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
+ * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
+ *
+ * @param executor The {@link Executor} that will be used to call the transportTypeCallback.
+ * @param transportTypeCallback The transport type associated with the current IMS registration,
+ * which will be one of following:
+ * {@see AccessNetworkConstants#TRANSPORT_TYPE_WWAN},
+ * {@see AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or
+ * {@see AccessNetworkConstants#TRANSPORT_TYPE_INVALID}.
*/
- @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor,
@NonNull @AccessNetworkConstants.TransportType
Consumer<Integer> transportTypeCallback) {
@@ -278,7 +332,7 @@
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "Get registration transport type error: IImsRcsController is null");
+ Log.w(TAG, "Get registration transport type error: IImsRcsController is null");
throw new IllegalStateException("Cannot find remote IMS service");
}
@@ -296,31 +350,33 @@
}
/**
- * Registers an {@link AvailabilityCallback} with the system, which will provide RCS
+ * Add an {@link OnAvailabilityChangedListener} with the system, which will provide RCS
* availability updates for the subscription specified.
*
* Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
* subscription changed events and call
- * {@link #unregisterRcsAvailabilityCallback(AvailabilityCallback)} to clean up after a
- * subscription is removed.
+ * {@link #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener)} to clean up
+ * after a subscription is removed.
* <p>
- * When the callback is registered, it will initiate the callback c to be called with the
- * current capabilities.
+ * When the listener is registered, it will initiate the callback listener to be called with
+ * the current capabilities.
*
* @param executor The executor the callback events should be run on.
- * @param c The RCS {@link AvailabilityCallback} to be registered.
- * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback)
+ * @param listener The RCS {@link OnAvailabilityChangedListener} to be registered.
+ * @see #removeOnAvailabilityChangedListener(OnAvailabilityChangedListener)
* @throws ImsException if the subscription associated with this instance of
* {@link ImsRcsManager} is valid, but the ImsService associated with the subscription is not
* available. This can happen if the ImsService has crashed, for example, or if the subscription
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public void registerRcsAvailabilityCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull AvailabilityCallback c) throws ImsException {
- if (c == null) {
- throw new IllegalArgumentException("Must include a non-null AvailabilityCallback.");
+ public void addOnAvailabilityChangedListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull OnAvailabilityChangedListener listener) throws ImsException {
+ if (listener == null) {
+ throw new IllegalArgumentException("Must include a non-null"
+ + "OnAvailabilityChangedListener.");
}
if (executor == null) {
throw new IllegalArgumentException("Must include a non-null Executor.");
@@ -328,56 +384,61 @@
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "Register availability callback: IImsRcsController is null");
+ Log.w(TAG, "Add availability changed listener: IImsRcsController is null");
throw new ImsException("Cannot find remote IMS service",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
- c.setExecutor(executor);
+ AvailabilityCallbackAdapter adapter =
+ addAvailabilityChangedListenerToCollection(executor, listener);
try {
- imsRcsController.registerRcsAvailabilityCallback(mSubId, c.getBinder());
-
+ imsRcsController.registerRcsAvailabilityCallback(mSubId, adapter.getBinder());
} catch (ServiceSpecificException e) {
throw new ImsException(e.toString(), e.errorCode);
} catch (RemoteException e) {
- Log.e(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e);
+ Log.w(TAG, "Error calling IImsRcsController#registerRcsAvailabilityCallback", e);
throw new ImsException("Remote IMS Service is not available",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
- /**
- * Removes an existing RCS {@link AvailabilityCallback}.
+ /**
+ * Removes an existing RCS {@link OnAvailabilityChangedListener}.
* <p>
* When the subscription associated with this callback is removed (SIM removed, ESIM swap,
* etc...), this callback will automatically be unregistered. If this method is called for an
* inactive subscription, it will result in a no-op.
- * @param c The RCS {@link AvailabilityCallback} to be removed.
- * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback)
+ * @param listener The RCS {@link OnAvailabilityChangedListener} to be removed.
+ * @see #addOnAvailabilityChangedListener(Executor, OnAvailabilityChangedListener)
* @throws ImsException if the IMS service is not available when calling this method.
* See {@link ImsException#getCode()} for more information on the error codes.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public void unregisterRcsAvailabilityCallback(@NonNull AvailabilityCallback c)
- throws ImsException {
- if (c == null) {
- throw new IllegalArgumentException("Must include a non-null AvailabilityCallback.");
+ public void removeOnAvailabilityChangedListener(
+ @NonNull OnAvailabilityChangedListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("Must include a non-null"
+ + "OnAvailabilityChangedListener.");
}
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "Unregister availability callback: IImsRcsController is null");
- throw new ImsException("Cannot find remote IMS service",
- ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ Log.w(TAG, "Remove availability changed listener: IImsRcsController is null");
+ return;
+ }
+
+ AvailabilityCallbackAdapter callback =
+ removeAvailabilityChangedListenerFromCollection(listener);
+ if (callback == null) {
+ return;
}
try {
- imsRcsController.unregisterRcsAvailabilityCallback(mSubId, c.getBinder());
+ imsRcsController.unregisterRcsAvailabilityCallback(mSubId, callback.getBinder());
} catch (RemoteException e) {
- Log.e(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e);
- throw new ImsException("Remote IMS Service is not available",
- ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ Log.w(TAG, "Error calling IImsRcsController#unregisterRcsAvailabilityCallback", e);
}
}
@@ -388,26 +449,24 @@
* RCS capabilities provided over-the-top by applications.
*
* @param capability The RCS capability to query.
- * @param radioTech The radio tech that this capability failed for, defined as
- * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
- * {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM} or
- * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}.
+ * @param radioTech The radio technology type that we are querying.
* @return true if the RCS capability is capable for this subscription, false otherwise. This
* does not necessarily mean that we are registered for IMS and the capability is available, but
* rather the subscription is capable of this service over IMS.
- * @see #isAvailable(int)
+ * @see #isAvailable(int, int)
* @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL
* @see android.telephony.CarrierConfigManager.Ims#KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL
* @throws ImsException if the IMS service is not available when calling this method.
* See {@link ImsException#getCode()} for more information on the error codes.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isCapable(@RcsUceAdapter.RcsImsCapabilityFlag int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) throws ImsException {
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "isCapable: IImsRcsController is null");
+ Log.w(TAG, "isCapable: IImsRcsController is null");
throw new ImsException("Cannot find remote IMS service",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
@@ -415,7 +474,7 @@
try {
return imsRcsController.isCapable(mSubId, capability, radioTech);
} catch (RemoteException e) {
- Log.e(TAG, "Error calling IImsRcsController#isCapable", e);
+ Log.w(TAG, "Error calling IImsRcsController#isCapable", e);
throw new ImsException("Remote IMS Service is not available",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
@@ -428,6 +487,7 @@
* RCS capabilities provided by over-the-top by applications.
*
* @param capability the RCS capability to query.
+ * @param radioTech The radio technology type that we are querying.
* @return true if the RCS capability is currently available for the associated subscription,
* false otherwise. If the capability is available, IMS is registered and the service is
* currently available over IMS.
@@ -436,25 +496,57 @@
* See {@link ImsException#getCode()} for more information on the error codes.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public boolean isAvailable(@RcsUceAdapter.RcsImsCapabilityFlag int capability)
+ public boolean isAvailable(@RcsUceAdapter.RcsImsCapabilityFlag int capability,
+ @ImsRegistrationImplBase.ImsRegistrationTech int radioTech)
throws ImsException {
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "isAvailable: IImsRcsController is null");
+ Log.w(TAG, "isAvailable: IImsRcsController is null");
throw new ImsException("Cannot find remote IMS service",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
try {
- return imsRcsController.isAvailable(mSubId, capability);
+ return imsRcsController.isAvailable(mSubId, capability, radioTech);
} catch (RemoteException e) {
- Log.e(TAG, "Error calling IImsRcsController#isAvailable", e);
+ Log.w(TAG, "Error calling IImsRcsController#isAvailable", e);
throw new ImsException("Remote IMS Service is not available",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
+ /**
+ * Add the {@link OnAvailabilityChangedListener} to collection for tracking.
+ * @param executor The executor that will be used when the publish state is changed and the
+ * {@link OnAvailabilityChangedListener} is called.
+ * @param listener The {@link OnAvailabilityChangedListener} to call the publish state changed.
+ * @return The {@link AvailabilityCallbackAdapter} to wrapper the
+ * {@link OnAvailabilityChangedListener}
+ */
+ private AvailabilityCallbackAdapter addAvailabilityChangedListenerToCollection(
+ @NonNull Executor executor, @NonNull OnAvailabilityChangedListener listener) {
+ AvailabilityCallbackAdapter adapter = new AvailabilityCallbackAdapter(executor, listener);
+ synchronized (mAvailabilityChangedCallbacks) {
+ mAvailabilityChangedCallbacks.put(listener, adapter);
+ }
+ return adapter;
+ }
+
+ /**
+ * Remove the existing {@link OnAvailabilityChangedListener} from the collection.
+ * @param listener The {@link OnAvailabilityChangedListener} to remove from the collection.
+ * @return The wrapper class {@link AvailabilityCallbackAdapter} associated with the
+ * {@link OnAvailabilityChangedListener}.
+ */
+ private AvailabilityCallbackAdapter removeAvailabilityChangedListenerFromCollection(
+ @NonNull OnAvailabilityChangedListener listener) {
+ synchronized (mAvailabilityChangedCallbacks) {
+ return mAvailabilityChangedCallbacks.remove(listener);
+ }
+ }
+
private IImsRcsController getIImsRcsController() {
IBinder binder = TelephonyFrameworkInitializer
.getTelephonyServiceManager()
diff --git a/tools/hiddenapi/Android.bp b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl
similarity index 68%
rename from tools/hiddenapi/Android.bp
rename to telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl
index e0eb06cb..0830ff2 100644
--- a/tools/hiddenapi/Android.bp
+++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.aidl
@@ -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,17 +14,6 @@
* limitations under the License.
*/
-python_binary_host {
- name: "merge_csv",
- main: "merge_csv.py",
- srcs: ["merge_csv.py"],
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- embedded_launcher: true
- },
- },
-}
+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 0000000..ccb3231
--- /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 b430bef..c682afe 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -72,30 +72,6 @@
*/
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 @@
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 @@
}
/**
+ * @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 @@
}
@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 @@
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 @@
* 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 @@
/**
* Notifies the framework when the IMS Provider is registered to the IMS network
- * with corresponding attributes
+ * 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>
- *
+ * @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 @@
/**
* 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/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index 7a6c28b..8931a78 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -47,7 +47,7 @@
void registerRcsAvailabilityCallback(int subId, IImsCapabilityCallback c);
void unregisterRcsAvailabilityCallback(int subId, IImsCapabilityCallback c);
boolean isCapable(int subId, int capability, int radioTech);
- boolean isAvailable(int subId, int capability);
+ boolean isAvailable(int subId, int capability, int radioTech);
// ImsUceAdapter specific
void requestCapabilities(int subId, String callingPackage, String callingFeatureId,
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl
index 749b191..179407c 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.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 @@
* {@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/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
index c5b1c90..f3791d1 100644
--- a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
+++ b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java
@@ -36,12 +36,9 @@
public final class CapabilityChangeRequest implements Parcelable {
/**
- * Contains a feature capability, defined as
- * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
- * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
- * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
- * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS},
- * along with an associated technology, defined as
+ * Contains a MMTEL feature capability {@link MmTelFeature.MmTelCapabilities} and RCS feature
+ * capability {@link RcsFeature.RcsImsCapabilities}, along with an associated technology,
+ * defined as
* {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} or
* {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
* {@link ImsRegistrationImplBase#REGISTRATION_TECH_CROSS_SIM}
@@ -50,7 +47,7 @@
private final int mCapability;
private final int radioTech;
- public CapabilityPair(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
+ public CapabilityPair(int capability,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
this.mCapability = capability;
this.radioTech = radioTech;
@@ -81,13 +78,10 @@
}
/**
- * @return The stored capability, defined as
- * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
- * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO},
- * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, or
- * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
+ * @return The stored capability, defined as {@link MmTelFeature.MmTelCapabilities} and
+ * {@link RcsFeature.RcsImsCapabilities}
*/
- public @MmTelFeature.MmTelCapabilities.MmTelCapability int getCapability() {
+ public int getCapability() {
return mCapability;
}
@@ -125,12 +119,11 @@
* Add one or many capabilities to the request to be enabled.
*
* @param capabilities A bitfield of capabilities to enable, valid values are defined in
- * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
+ * {@link MmTelFeature.MmTelCapabilities} and {@link RcsFeature.RcsImsCapabilities}.
* @param radioTech the radio tech that these capabilities should be enabled for, valid
* values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}.
*/
- public void addCapabilitiesToEnableForTech(
- @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities,
+ public void addCapabilitiesToEnableForTech(int capabilities,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
addAllCapabilities(mCapabilitiesToEnable, capabilities, radioTech);
}
@@ -138,12 +131,11 @@
/**
* Add one or many capabilities to the request to be disabled.
* @param capabilities A bitfield of capabilities to diable, valid values are defined in
- * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
+ * {@link MmTelFeature.MmTelCapabilities} and {@link RcsFeature.RcsImsCapabilities}.
* @param radioTech the radio tech that these capabilities should be disabled for, valid
* values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}.
*/
- public void addCapabilitiesToDisableForTech(
- @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities,
+ public void addCapabilitiesToDisableForTech(int capabilities,
@ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
addAllCapabilities(mCapabilitiesToDisable, capabilities, radioTech);
}
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 22df921..85703f8 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -194,7 +194,6 @@
* of the capability and notify the capability status as true using
* {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
* framework that the capability is available for usage.
- * @hide
*/
public static class RcsImsCapabilities extends Capabilities {
/** @hide*/
@@ -226,12 +225,21 @@
*/
public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
+ /**
+ * Create a new {@link RcsImsCapabilities} instance with the provided capabilities.
+ * @param capabilities The capabilities that are supported for RCS in the form of a
+ * bitfield.
+ */
public RcsImsCapabilities(@RcsUceAdapter.RcsImsCapabilityFlag int capabilities) {
super(capabilities);
}
- private RcsImsCapabilities(Capabilities c) {
- super(c.getMask());
+ /**
+ * Create a new {@link RcsImsCapabilities} instance with the provided capabilities.
+ * @param capabilities The capabilities instance that are supported for RCS
+ */
+ private RcsImsCapabilities(Capabilities capabilities) {
+ super(capabilities.getMask());
}
@Override
@@ -307,7 +315,7 @@
* set, the {@link RcsFeature} has brought up the capability and is ready for framework
* requests. To change the status of the capabilities
* {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called.
- * @hide
+ * @return A copy of the current RcsFeature capability status.
*/
@Override
public @NonNull final RcsImsCapabilities queryCapabilityStatus() {
@@ -318,13 +326,13 @@
* Notify the framework that the capabilities status has changed. If a capability is enabled,
* this signals to the framework that the capability has been initialized and is ready.
* Call {@link #queryCapabilityStatus()} to return the current capability status.
- * @hide
+ * @param capabilities The current capability status of the RcsFeature.
*/
- public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) {
- if (c == null) {
+ public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities capabilities) {
+ if (capabilities == null) {
throw new IllegalArgumentException("RcsImsCapabilities must be non-null!");
}
- super.notifyCapabilitiesStatusChanged(c);
+ super.notifyCapabilitiesStatusChanged(capabilities);
}
/**
@@ -333,7 +341,9 @@
* {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to
* enable or disable capability A, this method should return the correct configuration for
* capability A afterwards (until it has changed).
- * @hide
+ * @param capability The capability that we are querying the configuration for.
+ * @param radioTech The radio technology type that we are querying.
+ * @return true if the capability is enabled, false otherwise.
*/
public boolean queryCapabilityConfiguration(
@RcsUceAdapter.RcsImsCapabilityFlag int capability,
@@ -355,11 +365,12 @@
* If for some reason one or more of these capabilities can not be enabled/disabled,
* {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should
* be called for each capability change that resulted in an error.
- * @hide
+ * @param request The request to change the capability.
+ * @param callback To notify the framework that the result of the capability changes.
*/
@Override
public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
- @NonNull CapabilityCallbackProxy c) {
+ @NonNull CapabilityCallbackProxy callback) {
// Base Implementation - Override to provide functionality
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 4f753c3..39994be 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -18,17 +18,18 @@
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 @@
@Override
public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException {
- return getConnectionType();
+ synchronized (mLock) {
+ return (mRegistrationAttributes == null) ? REGISTRATION_TECH_NONE
+ : mRegistrationAttributes.getRegistrationTechnology();
+ }
}
@Override
@@ -122,8 +126,7 @@
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 @@
/**
* 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 @@
/**
* 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 @@
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 @@
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 @@
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 @@
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 @@
}
/**
- * @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 @@
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 0cd17da3..1d04953 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2387,4 +2387,18 @@
* 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 39dc9c2..e2d2eca 100644
--- a/tests/ApkVerityTest/Android.bp
+++ b/tests/ApkVerityTest/Android.bp
@@ -16,7 +16,10 @@
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 0000000..d67285ed
--- /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 37fbc29..8f2d4bc 100644
--- a/tests/ApkVerityTest/block_device_writer/Android.bp
+++ b/tests/ApkVerityTest/block_device_writer/Android.bp
@@ -51,3 +51,9 @@
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 0000000..5c2c15b
--- /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 d0eb9be..ab3572b 100644
--- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
+++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
@@ -24,6 +24,7 @@
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 @@
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 @@
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 @@
// 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 @@
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/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index e68fbd8..b2719fb 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -25,7 +25,6 @@
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.server.wm.flicker"/>
- <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
<option name="shell-timeout" value="6600s" />
<option name="test-timeout" value="6600s" />
<option name="hidden-api-checks" value="false" />
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 89c6663..c5447c1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -17,8 +17,11 @@
package com.android.server.wm.flicker
import android.platform.helpers.IAppHelper
+import com.android.server.wm.flicker.dsl.EventLogAssertionBuilder
import com.android.server.wm.flicker.dsl.EventLogAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.WmAssertionBuilder
import com.android.server.wm.flicker.dsl.WmAssertionBuilderLegacy
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper.Companion.NAV_BAR_LAYER_NAME
@@ -31,115 +34,88 @@
const val WALLPAPER_TITLE = "Wallpaper"
@JvmOverloads
-fun WmAssertionBuilderLegacy.statusBarWindowIsAlwaysVisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("statusBarWindowIsAlwaysVisible", bugId, enabled) {
- this.showsAboveAppWindow(STATUS_BAR_WINDOW_NAME)
+fun WmAssertionBuilder.statusBarWindowIsAlwaysVisible(bugId: Int = 0) {
+ all("statusBarWindowIsAlwaysVisible", bugId) {
+ this.showsAboveAppWindow(NAV_BAR_LAYER_NAME)
}
}
@JvmOverloads
-fun WmAssertionBuilderLegacy.navBarWindowIsAlwaysVisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("navBarWindowIsAlwaysVisible", bugId, enabled) {
- this.showsAboveAppWindow(NAV_BAR_WINDOW_NAME)
+fun WmAssertionBuilder.navBarWindowIsAlwaysVisible(bugId: Int = 0) {
+ all("navBarWindowIsAlwaysVisible", bugId) {
+ this.showsAboveAppWindow(NAV_BAR_LAYER_NAME)
}
}
-fun WmAssertionBuilderLegacy.visibleWindowsShownMoreThanOneConsecutiveEntry(
+fun WmAssertionBuilder.visibleWindowsShownMoreThanOneConsecutiveEntry(
ignoreWindows: List<String> = emptyList(),
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
+ bugId: Int = 0
) {
- all("visibleWindowsShownMoreThanOneConsecutiveEntry", bugId, enabled) {
+ all("visibleWindowsShownMoreThanOneConsecutiveEntry", bugId) {
this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoreWindows)
}
}
-fun WmAssertionBuilderLegacy.launcherReplacesAppWindowAsTopWindow(
- testApp: IAppHelper,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("launcherReplacesAppWindowAsTopWindow", bugId, enabled) {
+fun WmAssertionBuilder.launcherReplacesAppWindowAsTopWindow(testApp: IAppHelper, bugId: Int = 0) {
+ all("launcherReplacesAppWindowAsTopWindow", bugId) {
this.showsAppWindowOnTop(testApp.getPackage())
.then()
.showsAppWindowOnTop("Launcher")
}
}
-fun WmAssertionBuilderLegacy.wallpaperWindowBecomesVisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("wallpaperWindowBecomesVisible", bugId, enabled) {
+fun WmAssertionBuilder.wallpaperWindowBecomesVisible(bugId: Int = 0) {
+ all("wallpaperWindowBecomesVisible", bugId) {
this.hidesBelowAppWindow(WALLPAPER_TITLE)
.then()
.showsBelowAppWindow(WALLPAPER_TITLE)
}
}
-fun WmAssertionBuilderLegacy.wallpaperWindowBecomesInvisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("wallpaperWindowBecomesInvisible", bugId, enabled) {
+fun WmAssertionBuilder.wallpaperWindowBecomesInvisible(bugId: Int = 0) {
+ all("wallpaperWindowBecomesInvisible", bugId) {
this.showsBelowAppWindow("Wallpaper")
.then()
.hidesBelowAppWindow("Wallpaper")
}
}
-fun WmAssertionBuilderLegacy.appWindowAlwaysVisibleOnTop(
+fun WmAssertionBuilder.appWindowAlwaysVisibleOnTop(
packageName: String,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
+ bugId: Int = 0
) {
- all("appWindowAlwaysVisibleOnTop", bugId, enabled) {
+ all("appWindowAlwaysVisibleOnTop", bugId) {
this.showsAppWindowOnTop(packageName)
}
}
-fun WmAssertionBuilderLegacy.appWindowBecomesVisible(
- appName: String,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("appWindowBecomesVisible", bugId, enabled) {
+fun WmAssertionBuilder.appWindowBecomesVisible(appName: String, bugId: Int = 0) {
+ all("appWindowBecomesVisible", bugId) {
this.hidesAppWindow(appName)
.then()
.showsAppWindow(appName)
}
}
-fun WmAssertionBuilderLegacy.appWindowBecomesInVisible(
- appName: String,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("appWindowBecomesInVisible", bugId, enabled) {
+fun WmAssertionBuilder.appWindowBecomesInVisible(appName: String, bugId: Int = 0) {
+ all("appWindowBecomesInVisible", bugId) {
this.showsAppWindow(appName)
- .then()
- .hidesAppWindow(appName)
+ .then()
+ .hidesAppWindow(appName)
}
}
@JvmOverloads
-fun LayersAssertionBuilderLegacy.noUncoveredRegions(
+fun LayersAssertionBuilder.noUncoveredRegions(
beginRotation: Int,
endRotation: Int = beginRotation,
allStates: Boolean = true,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
+ bugId: Int = 0
) {
val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
val endingBounds = WindowUtils.getDisplayBounds(endRotation)
if (allStates) {
- all("noUncoveredRegions", bugId, enabled) {
+ all("noUncoveredRegions", bugId) {
if (startingBounds == endingBounds) {
this.coversAtLeastRegion(startingBounds)
} else {
@@ -159,6 +135,284 @@
}
@JvmOverloads
+fun LayersAssertionBuilder.navBarLayerIsAlwaysVisible(
+ rotatesScreen: Boolean = false,
+ bugId: Int = 0
+) {
+ if (rotatesScreen) {
+ all("navBarLayerIsAlwaysVisible", bugId) {
+ this.showsLayer(NAV_BAR_LAYER_NAME)
+ .then()
+ .hidesLayer(NAV_BAR_LAYER_NAME)
+ .then()
+ .showsLayer(NAV_BAR_LAYER_NAME)
+ }
+ } else {
+ all("navBarLayerIsAlwaysVisible", bugId) {
+ this.showsLayer(NAV_BAR_LAYER_NAME)
+ }
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.statusBarLayerIsAlwaysVisible(
+ rotatesScreen: Boolean = false,
+ bugId: Int = 0
+) {
+ if (rotatesScreen) {
+ all("statusBarLayerIsAlwaysVisible", bugId) {
+ this.showsLayer(STATUS_BAR_WINDOW_NAME)
+ .then()
+ hidesLayer(STATUS_BAR_WINDOW_NAME)
+ .then()
+ .showsLayer(STATUS_BAR_WINDOW_NAME)
+ }
+ } else {
+ all("statusBarLayerIsAlwaysVisible", bugId) {
+ this.showsLayer(STATUS_BAR_WINDOW_NAME)
+ }
+ }
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.navBarLayerRotatesAndScales(
+ beginRotation: Int,
+ endRotation: Int = beginRotation,
+ bugId: Int = 0
+) {
+ val startingPos = WindowUtils.getNavigationBarPosition(beginRotation)
+ val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
+
+ start("navBarLayerRotatesAndScales_StartingPos", bugId) {
+ this.hasVisibleRegion(NAV_BAR_LAYER_NAME, startingPos)
+ }
+ end("navBarLayerRotatesAndScales_EndingPost", bugId) {
+ this.hasVisibleRegion(NAV_BAR_LAYER_NAME, endingPos)
+ }
+
+ /*if (startingPos == endingPos) {
+ all("navBarLayerRotatesAndScales", enabled = false, bugId = 167747321) {
+ this.hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+ }
+ }*/
+}
+
+@JvmOverloads
+fun LayersAssertionBuilder.statusBarLayerRotatesScales(
+ beginRotation: Int,
+ endRotation: Int = beginRotation,
+ bugId: Int = 0
+) {
+ val startingPos = WindowUtils.getStatusBarPosition(beginRotation)
+ val endingPos = WindowUtils.getStatusBarPosition(endRotation)
+
+ start("statusBarLayerRotatesScales_StartingPos", bugId) {
+ this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, startingPos)
+ }
+ end("statusBarLayerRotatesScales_EndingPos", bugId) {
+ this.hasVisibleRegion(STATUS_BAR_WINDOW_NAME, endingPos)
+ }
+}
+
+fun LayersAssertionBuilder.visibleLayersShownMoreThanOneConsecutiveEntry(
+ ignoreLayers: List<String> = emptyList(),
+ bugId: Int = 0
+) {
+ all("visibleLayersShownMoreThanOneConsecutiveEntry", bugId) {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(ignoreLayers)
+ }
+}
+
+fun LayersAssertionBuilder.appLayerReplacesWallpaperLayer(appName: String, bugId: Int = 0) {
+ all("appLayerReplacesWallpaperLayer", bugId) {
+ this.showsLayer("Wallpaper")
+ .then()
+ .replaceVisibleLayer("Wallpaper", appName)
+ }
+}
+
+fun LayersAssertionBuilder.wallpaperLayerReplacesAppLayer(testApp: IAppHelper, bugId: Int = 0) {
+ all("appLayerReplacesWallpaperLayer", bugId) {
+ this.showsLayer(testApp.getPackage())
+ .then()
+ .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE)
+ }
+}
+
+fun LayersAssertionBuilder.layerAlwaysVisible(packageName: String, bugId: Int = 0) {
+ all("layerAlwaysVisible", bugId) {
+ this.showsLayer(packageName)
+ }
+}
+
+fun LayersAssertionBuilder.layerBecomesVisible(packageName: String, bugId: Int = 0) {
+ all("layerBecomesVisible", bugId) {
+ this.hidesLayer(packageName)
+ .then()
+ .showsLayer(packageName)
+ }
+}
+
+fun LayersAssertionBuilder.layerBecomesInvisible(packageName: String, bugId: Int = 0) {
+ all("layerBecomesInvisible", bugId) {
+ this.showsLayer(packageName)
+ .then()
+ .hidesLayer(packageName)
+ }
+}
+
+fun EventLogAssertionBuilder.focusChanges(vararg windows: String, bugId: Int = 0) {
+ all("focusChanges", bugId) {
+ this.focusChanges(windows)
+ }
+}
+
+fun EventLogAssertionBuilder.focusDoesNotChange(bugId: Int = 0) {
+ all("focusDoesNotChange", bugId) {
+ this.focusDoesNotChange()
+ }
+}
+
+@JvmOverloads
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.statusBarWindowIsAlwaysVisible(
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("statusBarWindowIsAlwaysVisible", bugId, enabled) {
+ this.showsAboveAppWindow(STATUS_BAR_WINDOW_NAME)
+ }
+}
+
+@JvmOverloads
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.navBarWindowIsAlwaysVisible(
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("navBarWindowIsAlwaysVisible", bugId, enabled) {
+ this.showsAboveAppWindow(NAV_BAR_WINDOW_NAME)
+ }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.visibleWindowsShownMoreThanOneConsecutiveEntry(
+ ignoreWindows: List<String> = emptyList(),
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("visibleWindowsShownMoreThanOneConsecutiveEntry", bugId, enabled) {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoreWindows)
+ }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.launcherReplacesAppWindowAsTopWindow(
+ testApp: IAppHelper,
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("launcherReplacesAppWindowAsTopWindow", bugId, enabled) {
+ this.showsAppWindowOnTop(testApp.getPackage())
+ .then()
+ .showsAppWindowOnTop("Launcher")
+ }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.wallpaperWindowBecomesVisible(
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("wallpaperWindowBecomesVisible", bugId, enabled) {
+ this.hidesBelowAppWindow(WALLPAPER_TITLE)
+ .then()
+ .showsBelowAppWindow(WALLPAPER_TITLE)
+ }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.wallpaperWindowBecomesInvisible(
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("wallpaperWindowBecomesInvisible", bugId, enabled) {
+ this.showsBelowAppWindow("Wallpaper")
+ .then()
+ .hidesBelowAppWindow("Wallpaper")
+ }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.appWindowAlwaysVisibleOnTop(
+ packageName: String,
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("appWindowAlwaysVisibleOnTop", bugId, enabled) {
+ this.showsAppWindowOnTop(packageName)
+ }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.appWindowBecomesVisible(
+ appName: String,
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("appWindowBecomesVisible", bugId, enabled) {
+ this.hidesAppWindow(appName)
+ .then()
+ .showsAppWindow(appName)
+ }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+fun WmAssertionBuilderLegacy.appWindowBecomesInVisible(
+ appName: String,
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ all("appWindowBecomesInVisible", bugId, enabled) {
+ this.showsAppWindow(appName)
+ .then()
+ .hidesAppWindow(appName)
+ }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+@JvmOverloads
+fun LayersAssertionBuilderLegacy.noUncoveredRegions(
+ beginRotation: Int,
+ endRotation: Int = beginRotation,
+ allStates: Boolean = true,
+ bugId: Int = 0,
+ enabled: Boolean = bugId == 0
+) {
+ val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
+ val endingBounds = WindowUtils.getDisplayBounds(endRotation)
+ if (allStates) {
+ all("noUncoveredRegions", bugId, enabled) {
+ if (startingBounds == endingBounds) {
+ this.coversAtLeastRegion(startingBounds)
+ } else {
+ this.coversAtLeastRegion(startingBounds)
+ .then()
+ .coversAtLeastRegion(endingBounds)
+ }
+ }
+ } else {
+ start("noUncoveredRegions_StartingPos") {
+ this.coversAtLeastRegion(startingBounds)
+ }
+ end("noUncoveredRegions_EndingPos") {
+ this.coversAtLeastRegion(endingBounds)
+ }
+ }
+}
+
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
+@JvmOverloads
fun LayersAssertionBuilderLegacy.navBarLayerIsAlwaysVisible(
rotatesScreen: Boolean = false,
bugId: Int = 0,
@@ -167,10 +421,10 @@
if (rotatesScreen) {
all("navBarLayerIsAlwaysVisible", bugId, enabled) {
this.showsLayer(NAV_BAR_LAYER_NAME)
- .then()
- .hidesLayer(NAV_BAR_LAYER_NAME)
- .then()
- .showsLayer(NAV_BAR_LAYER_NAME)
+ .then()
+ .hidesLayer(NAV_BAR_LAYER_NAME)
+ .then()
+ .showsLayer(NAV_BAR_LAYER_NAME)
}
} else {
all("navBarLayerIsAlwaysVisible", bugId, enabled) {
@@ -179,6 +433,7 @@
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
@JvmOverloads
fun LayersAssertionBuilderLegacy.statusBarLayerIsAlwaysVisible(
rotatesScreen: Boolean = false,
@@ -188,10 +443,10 @@
if (rotatesScreen) {
all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
this.showsLayer(STATUS_BAR_LAYER_NAME)
- .then()
- hidesLayer(STATUS_BAR_LAYER_NAME)
- .then()
- .showsLayer(STATUS_BAR_LAYER_NAME)
+ .then()
+ .hidesLayer(STATUS_BAR_LAYER_NAME)
+ .then()
+ .showsLayer(STATUS_BAR_LAYER_NAME)
}
} else {
all("statusBarLayerIsAlwaysVisible", bugId, enabled) {
@@ -200,6 +455,7 @@
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
@JvmOverloads
fun LayersAssertionBuilderLegacy.navBarLayerRotatesAndScales(
beginRotation: Int,
@@ -224,6 +480,7 @@
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
@JvmOverloads
fun LayersAssertionBuilderLegacy.statusBarLayerRotatesScales(
beginRotation: Int,
@@ -242,8 +499,9 @@
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun LayersAssertionBuilderLegacy.visibleLayersShownMoreThanOneConsecutiveEntry(
- ignoreLayers: List<String> = emptyList(),
+ ignoreLayers: List<String> = kotlin.collections.emptyList(),
bugId: Int = 0,
enabled: Boolean = bugId == 0
) {
@@ -252,6 +510,7 @@
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun LayersAssertionBuilderLegacy.appLayerReplacesWallpaperLayer(
appName: String,
bugId: Int = 0,
@@ -259,11 +518,12 @@
) {
all("appLayerReplacesWallpaperLayer", bugId, enabled) {
this.showsLayer("Wallpaper")
- .then()
- .replaceVisibleLayer("Wallpaper", appName)
+ .then()
+ .replaceVisibleLayer("Wallpaper", appName)
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun LayersAssertionBuilderLegacy.wallpaperLayerReplacesAppLayer(
testApp: IAppHelper,
bugId: Int = 0,
@@ -271,11 +531,12 @@
) {
all("appLayerReplacesWallpaperLayer", bugId, enabled) {
this.showsLayer(testApp.getPackage())
- .then()
- .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE)
+ .then()
+ .replaceVisibleLayer(testApp.getPackage(), WALLPAPER_TITLE)
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun LayersAssertionBuilderLegacy.layerAlwaysVisible(
packageName: String,
bugId: Int = 0,
@@ -286,6 +547,7 @@
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun LayersAssertionBuilderLegacy.layerBecomesVisible(
packageName: String,
bugId: Int = 0,
@@ -293,11 +555,12 @@
) {
all("layerBecomesVisible", bugId, enabled) {
this.hidesLayer(packageName)
- .then()
- .showsLayer(packageName)
+ .then()
+ .showsLayer(packageName)
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun LayersAssertionBuilderLegacy.layerBecomesInvisible(
packageName: String,
bugId: Int = 0,
@@ -305,11 +568,12 @@
) {
all("layerBecomesInvisible", bugId, enabled) {
this.showsLayer(packageName)
- .then()
- .hidesLayer(packageName)
+ .then()
+ .hidesLayer(packageName)
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun EventLogAssertionBuilderLegacy.focusChanges(
vararg windows: String,
bugId: Int = 0,
@@ -320,6 +584,7 @@
}
}
+@Deprecated("Move the assertion into one of the specific blocks (presubmit, postsubmit, flaky)")
fun EventLogAssertionBuilderLegacy.focusDoesNotChange(
bugId: Int = 0,
enabled: Boolean = bugId == 0
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index b5fd4a5..c507841 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.close
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -50,7 +49,6 @@
* Test app closes by pressing back button
* To run this test: `atest FlickerTests:CloseAppBackButtonTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -65,7 +63,7 @@
val testApp = SimpleAppHelper(instrumentation)
return FlickerTestRunnerFactory.getInstance()
.buildTest(instrumentation, repetitions = 5) { configuration ->
- withTestName { buildTestTag("closeAppBackButton", configuration) }
+ withTestName { buildTestTag(configuration) }
repeat { configuration.repetitions }
setup {
test {
@@ -89,29 +87,47 @@
}
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173684672)
+ val isRotated = configuration.startRotation.isRotated()
- launcherReplacesAppWindowAsTopWindow(testApp)
- wallpaperWindowBecomesVisible()
+ presubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ launcherReplacesAppWindowAsTopWindow(testApp)
+ wallpaperWindowBecomesVisible()
+ }
+
+ layersTrace {
+ noUncoveredRegions(configuration.startRotation,
+ Surface.ROTATION_0)
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ wallpaperLayerReplacesAppLayer(testApp)
+
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ }
+ }
}
- layersTrace {
- noUncoveredRegions(configuration.startRotation,
- Surface.ROTATION_0)
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0,
- enabled = !configuration.startRotation.isRotated())
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173684672)
+ flaky {
+ windowManagerTrace {
+ visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173684672)
+ }
- wallpaperLayerReplacesAppLayer(testApp)
+ layersTrace {
+ visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173684672)
+
+ if (isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ }
+ }
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 584e4b1..d1c3efe 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -37,6 +37,7 @@
import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.helpers.buildTestTag
+import com.android.server.wm.flicker.helpers.isRotated
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
@@ -64,7 +65,7 @@
val testApp = SimpleAppHelper(instrumentation)
return FlickerTestRunnerFactory.getInstance()
.buildTest(instrumentation, repetitions = 5) { configuration ->
- withTestName { buildTestTag("closeAppHomeButton", configuration) }
+ withTestName { buildTestTag(configuration) }
repeat { configuration.repetitions }
setup {
test {
@@ -88,28 +89,46 @@
}
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173689015)
+ val isRotated = configuration.startRotation.isRotated()
- launcherReplacesAppWindowAsTopWindow(testApp)
- wallpaperWindowBecomesVisible()
+ presubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ launcherReplacesAppWindowAsTopWindow(testApp)
+ wallpaperWindowBecomesVisible()
+ }
+
+ layersTrace {
+ noUncoveredRegions(configuration.startRotation,
+ Surface.ROTATION_0)
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ wallpaperLayerReplacesAppLayer(testApp)
+
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ }
+ }
}
- layersTrace {
- val isRotation0 = configuration.startRotation == Surface.ROTATION_0
- noUncoveredRegions(configuration.startRotation,
- Surface.ROTATION_0)
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0, enabled = isRotation0)
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0, enabled = isRotation0)
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173689015)
+ flaky {
+ windowManagerTrace {
+ visibleWindowsShownMoreThanOneConsecutiveEntry(bugId = 173689015)
+ }
+ layersTrace {
+ visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 173689015)
- wallpaperLayerReplacesAppLayer(testApp)
+ if (isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ }
+ }
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
index 1a47449..323236e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
@@ -43,7 +43,7 @@
wmHelper.waitForAppTransitionIdle()
// Ensure WindowManagerService wait until all animations have completed
- instrumentation.getUiAutomation().syncInputTransactions()
+ instrumentation.uiAutomation.syncInputTransactions()
} catch (e: RemoteException) {
throw RuntimeException(e)
}
@@ -71,8 +71,8 @@
/**
* Build a test tag for the test
* @param testName Name of the transition(s) being tested
- * @param app App being launcher
* @param configuration Configuration for the test
+ * @param extraInfo Additional information to append to the tag
*
* @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
</END_ROTATION></BEGIN_ROTATION></APP></NAME> */
@@ -92,9 +92,30 @@
/**
* Build a test tag for the test
+ * @param configuration Configuration for the test
+ * @param extraInfo Additional information to append to the tag
+ *
+ * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
+</END_ROTATION></BEGIN_ROTATION></APP></NAME> */
+@JvmOverloads
+fun buildTestTag(
+ configuration: Bundle,
+ extraInfo: String = ""
+): String {
+ return buildTestTag(testName = null,
+ app = null,
+ beginRotation = configuration.startRotation,
+ endRotation = configuration.endRotation,
+ app2 = null,
+ extraInfo = extraInfo)
+}
+
+/**
+ * Build a test tag for the test
* @param testName Name of the transition(s) being tested
* @param app App being launcher
* @param configuration Configuration for the test
+ * @param extraInfo Additional information to append to the tag
*
* @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
</END_ROTATION></BEGIN_ROTATION></APP></NAME> */
@@ -121,14 +142,17 @@
* @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>]
</EXTRA></NAME> */
fun buildTestTag(
- testName: String,
+ testName: String?,
app: String?,
beginRotation: Int,
endRotation: Int,
app2: String?,
extraInfo: String
): String {
- var testTag = testName
+ var testTag = ""
+ if (testName != null) {
+ testTag += testName
+ }
if (app != null) {
testTag += "__$app"
}
@@ -142,5 +166,9 @@
if (extraInfo.isNotEmpty()) {
testTag += "__$extraInfo"
}
+
+ if (testTag.startsWith("__")) {
+ testTag = testTag.drop(2)
+ }
return testTag
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index fde97ba..c7736f8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -16,8 +16,6 @@
package com.android.server.wm.flicker.ime
-import androidx.test.filters.FlakyTest
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -48,11 +46,9 @@
* Test IME window closing back to app window transitions.
* To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 178015460)
class CloseImeAutoOpenWindowToAppTest(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
@@ -66,7 +62,7 @@
.buildTest(instrumentation, repetitions = 5) { configuration ->
val testApp = ImeAppAutoFocusHelper(instrumentation,
configuration.startRotation)
- withTestName { buildTestTag("imeToAppAutoOpen", configuration) }
+ withTestName { buildTestTag(configuration) }
repeat { configuration.repetitions }
setup {
test {
@@ -89,26 +85,39 @@
testApp.closeIME(device, wmHelper)
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(listOf("InputMethod"))
+ val isRotated = configuration.startRotation.isRotated()
- imeAppWindowIsAlwaysVisible(testApp)
+ postsubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE))
+ imeAppWindowIsAlwaysVisible(testApp)
+ }
+
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ noUncoveredRegions(configuration.startRotation)
+ imeLayerBecomesInvisible()
+ imeAppLayerIsAlwaysVisible(testApp)
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation)
+ statusBarLayerRotatesScales(configuration.startRotation)
+ }
+ }
}
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.startRotation)
- navBarLayerRotatesAndScales(configuration.startRotation,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(configuration.startRotation,
- enabled = !configuration.startRotation.isRotated())
- visibleLayersShownMoreThanOneConsecutiveEntry()
+ flaky {
+ layersTrace {
+ visibleLayersShownMoreThanOneConsecutiveEntry()
- imeLayerBecomesInvisible()
- imeAppLayerIsAlwaysVisible(testApp)
+ if (isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation)
+ statusBarLayerRotatesScales(configuration.startRotation)
+ }
+ }
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index ab7c08f..aa24456 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.ime
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -47,7 +46,6 @@
* Test IME window closing back to app window transitions.
* To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -65,7 +63,7 @@
val testApp = ImeAppAutoFocusHelper(instrumentation,
configuration.startRotation)
withTestName {
- buildTestTag("imeToHomeAutoOpen", configuration)
+ buildTestTag(configuration)
}
repeat { configuration.repetitions }
setup {
@@ -90,32 +88,50 @@
wmHelper.waitImeWindowGone()
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ val isRotated = configuration.startRotation.isRotated()
- imeWindowBecomesInvisible()
- imeAppWindowBecomesInvisible(testApp)
+ presubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE))
+
+ imeWindowBecomesInvisible()
+ imeAppWindowBecomesInvisible(testApp)
+ }
+
+ layersTrace {
+ noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
+ imeLayerBecomesInvisible()
+ imeAppLayerBecomesInvisible(testApp)
+
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE))
+ }
+ }
}
- layersTrace {
- noUncoveredRegions(configuration.startRotation, Surface.ROTATION_0)
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0,
- enabled = !configuration.startRotation.isRotated())
- navBarLayerIsAlwaysVisible(
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerIsAlwaysVisible(
- enabled = !configuration.startRotation.isRotated())
- visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE),
- enabled = !configuration.startRotation.isRotated())
-
- imeLayerBecomesInvisible()
- imeAppLayerBecomesInvisible(testApp)
+ flaky {
+ layersTrace {
+ if (isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE))
+ }
+ }
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 0503cce0..2bd5abb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -16,8 +16,6 @@
package com.android.server.wm.flicker.ime
-import androidx.test.filters.FlakyTest
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -47,11 +45,9 @@
* Test IME window closing back to app window transitions.
* To run this test: `atest FlickerTests:CloseImeWindowToAppTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 178015460)
class CloseImeWindowToAppTest(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
@@ -64,7 +60,7 @@
val testApp = ImeAppHelper(instrumentation)
return FlickerTestRunnerFactory.getInstance()
.buildTest(instrumentation, repetitions = 5) { configuration ->
- withTestName { buildTestTag("imeToApp", configuration) }
+ withTestName { buildTestTag(configuration) }
repeat { configuration.repetitions }
setup {
test {
@@ -86,24 +82,25 @@
testApp.closeIME(device, wmHelper)
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(listOf("InputMethod"))
+ postsubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE))
+ imeAppWindowIsAlwaysVisible(testApp)
+ }
- imeAppWindowIsAlwaysVisible(testApp)
- }
-
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.startRotation)
- navBarLayerRotatesAndScales(configuration.startRotation)
- statusBarLayerRotatesScales(configuration.startRotation)
- visibleLayersShownMoreThanOneConsecutiveEntry()
-
- imeLayerBecomesInvisible()
- imeAppLayerIsAlwaysVisible(testApp)
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ noUncoveredRegions(configuration.startRotation)
+ navBarLayerRotatesAndScales(configuration.startRotation)
+ statusBarLayerRotatesScales(configuration.startRotation)
+ visibleLayersShownMoreThanOneConsecutiveEntry()
+ imeLayerBecomesInvisible()
+ imeAppLayerIsAlwaysVisible(testApp)
+ }
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 9cb075b..7b61bb5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -16,8 +16,6 @@
package com.android.server.wm.flicker.ime
-import androidx.test.filters.FlakyTest
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -47,11 +45,9 @@
* Test IME window closing to home transitions.
* To run this test: `atest FlickerTests:CloseImeWindowToHomeTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 178015460)
class CloseImeWindowToHomeTest(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
@@ -63,7 +59,7 @@
val testApp = ImeAppHelper(instrumentation)
return FlickerTestRunnerFactory.getInstance()
.buildTest(instrumentation, repetitions = 5) { configuration ->
- withTestName { buildTestTag("imeToHome", configuration) }
+ withTestName { buildTestTag(configuration) }
repeat { configuration.repetitions }
setup {
test {
@@ -91,31 +87,47 @@
}
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ val isRotated = configuration.startRotation.isRotated()
- imeWindowBecomesInvisible()
- imeAppWindowBecomesInvisible(testApp)
+ postsubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE))
+ imeWindowBecomesInvisible()
+ imeAppWindowBecomesInvisible(testApp)
+ }
+
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ imeLayerBecomesInvisible()
+ imeAppLayerBecomesInvisible(testApp)
+ noUncoveredRegions(configuration.startRotation,
+ Surface.ROTATION_0)
+
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ }
+ }
}
- layersTrace {
- noUncoveredRegions(configuration.startRotation,
- Surface.ROTATION_0)
- navBarLayerRotatesAndScales(configuration.startRotation,
- Surface.ROTATION_0,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(configuration.startRotation,
- Surface.ROTATION_0,
- enabled = !configuration.startRotation.isRotated())
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE),
- enabled = false)
+ flaky {
+ layersTrace {
+ visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE))
- imeLayerBecomesInvisible()
- imeAppLayerBecomesInvisible(testApp)
+ if (isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ Surface.ROTATION_0)
+ }
+ }
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index c775cb8..cfdd856 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -17,106 +17,82 @@
package com.android.server.wm.flicker.ime
import android.platform.helpers.IAppHelper
-import com.android.server.wm.flicker.dsl.LayersAssertionBuilderLegacy
-import com.android.server.wm.flicker.dsl.WmAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.LayersAssertionBuilder
+import com.android.server.wm.flicker.dsl.WmAssertionBuilder
const val IME_WINDOW_TITLE = "InputMethod"
@JvmOverloads
-fun LayersAssertionBuilderLegacy.imeLayerBecomesVisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("imeLayerBecomesVisible", bugId, enabled) {
+fun LayersAssertionBuilder.imeLayerBecomesVisible(bugId: Int = 0) {
+ all("imeLayerBecomesVisible", bugId) {
this.hidesLayer(IME_WINDOW_TITLE)
.then()
.showsLayer(IME_WINDOW_TITLE)
}
}
-fun LayersAssertionBuilderLegacy.imeLayerBecomesInvisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("imeLayerBecomesInvisible", bugId, enabled) {
+@JvmOverloads
+fun LayersAssertionBuilder.imeLayerBecomesInvisible(bugId: Int = 0) {
+ all("imeLayerBecomesInvisible", bugId) {
this.showsLayer(IME_WINDOW_TITLE)
.then()
.hidesLayer(IME_WINDOW_TITLE)
}
}
-fun LayersAssertionBuilderLegacy.imeAppLayerIsAlwaysVisible(
- testApp: IAppHelper,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("imeAppLayerIsAlwaysVisible", bugId, enabled) {
+@JvmOverloads
+fun LayersAssertionBuilder.imeAppLayerIsAlwaysVisible(testApp: IAppHelper, bugId: Int = 0) {
+ all("imeAppLayerIsAlwaysVisible", bugId) {
this.showsLayer(testApp.getPackage())
}
}
-fun WmAssertionBuilderLegacy.imeAppWindowIsAlwaysVisible(
- testApp: IAppHelper,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("imeAppWindowIsAlwaysVisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeAppWindowIsAlwaysVisible(testApp: IAppHelper, bugId: Int = 0) {
+ all("imeAppWindowIsAlwaysVisible", bugId) {
this.showsAppWindowOnTop(testApp.getPackage())
}
}
-fun WmAssertionBuilderLegacy.imeWindowBecomesVisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("imeWindowBecomesVisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeWindowBecomesVisible(bugId: Int = 0) {
+ all("imeWindowBecomesVisible", bugId) {
this.hidesNonAppWindow(IME_WINDOW_TITLE)
.then()
.showsNonAppWindow(IME_WINDOW_TITLE)
}
}
-fun WmAssertionBuilderLegacy.imeWindowBecomesInvisible(
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("imeWindowBecomesInvisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeWindowBecomesInvisible(bugId: Int = 0) {
+ all("imeWindowBecomesInvisible", bugId) {
this.showsNonAppWindow(IME_WINDOW_TITLE)
.then()
.hidesNonAppWindow(IME_WINDOW_TITLE)
}
}
-fun WmAssertionBuilderLegacy.imeAppWindowBecomesVisible(
- windowName: String,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("imeAppWindowBecomesVisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeAppWindowBecomesVisible(windowName: String, bugId: Int = 0) {
+ all("imeAppWindowBecomesVisible", bugId) {
this.hidesAppWindow(windowName)
.then()
.showsAppWindow(windowName)
}
}
-fun WmAssertionBuilderLegacy.imeAppWindowBecomesInvisible(
- testApp: IAppHelper,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("imeAppWindowBecomesInvisible", bugId, enabled) {
+@JvmOverloads
+fun WmAssertionBuilder.imeAppWindowBecomesInvisible(testApp: IAppHelper, bugId: Int = 0) {
+ all("imeAppWindowBecomesInvisible", bugId) {
this.showsAppWindowOnTop(testApp.getPackage())
.then()
.appWindowNotOnTop(testApp.getPackage())
}
}
-fun LayersAssertionBuilderLegacy.imeAppLayerBecomesInvisible(
- testApp: IAppHelper,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("imeAppLayerBecomesInvisible", bugId, enabled) {
+@JvmOverloads
+fun LayersAssertionBuilder.imeAppLayerBecomesInvisible(testApp: IAppHelper, bugId: Int = 0) {
+ all("imeAppLayerBecomesInvisible", bugId) {
this.skipUntilFirstAssertion()
.showsLayer(testApp.getPackage())
.then()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index 13c6cd7..9e94d6e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -16,8 +16,6 @@
package com.android.server.wm.flicker.ime
-import androidx.test.filters.FlakyTest
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -50,11 +48,9 @@
* Test IME window opening transitions.
* To run this test: `atest FlickerTests:OpenImeWindowTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-@FlakyTest(bugId = 178015460)
class OpenImeWindowTest(
testSpec: FlickerTestRunnerFactory.TestSpec
) : FlickerTestRunner(testSpec) {
@@ -66,7 +62,7 @@
val testApp = ImeAppHelper(instrumentation)
return FlickerTestRunnerFactory.getInstance()
.buildTest(instrumentation, repetitions = 5) { configuration ->
- withTestName { buildTestTag("openIme", configuration) }
+ withTestName { buildTestTag(configuration) }
repeat { configuration.repetitions }
setup {
test {
@@ -88,27 +84,43 @@
}
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry()
+ val isRotated = configuration.startRotation.isRotated()
- imeWindowBecomesVisible()
- appWindowAlwaysVisibleOnTop(testApp.`package`)
+ postsubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+
+ imeWindowBecomesVisible()
+ appWindowAlwaysVisibleOnTop(testApp.`package`)
+ }
+
+ layersTrace {
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ noUncoveredRegions(configuration.startRotation)
+ imeLayerBecomesVisible()
+ layerAlwaysVisible(testApp.`package`)
+
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation)
+ statusBarLayerRotatesScales(configuration.startRotation)
+ }
+ }
}
- layersTrace {
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- noUncoveredRegions(configuration.startRotation)
- navBarLayerRotatesAndScales(configuration.startRotation,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(configuration.startRotation,
- enabled = !configuration.startRotation.isRotated())
- visibleLayersShownMoreThanOneConsecutiveEntry()
+ flaky {
+ windowManagerTrace {
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+ layersTrace {
+ visibleLayersShownMoreThanOneConsecutiveEntry()
- imeLayerBecomesVisible()
- layerAlwaysVisible(testApp.`package`)
+ if (isRotated) {
+ navBarLayerRotatesAndScales(configuration.startRotation)
+ statusBarLayerRotatesScales(configuration.startRotation)
+ }
+ }
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index 6cc64df..2fe49af 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.ime
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -52,7 +51,6 @@
* Test IME window opening transitions.
* To run this test: `atest FlickerTests:ReOpenImeWindowTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -67,34 +65,37 @@
val testAppComponentName = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME
return FlickerTestRunnerFactory.getInstance()
.buildTest(instrumentation, repetitions = 1) { configuration ->
- val testApp = ImeAppAutoFocusHelper(instrumentation,
- configuration.startRotation)
- withTestName { buildTestTag("reOpenImeAutoFocus", configuration) }
- repeat { configuration.repetitions }
- setup {
- test {
- device.wakeUpAndGoToHomeScreen()
- testApp.launchViaIntent(wmHelper)
- testApp.openIME(device, wmHelper)
- }
- eachRun {
- device.pressRecentApps()
- wmHelper.waitImeWindowGone()
- wmHelper.waitForAppTransitionIdle()
- this.setRotation(configuration.startRotation)
- }
+ val testApp = ImeAppAutoFocusHelper(instrumentation,
+ configuration.startRotation)
+ withTestName { buildTestTag(configuration) }
+ repeat { configuration.repetitions }
+ setup {
+ test {
+ device.wakeUpAndGoToHomeScreen()
+ testApp.launchViaIntent(wmHelper)
+ testApp.openIME(device, wmHelper)
}
- transitions {
- device.reopenAppFromOverview()
- wmHelper.waitImeWindowShown()
+ eachRun {
+ device.pressRecentApps()
+ wmHelper.waitImeWindowGone()
+ wmHelper.waitForAppTransitionIdle()
+ this.setRotation(configuration.startRotation)
}
- teardown {
- test {
- this.setRotation(Surface.ROTATION_0)
- testApp.exit()
- }
+ }
+ transitions {
+ device.reopenAppFromOverview()
+ wmHelper.waitImeWindowShown()
+ }
+ teardown {
+ test {
+ this.setRotation(Surface.ROTATION_0)
+ testApp.exit()
}
- assertions {
+ }
+ assertions {
+ val isRotated = configuration.startRotation.isRotated()
+
+ presubmit {
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
@@ -107,21 +108,34 @@
layersTrace {
noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(Surface.ROTATION_0,
- configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
statusBarLayerIsAlwaysVisible()
navBarLayerIsAlwaysVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(enabled = false)
-
imeLayerBecomesVisible()
appLayerReplacesWallpaperLayer(testAppComponentName.className)
+
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ }
+ }
+ }
+
+ flaky {
+ layersTrace {
+ visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ if (isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ }
}
}
}
+ }
}
}
}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
index 1bd1190..be3fa5f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/CommonAssertions.kt
@@ -17,14 +17,10 @@
package com.android.server.wm.flicker.launch
import android.platform.helpers.IAppHelper
-import com.android.server.wm.flicker.dsl.WmAssertionBuilderLegacy
+import com.android.server.wm.flicker.dsl.WmAssertionBuilder
-fun WmAssertionBuilderLegacy.appWindowReplacesLauncherAsTopWindow(
- testApp: IAppHelper,
- bugId: Int = 0,
- enabled: Boolean = bugId == 0
-) {
- all("appWindowReplacesLauncherAsTopWindow", bugId, enabled) {
+fun WmAssertionBuilder.appWindowReplacesLauncherAsTopWindow(testApp: IAppHelper, bugId: Int = 0) {
+ all("appWindowReplacesLauncherAsTopWindow", bugId) {
this.showsAppWindowOnTop("Launcher")
.then()
.showsAppWindowOnTop("Snapshot", testApp.getPackage())
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 010ebf2..0ec0b04 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -51,7 +50,6 @@
* Test cold launch app from launcher.
* To run this test: `atest FlickerTests:OpenAppColdTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -66,7 +64,7 @@
val testApp = SimpleAppHelper(instrumentation)
return FlickerTestRunnerFactory.getInstance()
.buildTest(instrumentation) { configuration ->
- withTestName { buildTestTag("openAppCold", testApp, configuration) }
+ withTestName { buildTestTag(configuration) }
repeat { configuration.repetitions }
setup {
test {
@@ -88,33 +86,48 @@
}
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry()
+ val isRotated = configuration.startRotation.isRotated()
- appWindowReplacesLauncherAsTopWindow(testApp)
- wallpaperWindowBecomesInvisible()
+ presubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
+ appWindowReplacesLauncherAsTopWindow(testApp)
+ wallpaperWindowBecomesInvisible()
+ }
+
+ layersTrace {
+ // During testing the launcher is always in portrait mode
+ noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ appLayerReplacesWallpaperLayer(testApp.`package`)
+
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ }
+ }
+
+ eventLog {
+ focusChanges("NexusLauncherActivity", testApp.`package`)
+ }
}
- layersTrace {
- // During testing the launcher is always in portrait mode
- noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(Surface.ROTATION_0,
- configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(enabled = false)
+ flaky {
+ layersTrace {
+ visibleLayersShownMoreThanOneConsecutiveEntry()
- appLayerReplacesWallpaperLayer(testApp.`package`)
- }
-
- eventLog {
- focusChanges("NexusLauncherActivity", testApp.`package`)
+ if (isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ }
+ }
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 5e08921..84cc8e3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -52,7 +51,6 @@
* Launch an app from the recents app view (the overview)
* To run this test: `atest FlickerTests:OpenAppFromOverviewTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -67,7 +65,7 @@
val testApp = SimpleAppHelper(instrumentation)
return FlickerTestRunnerFactory.getInstance()
.buildTest(instrumentation, repetitions = 5) { configuration ->
- withTestName { buildTestTag("openAppFromOverview", configuration) }
+ withTestName { buildTestTag(configuration) }
repeat { configuration.repetitions }
setup {
test {
@@ -92,36 +90,57 @@
}
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry()
+ val isRotated = configuration.startRotation.isRotated()
- appWindowReplacesLauncherAsTopWindow(testApp)
- wallpaperWindowBecomesInvisible()
+ presubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ appWindowReplacesLauncherAsTopWindow(testApp)
+ wallpaperWindowBecomesInvisible()
+ }
+
+ layersTrace {
+ appLayerReplacesWallpaperLayer(testApp.`package`)
+
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ } else {
+ statusBarLayerIsAlwaysVisible()
+ navBarLayerIsAlwaysVisible()
+ }
+ }
+
+ eventLog {
+ focusChanges("NexusLauncherActivity", testApp.`package`)
+ }
}
- layersTrace {
- noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation,
- bugId = 141361128)
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(Surface.ROTATION_0,
- configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerIsAlwaysVisible(
- enabled = Surface.ROTATION_0 == configuration.endRotation)
- navBarLayerIsAlwaysVisible(
- enabled = Surface.ROTATION_0 == configuration.endRotation)
- visibleLayersShownMoreThanOneConsecutiveEntry(
- enabled = false)
-
- appLayerReplacesWallpaperLayer(testApp.`package`)
+ postsubmit {
+ windowManagerTrace {
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
}
- eventLog {
- focusChanges("NexusLauncherActivity", testApp.`package`)
+ flaky {
+ layersTrace {
+ visibleLayersShownMoreThanOneConsecutiveEntry()
+ noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation,
+ bugId = 141361128)
+
+ if (isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ } else {
+ statusBarLayerIsAlwaysVisible()
+ navBarLayerIsAlwaysVisible()
+ }
+ }
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index 092cd4d..1f375a5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker.launch
-import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
@@ -51,7 +50,6 @@
* Test warm launch app.
* To run this test: `atest FlickerTests:OpenAppWarmTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -64,7 +62,7 @@
val testApp = SimpleAppHelper(instrumentation)
return FlickerTestRunnerFactory.getInstance()
.buildTest(instrumentation) { configuration ->
- withTestName { buildTestTag("openAppWarm", testApp, configuration) }
+ withTestName { buildTestTag(configuration) }
repeat { configuration.repetitions }
setup {
test {
@@ -91,33 +89,49 @@
}
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry()
+ val isRotated = configuration.startRotation.isRotated()
- appWindowReplacesLauncherAsTopWindow(testApp)
- wallpaperWindowBecomesInvisible()
+ presubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ appWindowReplacesLauncherAsTopWindow(testApp)
+ wallpaperWindowBecomesInvisible()
+ }
+
+ layersTrace {
+ // During testing the launcher is always in portrait mode
+ noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
+ navBarLayerIsAlwaysVisible()
+ statusBarLayerIsAlwaysVisible()
+ appLayerReplacesWallpaperLayer(testApp.`package`)
+
+ if (!isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ }
+ }
+
+ eventLog {
+ focusChanges("NexusLauncherActivity", testApp.`package`)
+ }
}
- layersTrace {
- // During testing the launcher is always in portrait mode
- noUncoveredRegions(Surface.ROTATION_0, configuration.endRotation)
- navBarLayerRotatesAndScales(Surface.ROTATION_0,
- configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
- statusBarLayerRotatesScales(Surface.ROTATION_0,
- configuration.endRotation,
- enabled = !configuration.startRotation.isRotated())
- navBarLayerIsAlwaysVisible()
- statusBarLayerIsAlwaysVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(enabled = false)
+ flaky {
+ layersTrace {
+ visibleLayersShownMoreThanOneConsecutiveEntry()
- appLayerReplacesWallpaperLayer(testApp.`package`)
- }
-
- eventLog {
- focusChanges("NexusLauncherActivity", testApp.`package`)
+ if (isRotated) {
+ navBarLayerRotatesAndScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(Surface.ROTATION_0,
+ configuration.endRotation)
+ }
+ }
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 1c44b21..7bfdd96 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -17,7 +17,6 @@
package com.android.server.wm.flicker.rotation
import android.os.Bundle
-import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerTestRunner
@@ -48,7 +47,6 @@
* Cycle through supported app rotations.
* To run this test: `atest FlickerTests:ChangeAppRotationTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -67,51 +65,56 @@
@JvmStatic
fun getParams(): Collection<Array<Any>> {
val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
- withTestName { buildTestTag("changeAppRotation", configuration) }
+ withTestName { buildTestTag(configuration) }
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible()
- statusBarWindowIsAlwaysVisible()
- visibleWindowsShownMoreThanOneConsecutiveEntry()
- }
-
- 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)
- visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 140855415)
- }
-
- layersTrace {
- val startingPos = WindowUtils.getDisplayBounds(
- configuration.startRotation)
- val endingPos = WindowUtils.getDisplayBounds(
- configuration.endRotation)
-
- start("appLayerRotates_StartingPos", bugId = 140855415) {
- this.hasVisibleRegion(testApp.getPackage(), startingPos)
+ presubmit {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible()
+ statusBarWindowIsAlwaysVisible()
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
}
- end("appLayerRotates_EndingPos", bugId = 140855415) {
- this.hasVisibleRegion(testApp.getPackage(), endingPos)
- }
+ layersTrace {
+ noUncoveredRegions(configuration.startRotation,
+ configuration.endRotation, allStates = false)
- all("screenshotLayerBecomesInvisible") {
- this.showsLayer(testApp.getPackage())
- .then()
- .showsLayer(SCREENSHOT_LAYER)
- .then()
- .showsLayer(testApp.getPackage())
+ all("screenshotLayerBecomesInvisible") {
+ this.showsLayer(testApp.getPackage())
+ .then()
+ .showsLayer(SCREENSHOT_LAYER)
+ .then()
+ .showsLayer(testApp.getPackage())
+ }
}
}
- eventLog {
- focusDoesNotChange(bugId = 151179149)
+ flaky {
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ statusBarLayerIsAlwaysVisible(bugId = 140855415)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ configuration.endRotation, bugId = 140855415)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ configuration.endRotation, bugId = 140855415)
+ visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 140855415)
+
+ val startingPos = WindowUtils.getDisplayBounds(
+ configuration.startRotation)
+ val endingPos = WindowUtils.getDisplayBounds(
+ configuration.endRotation)
+
+ start("appLayerRotates_StartingPos", bugId = 140855415) {
+ this.hasVisibleRegion(testApp.getPackage(), startingPos)
+ }
+
+ end("appLayerRotates_EndingPos", bugId = 140855415) {
+ this.hasVisibleRegion(testApp.getPackage(), endingPos)
+ }
+ }
+
+ eventLog {
+ focusDoesNotChange(bugId = 151179149)
+ }
}
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index f04131b..7861464 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -17,7 +17,6 @@
package com.android.server.wm.flicker.rotation
import android.os.Bundle
-import android.platform.test.annotations.Presubmit
import androidx.test.filters.RequiresDevice
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerTestRunner
@@ -51,7 +50,6 @@
* Cycle through supported app rotations using seamless rotations.
* To run this test: `atest FlickerTests:SeamlessAppRotationTest`
*/
-@Presubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -97,63 +95,67 @@
} else {
""
}
- buildTestTag("seamlessRotation", configuration, extraInfo = extra)
+ buildTestTag(configuration, extraInfo = extra)
}
assertions {
- windowManagerTrace {
- navBarWindowIsAlwaysVisible(bugId = 140855415)
- statusBarWindowIsAlwaysVisible(bugId = 140855415)
- visibleWindowsShownMoreThanOneConsecutiveEntry()
- appWindowAlwaysVisibleOnTop(testApp.`package`)
- }
+ val startingBounds = WindowUtils.getDisplayBounds(configuration.startRotation)
+ val endingBounds = WindowUtils.getDisplayBounds(configuration.endRotation)
- layersTrace {
- navBarLayerIsAlwaysVisible(bugId = 140855415)
- statusBarLayerIsAlwaysVisible(bugId = 140855415)
- noUncoveredRegions(configuration.startRotation,
- configuration.endRotation, allStates = false, bugId = 147659548)
- navBarLayerRotatesAndScales(configuration.startRotation,
- configuration.endRotation,
- enabled = false)
- statusBarLayerRotatesScales(configuration.startRotation,
- configuration.endRotation, enabled = false)
- visibleLayersShownMoreThanOneConsecutiveEntry(
- enabled = configuration.startRotation == configuration.endRotation)
- layerAlwaysVisible(testApp.`package`)
- }
-
- layersTrace {
- val startingBounds = WindowUtils
- .getDisplayBounds(configuration.startRotation)
- val endingBounds = WindowUtils
- .getDisplayBounds(configuration.endRotation)
-
- all("appLayerRotates", bugId = 147659548) {
- if (startingBounds == endingBounds) {
- this.hasVisibleRegion(
- testApp.`package`, startingBounds)
- } else {
- this.hasVisibleRegion(testApp.`package`,
- startingBounds)
- .then()
- .hasVisibleRegion(testApp.`package`,
- endingBounds)
- }
+ presubmit {
+ windowManagerTrace {
+ visibleWindowsShownMoreThanOneConsecutiveEntry()
+ appWindowAlwaysVisibleOnTop(testApp.`package`)
}
- all("noUncoveredRegions", bugId = 147659548) {
- if (startingBounds == endingBounds) {
- this.coversAtLeastRegion(startingBounds)
- } else {
- this.coversAtLeastRegion(startingBounds)
- .then()
- .coversAtLeastRegion(endingBounds)
- }
+ layersTrace {
+ layerAlwaysVisible(testApp.`package`)
}
}
- eventLog {
- focusDoesNotChange(bugId = 151179149)
+ flaky {
+ windowManagerTrace {
+ navBarWindowIsAlwaysVisible(bugId = 140855415)
+ statusBarWindowIsAlwaysVisible(bugId = 140855415)
+ }
+
+ layersTrace {
+ navBarLayerIsAlwaysVisible(bugId = 140855415)
+ statusBarLayerIsAlwaysVisible(bugId = 140855415)
+ noUncoveredRegions(configuration.startRotation,
+ configuration.endRotation, allStates = false, bugId = 147659548)
+ navBarLayerRotatesAndScales(configuration.startRotation,
+ configuration.endRotation)
+ statusBarLayerRotatesScales(configuration.startRotation,
+ configuration.endRotation)
+ visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ all("appLayerRotates", bugId = 147659548) {
+ if (startingBounds == endingBounds) {
+ this.hasVisibleRegion(
+ testApp.`package`, startingBounds)
+ } else {
+ this.hasVisibleRegion(testApp.`package`,
+ startingBounds)
+ .then()
+ .hasVisibleRegion(testApp.`package`,
+ endingBounds)
+ }
+ }
+
+ all("noUncoveredRegions", bugId = 147659548) {
+ if (startingBounds == endingBounds) {
+ this.coversAtLeastRegion(startingBounds)
+ } else {
+ this.coversAtLeastRegion(startingBounds)
+ .then()
+ .coversAtLeastRegion(endingBounds)
+ }
+ }
+ }
+
+ eventLog {
+ focusDoesNotChange(bugId = 151179149)
+ }
}
}
}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 1a940c7..c6c67fe 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -753,6 +753,15 @@
</intent-filter>
</activity>
+ <activity android:name="RenderEffectShaderActivity"
+ android:label="RenderEffect/Shader"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.android.test.hwui.TEST"/>
+ </intent-filter>
+ </activity>
+
<activity android:name="TextActivity"
android:label="Text/Simple Text"
android:theme="@android:style/Theme.NoTitleBar"
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java
new file mode 100644
index 0000000..661d48a
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectShaderActivity.java
@@ -0,0 +1,107 @@
+/*
+ * 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.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.RenderEffect;
+import android.graphics.RenderNode;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class RenderEffectShaderActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ LinearLayout layout = new LinearLayout(this);
+ layout.setClipChildren(false);
+ layout.setGravity(Gravity.CENTER);
+ layout.setOrientation(LinearLayout.VERTICAL);
+
+
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(500, 500);
+ params.bottomMargin = 100;
+
+ layout.addView(new ShaderRenderEffectView(this), params);
+
+ setContentView(layout);
+ }
+
+ public static class ShaderRenderEffectView extends View {
+
+ private final Paint mPaint;
+ private final RenderNode mRenderNode;
+
+ public ShaderRenderEffectView(Context c) {
+ super(c);
+
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mRenderNode = new RenderNode("blurNode");
+
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (changed) {
+ LinearGradient gradient = new LinearGradient(
+ 0f, 0f,
+ 0f, bottom - top,
+ new int[]{Color.CYAN, Color.MAGENTA},
+ null,
+ Shader.TileMode.CLAMP
+ );
+ mRenderNode.setRenderEffect(
+ RenderEffect.createShaderEffect(gradient)
+ );
+
+ int width = right - left;
+ int height = bottom - top;
+ mRenderNode.setPosition(0, 0, width, height);
+ Canvas canvas = mRenderNode.beginRecording(width, height);
+ mPaint.setColor(Color.BLUE);
+
+ canvas.drawRect(
+ 0,
+ 0,
+ width,
+ height,
+ mPaint
+ );
+
+ mPaint.setColor(Color.RED);
+ canvas.drawCircle((right - left) / 2f, (bottom - top) / 2f, 50f, mPaint);
+
+ mRenderNode.endRecording();
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawRenderNode(mRenderNode);
+ }
+ }
+}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
index 9b15b04..5c26448 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/common/ColorModeControls.kt
@@ -34,12 +34,14 @@
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
displayManager = context.getSystemService(DisplayManager::class.java)!!
+ displayId = context.getDisplayId()
}
private var window: Window? = null
private var currentModeDisplay: TextView? = null
private val displayManager: DisplayManager
private var targetSdrWhitePointIndex = 0
+ private var displayId: Int
private val whitePoint get() = SDR_WHITE_POINTS[targetSdrWhitePointIndex]
@@ -107,7 +109,7 @@
// Imperfect, but close enough, synchronization by waiting for frame commit to set the value
viewTreeObserver.registerFrameCommitCallback {
try {
- displayManager.setTemporaryBrightness(level)
+ displayManager.setTemporaryBrightness(displayId, level)
} catch (ex: Exception) {
// Ignore a permission denied rejection - it doesn't meaningfully change much
}
diff --git a/tests/UpdatableSystemFontTest/Android.bp b/tests/UpdatableSystemFontTest/Android.bp
index d809fe8..43a5078 100644
--- a/tests/UpdatableSystemFontTest/Android.bp
+++ b/tests/UpdatableSystemFontTest/Android.bp
@@ -16,8 +16,14 @@
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 efe5d70..7b919bd 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 6d161a5..e249f8a9 100644
--- a/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
+++ b/tests/UpdatableSystemFontTest/src/com/android/updatablesystemfont/UpdatableSystemFontTest.java
@@ -21,7 +21,9 @@
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.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 @@
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 @@
.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/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
index cade5ba..d232a507 100644
--- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
+++ b/tests/net/common/java/android/net/OemNetworkPreferencesTest.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.
@@ -20,22 +20,20 @@
import static com.android.testutils.ParcelUtils.assertParcelSane;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.os.Build;
-import android.util.SparseArray;
import androidx.test.filters.SmallTest;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Map;
@IgnoreUpTo(Build.VERSION_CODES.R)
@RunWith(DevSdkIgnoreRunner.class)
@@ -45,51 +43,51 @@
private static final int TEST_PREF = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_DEFAULT;
private static final String TEST_PACKAGE = "com.google.apps.contacts";
- private final List<String> mPackages = new ArrayList<>();
private final OemNetworkPreferences.Builder mBuilder = new OemNetworkPreferences.Builder();
- @Before
- public void beforeEachTestMethod() {
- mPackages.add(TEST_PACKAGE);
- }
-
@Test
- public void builderAddNetworkPreferenceRequiresNonNullPackages() {
+ public void testBuilderAddNetworkPreferenceRequiresNonNullPackageName() {
assertThrows(NullPointerException.class,
- () -> mBuilder.addNetworkPreference(TEST_PREF, null));
+ () -> mBuilder.addNetworkPreference(null, TEST_PREF));
}
@Test
- public void getNetworkPreferencesReturnsCorrectValue() {
- final int expectedNumberOfMappings = 1;
- mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+ public void testBuilderRemoveNetworkPreferenceRequiresNonNullPackageName() {
+ assertThrows(NullPointerException.class,
+ () -> mBuilder.removeNetworkPreference(null));
+ }
- final SparseArray<List<String>> networkPreferences =
+ @Test
+ public void testGetNetworkPreferenceReturnsCorrectValue() {
+ final int expectedNumberOfMappings = 1;
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+
+ final Map<String, Integer> networkPreferences =
mBuilder.build().getNetworkPreferences();
assertEquals(expectedNumberOfMappings, networkPreferences.size());
- assertEquals(mPackages.size(), networkPreferences.get(TEST_PREF).size());
- assertEquals(mPackages.get(0), networkPreferences.get(TEST_PREF).get(0));
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
}
@Test
- public void getNetworkPreferencesReturnsUnmodifiableValue() {
+ public void testGetNetworkPreferenceReturnsUnmodifiableValue() {
final String newPackage = "new.com.google.apps.contacts";
- mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
- final SparseArray<List<String>> networkPreferences =
+ final Map<String, Integer> networkPreferences =
mBuilder.build().getNetworkPreferences();
assertThrows(UnsupportedOperationException.class,
- () -> networkPreferences.get(TEST_PREF).set(mPackages.size() - 1, newPackage));
+ () -> networkPreferences.put(newPackage, TEST_PREF));
assertThrows(UnsupportedOperationException.class,
- () -> networkPreferences.get(TEST_PREF).add(newPackage));
+ () -> networkPreferences.remove(TEST_PACKAGE));
+
}
@Test
- public void toStringReturnsCorrectValue() {
- mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+ public void testToStringReturnsCorrectValue() {
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
final String networkPreferencesString = mBuilder.build().getNetworkPreferences().toString();
@@ -99,10 +97,56 @@
@Test
public void testOemNetworkPreferencesParcelable() {
- mBuilder.addNetworkPreference(TEST_PREF, mPackages);
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
final OemNetworkPreferences prefs = mBuilder.build();
assertParcelSane(prefs, 1 /* fieldCount */);
}
+
+ @Test
+ public void testAddNetworkPreferenceOverwritesPriorPreference() {
+ final int newPref = OemNetworkPreferences.OEM_NETWORK_PREFERENCE_OEM_PAID;
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+ Map<String, Integer> networkPreferences =
+ mBuilder.build().getNetworkPreferences();
+
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+ assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF);
+
+ mBuilder.addNetworkPreference(TEST_PACKAGE, newPref);
+ networkPreferences = mBuilder.build().getNetworkPreferences();
+
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+ assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), newPref);
+ }
+
+ @Test
+ public void testRemoveNetworkPreferenceRemovesValue() {
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+ Map<String, Integer> networkPreferences =
+ mBuilder.build().getNetworkPreferences();
+
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+
+ mBuilder.removeNetworkPreference(TEST_PACKAGE);
+ networkPreferences = mBuilder.build().getNetworkPreferences();
+
+ assertFalse(networkPreferences.containsKey(TEST_PACKAGE));
+ }
+
+ @Test
+ public void testConstructorByOemNetworkPreferencesSetsValue() {
+ mBuilder.addNetworkPreference(TEST_PACKAGE, TEST_PREF);
+ OemNetworkPreferences networkPreference = mBuilder.build();
+
+ final Map<String, Integer> networkPreferences =
+ new OemNetworkPreferences
+ .Builder(networkPreference)
+ .build()
+ .getNetworkPreferences();
+
+ assertTrue(networkPreferences.containsKey(TEST_PACKAGE));
+ assertEquals(networkPreferences.get(TEST_PACKAGE).intValue(), TEST_PREF);
+ }
}
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index dc9e587..e1da3d0 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -17,6 +17,7 @@
package com.android.server;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
@@ -84,6 +85,7 @@
final String typeName = ConnectivityManager.getNetworkTypeName(type);
mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities();
mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
mNetworkCapabilities.addTransportType(transport);
switch (transport) {
case TRANSPORT_ETHERNET:
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index c2fddf3..6a09b02 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.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 @@
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); });
@@ -345,15 +349,17 @@
@Test
public void testRequestType() throws Exception {
final String testPkgName = "MyPackage";
+ final String testAttributionTag = "MyTag";
final ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
when(mCtx.getOpPackageName()).thenReturn(testPkgName);
+ when(mCtx.getAttributionTag()).thenReturn(testAttributionTag);
final NetworkRequest request = makeRequest(1);
final NetworkCallback callback = new ConnectivityManager.NetworkCallback();
manager.requestNetwork(request, callback);
verify(mService).requestNetwork(eq(request.networkCapabilities),
eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
- eq(testPkgName), eq(null));
+ eq(testPkgName), eq(testAttributionTag));
reset(mService);
// Verify that register network callback does not calls requestNetwork at all.
@@ -361,19 +367,26 @@
verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(),
anyInt(), any(), any());
verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(),
- eq(testPkgName));
+ eq(testPkgName), eq(testAttributionTag));
reset(mService);
manager.registerDefaultNetworkCallback(callback);
verify(mService).requestNetwork(eq(null),
eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
- eq(testPkgName), eq(null));
+ eq(testPkgName), eq(testAttributionTag));
reset(mService);
manager.requestBackgroundNetwork(request, null, callback);
verify(mService).requestNetwork(eq(request.networkCapabilities),
eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
- eq(testPkgName), eq(null));
+ 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);
}
diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt
index 91fcbc0..1f8f6f3 100644
--- a/tests/net/java/android/net/NetworkTemplateTest.kt
+++ b/tests/net/java/android/net/NetworkTemplateTest.kt
@@ -35,7 +35,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
import kotlin.test.assertFalse
@@ -60,16 +59,13 @@
subscriberId: String? = null,
ssid: String? = null
): NetworkState {
- val info = mock(NetworkInfo::class.java)
- doReturn(type).`when`(info).type
- doReturn(NetworkInfo.State.CONNECTED).`when`(info).state
val lp = LinkProperties()
val caps = NetworkCapabilities().apply {
setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false)
setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true)
setSSID(ssid)
}
- return NetworkState(info, lp, caps, mock(Network::class.java), subscriberId, ssid)
+ return NetworkState(type, lp, caps, mock(Network::class.java), subscriberId, ssid)
}
private fun NetworkTemplate.assertMatches(ident: NetworkIdentity) =
diff --git a/tests/net/java/android/net/VpnTransportInfoTest.java b/tests/net/java/android/net/VpnTransportInfoTest.java
new file mode 100644
index 0000000..2fd5e38
--- /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 0674138..e1fd8f0 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.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;
@@ -331,11 +332,12 @@
private static final String TAG = "ConnectivityServiceTest";
private static final int TIMEOUT_MS = 500;
- private static final int TEST_LINGER_DELAY_MS = 300;
- // Chosen to be less than the linger timeout. This ensures that we can distinguish between a
- // LOST callback that arrives immediately and a LOST callback that arrives after the linger
- // timeout. For this, our assertions should run fast enough to leave less than
- // (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
+ private static final int TEST_LINGER_DELAY_MS = 400;
+ private static final int TEST_NASCENT_DELAY_MS = 300;
+ // Chosen to be less than the linger and nascent timeout. This ensures that we can distinguish
+ // between a LOST callback that arrives immediately and a LOST callback that arrives after
+ // the linger/nascent timeout. For this, our assertions should run fast enough to leave
+ // less than (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
// supposedly fired, and the time we call expectCallback.
private static final int TEST_CALLBACK_TIMEOUT_MS = 250;
// Chosen to be less than TEST_CALLBACK_TIMEOUT_MS. This ensures that requests have time to
@@ -1109,7 +1111,7 @@
}
@Override
- public int getActiveAppVpnType() {
+ public int getActiveVpnType() {
return mVpnType;
}
@@ -1122,10 +1124,12 @@
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);
@@ -1262,6 +1266,13 @@
}
}
+ private void processBroadcastForVpn(Intent intent) {
+ // The BroadcastReceiver for this broadcast checks it is being run on the handler thread.
+ final Handler handler = new Handler(mCsHandlerThread.getLooper());
+ handler.post(() -> mServiceContext.sendBroadcast(intent));
+ waitForIdle();
+ }
+
private void mockUidNetworkingBlocked() {
doAnswer(i -> mContext.getSystemService(NetworkPolicyManager.class)
.checkUidNetworkingBlocked(i.getArgument(0) /* uid */, mUidRules,
@@ -1395,6 +1406,7 @@
mMockNetd,
mDeps);
mService.mLingerDelayMs = TEST_LINGER_DELAY_MS;
+ mService.mNascentDelayMs = TEST_NASCENT_DELAY_MS;
verify(mDeps).makeMultinetworkPolicyTracker(any(), any(), any());
final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
@@ -1737,6 +1749,108 @@
verifyNoNetwork();
}
+ /**
+ * Verify a newly created network will be inactive instead of torn down even if no one is
+ * requesting.
+ */
+ @Test
+ public void testNewNetworkInactive() throws Exception {
+ // Create a callback that monitoring the testing network.
+ final TestNetworkCallback listenCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(new NetworkRequest.Builder().build(), listenCallback);
+
+ // 1. Create a network that is not requested by anyone, and does not satisfy any of the
+ // default requests. Verify that the network will be inactive instead of torn down.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connectWithoutInternet();
+ listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ listenCallback.assertNoCallback();
+
+ // Verify that the network will be torn down after nascent expiry. A small period of time
+ // is added in case of flakiness.
+ final int nascentTimeoutMs =
+ mService.mNascentDelayMs + mService.mNascentDelayMs / 4;
+ listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent, nascentTimeoutMs);
+
+ // 2. Create a network that is satisfied by a request comes later.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connectWithoutInternet();
+ listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ final TestNetworkCallback wifiCallback = new TestNetworkCallback();
+ mCm.requestNetwork(wifiRequest, wifiCallback);
+ wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+ // Verify that the network will be kept since the request is still satisfied. And is able
+ // to get disconnected as usual if the request is released after the nascent timer expires.
+ listenCallback.assertNoCallback(nascentTimeoutMs);
+ mCm.unregisterNetworkCallback(wifiCallback);
+ listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+
+ // 3. Create a network that is satisfied by a request comes later.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connectWithoutInternet();
+ listenCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ mCm.requestNetwork(wifiRequest, wifiCallback);
+ wifiCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+ // Verify that the network will still be torn down after the request gets removed.
+ mCm.unregisterNetworkCallback(wifiCallback);
+ listenCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+
+ // There is no need to ensure that LOSING is never sent in the common case that the
+ // network immediately satisfies a request that was already present, because it is already
+ // verified anywhere whenever {@code TestNetworkCallback#expectAvailable*} is called.
+
+ mCm.unregisterNetworkCallback(listenCallback);
+ }
+
+ /**
+ * Verify a newly created network will be inactive and switch to background if only background
+ * request is satisfied.
+ */
+ @Test
+ public void testNewNetworkInactive_bgNetwork() throws Exception {
+ // Create a callback that monitoring the wifi network.
+ final TestNetworkCallback wifiListenCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build(), wifiListenCallback);
+
+ // Create callbacks that can monitor background and foreground mobile networks.
+ // This is done by granting using background networks permission before registration. Thus,
+ // the service will not add {@code NET_CAPABILITY_FOREGROUND} by default.
+ grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid());
+ final TestNetworkCallback bgMobileListenCallback = new TestNetworkCallback();
+ final TestNetworkCallback fgMobileListenCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build(), bgMobileListenCallback);
+ mCm.registerNetworkCallback(new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_FOREGROUND).build(), fgMobileListenCallback);
+
+ // Connect wifi, which satisfies default request.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ wifiListenCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+
+ // Connect a cellular network, verify that satisfies only the background callback.
+ setAlwaysOnNetworks(true);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ bgMobileListenCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ fgMobileListenCallback.assertNoCallback();
+ assertFalse(isForegroundNetwork(mCellNetworkAgent));
+
+ mCellNetworkAgent.disconnect();
+ bgMobileListenCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ fgMobileListenCallback.assertNoCallback();
+
+ mCm.unregisterNetworkCallback(wifiListenCallback);
+ mCm.unregisterNetworkCallback(bgMobileListenCallback);
+ mCm.unregisterNetworkCallback(fgMobileListenCallback);
+ }
+
@Test
public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
// Test bringing up unvalidated WiFi
@@ -2596,6 +2710,10 @@
NetworkCapabilities filter = new NetworkCapabilities();
filter.addCapability(capability);
+ // Add NOT_VCN_MANAGED capability into filter unconditionally since some request will add
+ // NOT_VCN_MANAGED automatically but not for NetworkCapabilities,
+ // see {@code NetworkCapabilities#deduceNotVcnManagedCapability} for more details.
+ filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests");
handlerThread.start();
final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
@@ -3534,10 +3652,19 @@
@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.
@@ -3552,27 +3679,35 @@
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.
@@ -3580,19 +3715,25 @@
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());
}
@@ -3847,6 +3988,7 @@
handlerThread.start();
NetworkCapabilities filter = new NetworkCapabilities()
.addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
.addCapability(NET_CAPABILITY_INTERNET);
final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
mServiceContext, "testFactory", filter);
@@ -3894,8 +4036,9 @@
setAlwaysOnNetworks(false);
testFactory.expectRequestRemove();
- // ... and cell data to be torn down.
- cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ // ... and cell data to be torn down after nascent network timeout.
+ cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent,
+ mService.mNascentDelayMs + TEST_CALLBACK_TIMEOUT_MS);
assertLength(1, mCm.getAllNetworks());
} finally {
testFactory.terminate();
@@ -5300,20 +5443,20 @@
// MOBILE_IFNAME even though the default network is wifi.
// TODO: fix this to pass in the actual default network interface. Whether or not the VPN
// applies to the system server UID should not have any bearing on network stats.
- mService.setUnderlyingNetworksForVpn(onlyCell);
+ mMockVpn.setUnderlyingNetworks(onlyCell);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{MOBILE_IFNAME});
reset(mStatsService);
- mService.setUnderlyingNetworksForVpn(cellAndWifi);
+ mMockVpn.setUnderlyingNetworks(cellAndWifi);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{MOBILE_IFNAME, WIFI_IFNAME});
reset(mStatsService);
// Null underlying networks are ignored.
- mService.setUnderlyingNetworksForVpn(cellNullAndWifi);
+ mMockVpn.setUnderlyingNetworks(cellNullAndWifi);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{MOBILE_IFNAME, WIFI_IFNAME});
@@ -5362,25 +5505,25 @@
// is probably a performance improvement (though it's very unlikely that a VPN would declare
// no underlying networks).
// Also, for the same reason as above, the active interface passed in is null.
- mService.setUnderlyingNetworksForVpn(new Network[0]);
+ mMockVpn.setUnderlyingNetworks(new Network[0]);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, null);
reset(mStatsService);
// Specifying only a null underlying network is the same as no networks.
- mService.setUnderlyingNetworksForVpn(onlyNull);
+ mMockVpn.setUnderlyingNetworks(onlyNull);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, null);
reset(mStatsService);
// Specifying networks that are all disconnected is the same as specifying no networks.
- mService.setUnderlyingNetworksForVpn(onlyCell);
+ mMockVpn.setUnderlyingNetworks(onlyCell);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, null);
reset(mStatsService);
// Passing in null again means follow the default network again.
- mService.setUnderlyingNetworksForVpn(null);
+ mMockVpn.setUnderlyingNetworks(null);
waitForIdle();
expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
new String[]{WIFI_IFNAME});
@@ -5749,6 +5892,7 @@
.addTransportType(TRANSPORT_CELLULAR)
.addCapability(NET_CAPABILITY_INTERNET)
.addCapability(NET_CAPABILITY_NOT_CONGESTED)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
.setLinkDownstreamBandwidthKbps(10);
final NetworkCapabilities wifiNc = new NetworkCapabilities()
.addTransportType(TRANSPORT_WIFI)
@@ -5757,6 +5901,7 @@
.addCapability(NET_CAPABILITY_NOT_ROAMING)
.addCapability(NET_CAPABILITY_NOT_CONGESTED)
.addCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
.setLinkUpstreamBandwidthKbps(20);
mCellNetworkAgent.setNetworkCapabilities(cellNc, true /* sendToConnectivityService */);
mWiFiNetworkAgent.setNetworkCapabilities(wifiNc, true /* sendToConnectivityService */);
@@ -5855,7 +6000,7 @@
mMockVpn.establishForMyUid(false, true, false);
assertUidRangesUpdatedForMyUid(true);
final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
- mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork});
+ mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork});
callback.expectAvailableCallbacksUnvalidated(mMockVpn);
assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasTransport(TRANSPORT_VPN));
@@ -6015,6 +6160,10 @@
@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();
@@ -6022,6 +6171,7 @@
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();
@@ -6035,6 +6185,8 @@
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);
@@ -6044,12 +6196,13 @@
genericNotVpnNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ systemDefaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
vpnNetworkCallback.assertNoCallback();
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
final Set<UidRange> ranges = uidRangesForUid(uid);
mMockVpn.registerAgent(ranges);
- mService.setUnderlyingNetworksForVpn(new Network[0]);
+ mMockVpn.setUnderlyingNetworks(new Network[0]);
// VPN networks do not satisfy the default request and are automatically validated
// by NetworkMonitor
@@ -6064,7 +6217,10 @@
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);
@@ -6081,6 +6237,7 @@
// 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);
@@ -6092,6 +6249,7 @@
// 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();
@@ -6100,6 +6258,7 @@
wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
vpnNetworkCallback.assertNoCallback();
defaultCallback.assertNoCallback();
+ systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
mMockVpn.disconnect();
@@ -6108,12 +6267,14 @@
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
@@ -6253,7 +6414,7 @@
private void assertDefaultNetworkCapabilities(int userId, NetworkAgentWrapper... networks) {
final NetworkCapabilities[] defaultCaps = mService.getDefaultNetworkCapabilitiesForUser(
- userId, "com.android.calling.package");
+ userId, "com.android.calling.package", "com.test");
final String defaultCapsString = Arrays.toString(defaultCaps);
assertEquals(defaultCapsString, defaultCaps.length, networks.length);
final Set<NetworkCapabilities> defaultCapsSet = new ArraySet<>(defaultCaps);
@@ -6297,7 +6458,7 @@
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mCellNetworkAgent.connect(true);
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6312,7 +6473,7 @@
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mWiFiNetworkAgent.connect(true);
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6323,7 +6484,7 @@
assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Don't disconnect, but note the VPN is not using wifi any more.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6354,7 +6515,7 @@
vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
// Use Wifi but not cell. Note the VPN is now unmetered and not suspended.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6365,7 +6526,7 @@
assertDefaultNetworkCapabilities(userId, mWiFiNetworkAgent);
// Use both again.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6380,7 +6541,7 @@
vpnNetworkCallback.assertNoCallback();
// Stop using WiFi. The VPN is suspended again.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
@@ -6391,7 +6552,7 @@
assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Use both again.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
@@ -6526,9 +6687,7 @@
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
// Send a USER_ADDED broadcast for it.
- // The BroadcastReceiver for this broadcast checks that is being run on the handler thread.
- final Handler handler = new Handler(mCsHandlerThread.getLooper());
- handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
+ processBroadcastForVpn(addedIntent);
// Expect that the VPN UID ranges contain both |uid| and the UID range for the newly-added
// restricted user.
@@ -6552,7 +6711,7 @@
// Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
- handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
+ processBroadcastForVpn(removedIntent);
// Expect that the VPN gains the UID range for the restricted user, and that the capability
// change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved.
@@ -6609,9 +6768,7 @@
// TODO: check that VPN app within restricted profile still has access, etc.
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
- final Handler handler = new Handler(mCsHandlerThread.getLooper());
- handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
- waitForIdle();
+ processBroadcastForVpn(addedIntent);
assertNull(mCm.getActiveNetworkForUid(uid));
assertNull(mCm.getActiveNetworkForUid(restrictedUid));
@@ -6621,8 +6778,7 @@
// Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, RESTRICTED_USER);
- handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
- waitForIdle();
+ processBroadcastForVpn(removedIntent);
assertNull(mCm.getActiveNetworkForUid(uid));
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
@@ -6724,7 +6880,7 @@
// Ensure VPN is now the active network.
assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// VPN is using Cell
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork() });
waitForIdle();
@@ -6732,7 +6888,7 @@
assertTrue(mCm.isActiveNetworkMetered());
// VPN is now using WiFi
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mWiFiNetworkAgent.getNetwork() });
waitForIdle();
@@ -6740,7 +6896,7 @@
assertFalse(mCm.isActiveNetworkMetered());
// VPN is using Cell | WiFi.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
waitForIdle();
@@ -6748,7 +6904,7 @@
assertTrue(mCm.isActiveNetworkMetered());
// VPN is using WiFi | Cell.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mWiFiNetworkAgent.getNetwork(), mCellNetworkAgent.getNetwork() });
waitForIdle();
@@ -6756,7 +6912,7 @@
assertTrue(mCm.isActiveNetworkMetered());
// VPN is not using any underlying networks.
- mService.setUnderlyingNetworksForVpn(new Network[0]);
+ mMockVpn.setUnderlyingNetworks(new Network[0]);
waitForIdle();
// VPN without underlying networks is treated as metered.
@@ -6783,7 +6939,7 @@
assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// VPN is tracking current platform default (WiFi).
- mService.setUnderlyingNetworksForVpn(null);
+ mMockVpn.setUnderlyingNetworks(null);
waitForIdle();
// Despite VPN using WiFi (which is unmetered), VPN itself is marked as always metered.
@@ -6791,7 +6947,7 @@
// VPN explicitly declares WiFi as its underlying network.
- mService.setUnderlyingNetworksForVpn(
+ mMockVpn.setUnderlyingNetworks(
new Network[] { mWiFiNetworkAgent.getNetwork() });
waitForIdle();
@@ -7169,6 +7325,7 @@
}
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);
@@ -7199,9 +7356,7 @@
final int userId = UserHandle.getUserId(Process.myUid());
final Intent addedIntent = new Intent(ACTION_USER_UNLOCKED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- final Handler handler = new Handler(mCsHandlerThread.getLooper());
- handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
- waitForIdle();
+ processBroadcastForVpn(addedIntent);
// Lockdown VPN disables teardown and enables lockdown.
assertFalse(mMockVpn.getEnableTeardown());
@@ -7283,6 +7438,9 @@
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();
@@ -7409,19 +7567,13 @@
mWiFiNetworkAgent.removeCapability(testCap);
callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent);
callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent);
- // TODO: Test default network changes for NOT_VCN_MANAGED once the default request has
- // it.
- if (testCap == NET_CAPABILITY_TRUSTED) {
- verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
- reset(mMockNetd);
- }
+ verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
mCellNetworkAgent.removeCapability(testCap);
callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
callbackWithoutCap.assertNoCallback();
- if (testCap == NET_CAPABILITY_TRUSTED) {
- verify(mMockNetd).networkClearDefault();
- }
+ verify(mMockNetd).networkClearDefault();
mCm.unregisterNetworkCallback(callbackWithCap);
mCm.unregisterNetworkCallback(callbackWithoutCap);
@@ -8272,7 +8424,8 @@
when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
if (op != null) {
- when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName())))
+ when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()),
+ eq(mContext.getPackageName()), eq(getAttributionTag()), anyString()))
.thenReturn(AppOpsManager.MODE_ALLOWED);
}
@@ -8285,7 +8438,7 @@
final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid);
return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- netCap, callerUid, mContext.getPackageName()).getOwnerUid();
+ netCap, callerUid, mContext.getPackageName(), getAttributionTag()).getOwnerUid();
}
private void verifyWifiInfoCopyNetCapsForCallerPermission(
@@ -8295,7 +8448,7 @@
final NetworkCapabilities netCap = new NetworkCapabilities().setTransportInfo(wifiInfo);
mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- netCap, callerUid, mContext.getPackageName());
+ netCap, callerUid, mContext.getPackageName(), getAttributionTag());
verify(wifiInfo).makeCopy(eq(shouldMakeCopyWithLocationSensitiveFieldsParcelable));
}
@@ -8414,11 +8567,7 @@
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
@@ -8426,11 +8575,7 @@
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
@@ -8643,7 +8788,7 @@
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
- assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {naiWithoutUid.network}));
+ assertTrue(mMockVpn.setUnderlyingNetworks(new Network[] {naiWithoutUid.network}));
waitForIdle();
assertTrue(
"Active VPN permission not applied",
@@ -8651,7 +8796,7 @@
Process.myPid(), Process.myUid(), naiWithoutUid,
mContext.getOpPackageName()));
- assertTrue(mService.setUnderlyingNetworksForVpn(null));
+ assertTrue(mMockVpn.setUnderlyingNetworks(null));
waitForIdle();
assertFalse(
"VPN shouldn't receive callback on non-underlying network",
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 3a93c5b..cffd2d1d 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 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.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;
@@ -257,12 +259,14 @@
@Test
public void testRestrictedProfilesAreAddedToVpn() {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB);
final Vpn vpn = createVpn(primaryUser.id);
- final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
- null, null);
+
+ // Assume the user can have restricted profiles.
+ doReturn(true).when(mUserManager).canHaveRestrictedProfile();
+ final Set<UidRange> ranges =
+ vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, null);
assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
PRI_USER_RANGE, UidRange.createForUser(restrictedProfileA.id)
@@ -271,7 +275,6 @@
@Test
public void testManagedProfilesAreNotAddedToVpn() {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
setMockedUsers(primaryUser, managedProfileA);
final Vpn vpn = createVpn(primaryUser.id);
@@ -294,7 +297,6 @@
@Test
public void testUidAllowAndDenylist() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRange user = PRI_USER_RANGE;
final String[] packages = {PKGS[0], PKGS[1], PKGS[2]};
@@ -320,7 +322,6 @@
@Test
public void testGetAlwaysAndOnGetLockDown() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
// Default state.
@@ -345,7 +346,6 @@
@Test
public void testLockdownChangingPackage() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRange user = PRI_USER_RANGE;
@@ -373,7 +373,6 @@
@Test
public void testLockdownAllowlist() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRange user = PRI_USER_RANGE;
@@ -448,7 +447,6 @@
@Test
public void testLockdownRuleRepeatability() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] {
new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)};
@@ -481,7 +479,6 @@
@Test
public void testLockdownRuleReversibility() throws Exception {
- if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
final Vpn vpn = createVpn(primaryUser.id);
final UidRangeParcel[] entireUser = {
new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)
@@ -958,14 +955,7 @@
}
private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception {
- // TODO(b/175883995): once these tests have been updated for the changes to the UserManager
- // API, remove this ad-hoc setup code and use setMockedUsers(primaryUser) again.
- // setMockedUsers(primaryUser);
- final ArrayList<UserInfo> users = new ArrayList<>();
- users.add(primaryUser);
- when(mUserManager.getAliveUsers()).thenReturn(users);
- when(mUserManager.getUserInfo(primaryUser.id)).thenReturn(primaryUser);
- when(mUserManager.canHaveRestrictedProfile()).thenReturn(false);
+ setMockedUsers(primaryUser);
// Dummy egress interface
final LinkProperties lp = new LinkProperties();
@@ -996,6 +986,13 @@
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();
@@ -1032,8 +1029,10 @@
// 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
@@ -1043,6 +1042,8 @@
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();
@@ -1199,11 +1200,6 @@
final int id = (int) invocation.getArguments()[0];
return userMap.get(id);
}).when(mUserManager).getUserInfo(anyInt());
-
- doAnswer(invocation -> {
- final int id = (int) invocation.getArguments()[0];
- return (userMap.get(id).flags & UserInfo.FLAG_ADMIN) != 0;
- }).when(mUserManager).canHaveRestrictedProfile();
}
/**
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index dde78aa..214c82d 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -80,8 +80,6 @@
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
import android.net.NetworkState;
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
@@ -1456,8 +1454,6 @@
}
private static NetworkState buildWifiState(boolean isMetered, @NonNull String iface) {
- final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
- info.setDetailedState(DetailedState.CONNECTED, null, null);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(iface);
final NetworkCapabilities capabilities = new NetworkCapabilities();
@@ -1465,7 +1461,7 @@
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
capabilities.setSSID(TEST_SSID);
- return new NetworkState(info, prop, capabilities, WIFI_NETWORK, null, TEST_SSID);
+ return new NetworkState(TYPE_WIFI, prop, capabilities, WIFI_NETWORK, null, TEST_SSID);
}
private static NetworkState buildMobile3gState(String subscriberId) {
@@ -1473,17 +1469,14 @@
}
private static NetworkState buildMobile3gState(String subscriberId, boolean isRoaming) {
- final NetworkInfo info = new NetworkInfo(
- TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UMTS, null, null);
- info.setDetailedState(DetailedState.CONNECTED, null, null);
- info.setRoaming(isRoaming);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
final NetworkCapabilities capabilities = new NetworkCapabilities();
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming);
capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, subscriberId, null);
+ return new NetworkState(
+ TYPE_MOBILE, prop, capabilities, MOBILE_NETWORK, subscriberId, null);
}
private NetworkStats buildEmptyStats() {
@@ -1491,11 +1484,9 @@
}
private static NetworkState buildVpnState() {
- final NetworkInfo info = new NetworkInfo(TYPE_VPN, 0, null, null);
- info.setDetailedState(DetailedState.CONNECTED, null, null);
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TUN_IFACE);
- return new NetworkState(info, prop, new NetworkCapabilities(), VPN_NETWORK, null, null);
+ return new NetworkState(TYPE_VPN, prop, new NetworkCapabilities(), VPN_NETWORK, null, null);
}
private long getElapsedRealtime() {
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index aeb142b..1fe13fe 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -28,8 +28,6 @@
import junit.framework.TestCase;
-import org.junit.Test;
-
/**
* TODO: Remove this. This is only a placeholder, need to implement this.
*/
@@ -56,7 +54,7 @@
}
try {
- mWm.addWindowToken(null, TYPE_APPLICATION, DEFAULT_DISPLAY);
+ mWm.addWindowToken(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null /* options */);
fail("IWindowManager.addWindowToken did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
@@ -155,29 +153,4 @@
fail("Unexpected remote exception");
}
}
-
- @Test
- public void testADD_WINDOW_TOKEN_WITH_OPTIONS() {
- // Verify if addWindowTokenWithOptions throw SecurityException for privileged window type.
- try {
- mWm.addWindowTokenWithOptions(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null, "");
- fail("IWindowManager.addWindowTokenWithOptions did not throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- } catch (RemoteException e) {
- fail("Unexpected remote exception");
- }
-
- // Verify if addWindowTokenWithOptions throw SecurityException for null packageName.
- try {
- mWm.addWindowTokenWithOptions(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null, null);
- fail("IWindowManager.addWindowTokenWithOptions did not throw SecurityException as"
- + " expected");
- } catch (SecurityException e) {
- // expected
- } catch (RemoteException e) {
- fail("Unexpected remote exception");
- }
- }
}
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 86a1591..3e659d0 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -59,12 +59,17 @@
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfig() {
+ return buildTestConfigWithExposedCaps(EXPOSED_CAPS);
+ }
+
+ // Public for use in VcnGatewayConnectionTest
+ public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
final VcnGatewayConnectionConfig.Builder builder =
new VcnGatewayConnectionConfig.Builder()
.setRetryInterval(RETRY_INTERVALS_MS)
.setMaxMtu(MAX_MTU);
- for (int caps : EXPOSED_CAPS) {
+ for (int caps : exposedCaps) {
builder.addExposedCapability(caps);
}
diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
index f9db408..7dada9d 100644
--- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -65,7 +65,7 @@
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 @@
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 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 e32e1e8..c290bff 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;
@@ -66,6 +70,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
import com.android.server.vcn.TelephonySubscriptionTracker;
import com.android.server.vcn.Vcn;
import com.android.server.vcn.VcnContext;
@@ -105,6 +110,7 @@
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 */,
@@ -142,6 +148,9 @@
private final TelephonySubscriptionTracker mSubscriptionTracker =
mock(TelephonySubscriptionTracker.class);
+ private final ArgumentCaptor<VcnSafemodeCallback> mSafemodeCallbackCaptor =
+ ArgumentCaptor.forClass(VcnSafemodeCallback.class);
+
private final VcnManagementService mVcnMgmtSvc;
private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener =
@@ -184,7 +193,7 @@
doAnswer((invocation) -> {
// Mock-within a doAnswer is safe, because it doesn't actually run nested.
return mock(Vcn.class);
- }).when(mMockDeps).newVcn(any(), any(), any(), any());
+ }).when(mMockDeps).newVcn(any(), any(), any(), any(), any());
final PersistableBundle bundle =
PersistableBundleUtils.fromMap(
@@ -307,7 +316,7 @@
TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
verify(mMockDeps)
- .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot));
+ .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot), any());
}
@Test
@@ -485,7 +494,8 @@
eq(mVcnContext),
eq(TEST_UUID_2),
eq(TEST_VCN_CONFIG),
- eq(TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT));
+ eq(TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT),
+ any());
// Verify Vcn is updated if it was previously started
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
@@ -532,59 +542,121 @@
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);
-
- NetworkCapabilities nc =
- new NetworkCapabilities.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID))
- .build();
-
- VcnUnderlyingNetworkPolicy policy =
- mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
+ final VcnUnderlyingNetworkPolicy policy =
+ startVcnAndGetPolicyForTransport(
+ TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */, TRANSPORT_CELLULAR);
assertFalse(policy.isTeardownRequested());
- verifyMergedNetworkCapabilitiesIsVcnManaged(
- policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_CELLULAR);
+ verifyMergedNetworkCapabilities(
+ policy.getMergedNetworkCapabilities(),
+ TRANSPORT_CELLULAR,
+ true /* isVcnManaged */,
+ false /* isRestricted */);
+ }
+
+ @Test
+ public void testGetUnderlyingNetworkPolicyCellular_safeMode() throws Exception {
+ final VcnUnderlyingNetworkPolicy policy =
+ startVcnAndGetPolicyForTransport(
+ TEST_SUBSCRIPTION_ID,
+ TEST_UUID_2,
+ false /* isActive */,
+ TRANSPORT_CELLULAR);
+
+ assertFalse(policy.isTeardownRequested());
+ verifyMergedNetworkCapabilities(
+ policy.getMergedNetworkCapabilities(),
+ NetworkCapabilities.TRANSPORT_CELLULAR,
+ false /* isVcnManaged */,
+ false /* isRestricted */);
}
@Test
public void testGetUnderlyingNetworkPolicyWifi() throws Exception {
- setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
-
- 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();
-
- VcnUnderlyingNetworkPolicy policy =
- mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
+ final VcnUnderlyingNetworkPolicy policy =
+ startVcnAndGetPolicyForTransport(
+ TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */, TRANSPORT_WIFI);
assertFalse(policy.isTeardownRequested());
- verifyMergedNetworkCapabilitiesIsVcnManaged(
- policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_WIFI);
+ verifyMergedNetworkCapabilities(
+ policy.getMergedNetworkCapabilities(),
+ NetworkCapabilities.TRANSPORT_WIFI,
+ true /* isVcnManaged */,
+ true /* isRestricted */);
+ }
+
+ @Test
+ public void testGetUnderlyingNetworkPolicyVcnWifi_safeMode() throws Exception {
+ final VcnUnderlyingNetworkPolicy policy =
+ startVcnAndGetPolicyForTransport(
+ TEST_SUBSCRIPTION_ID, TEST_UUID_2, false /* isActive */, TRANSPORT_WIFI);
+
+ assertFalse(policy.isTeardownRequested());
+ 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 =
@@ -634,4 +706,25 @@
verify(mMockPolicyListener).onPolicyChanged();
}
+
+ @Test
+ public void testVcnSafemodeCallbackOnEnteredSafemode() throws Exception {
+ TelephonySubscriptionSnapshot snapshot =
+ triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
+ verify(mMockDeps)
+ .newVcn(
+ eq(mVcnContext),
+ eq(TEST_UUID_1),
+ eq(TEST_VCN_CONFIG),
+ eq(snapshot),
+ mSafemodeCallbackCaptor.capture());
+
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ VcnSafemodeCallback safemodeCallback = mSafemodeCallbackCaptor.getValue();
+ safemodeCallback.onEnteredSafemode();
+
+ assertFalse(mVcnMgmtSvc.getAllVcns().get(TEST_UUID_1).isActive());
+ verify(mMockPolicyListener).onPolicyChanged();
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index e20070e..278d93a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -18,22 +18,35 @@
import static android.net.IpSecManager.DIRECTION_IN;
import static android.net.IpSecManager.DIRECTION_OUT;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+
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.ArgumentCaptor;
+
+import java.util.Collections;
/** Tests for VcnGatewayConnection.ConnectedState */
@RunWith(AndroidJUnit4.class)
@@ -107,6 +120,51 @@
}
@Test
+ public void testChildOpenedRegistersNetwork() throws Exception {
+ final VcnChildSessionConfiguration mMockChildSessionConfig =
+ mock(VcnChildSessionConfiguration.class);
+ doReturn(Collections.singletonList(TEST_INTERNAL_ADDR))
+ .when(mMockChildSessionConfig)
+ .getInternalAddresses();
+ doReturn(Collections.singletonList(TEST_DNS_ADDR))
+ .when(mMockChildSessionConfig)
+ .getInternalDnsServers();
+
+ getChildSessionCallback().onOpened(mMockChildSessionConfig);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+
+ final ArgumentCaptor<LinkProperties> lpCaptor =
+ ArgumentCaptor.forClass(LinkProperties.class);
+ final ArgumentCaptor<NetworkCapabilities> ncCaptor =
+ ArgumentCaptor.forClass(NetworkCapabilities.class);
+ verify(mConnMgr)
+ .registerNetworkAgent(
+ any(),
+ any(),
+ lpCaptor.capture(),
+ ncCaptor.capture(),
+ anyInt(),
+ any(),
+ anyInt());
+ verify(mIpSecSvc)
+ .addAddressToTunnelInterface(
+ eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(TEST_INTERNAL_ADDR), any());
+
+ final LinkProperties lp = lpCaptor.getValue();
+ assertEquals(Collections.singletonList(TEST_INTERNAL_ADDR), lp.getLinkAddresses());
+ assertEquals(Collections.singletonList(TEST_DNS_ADDR), lp.getDnsServers());
+
+ final NetworkCapabilities nc = ncCaptor.getValue();
+ assertTrue(nc.hasTransport(TRANSPORT_CELLULAR));
+ assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ for (int cap : mConfig.getAllExposedCapabilities()) {
+ assertTrue(nc.hasCapability(cap));
+ }
+ }
+
+ @Test
public void testChildSessionClosedTriggersDisconnect() throws Exception {
getChildSessionCallback().onClosed();
mTestLooper.dispatchAll();
@@ -122,6 +180,4 @@
assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
verify(mIkeSession).close();
}
-
- // TODO: Add tests for childOpened() when ChildSessionConfiguration can be mocked or created
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
index fbaae6f..8643d8a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -45,7 +45,12 @@
public void testEnterWhileNotRunningTriggersQuit() throws Exception {
final VcnGatewayConnection vgc =
new VcnGatewayConnection(
- mVcnContext, TEST_SUB_GRP, TEST_SUBSCRIPTION_SNAPSHOT, mConfig, mDeps);
+ mVcnContext,
+ TEST_SUB_GRP,
+ TEST_SUBSCRIPTION_SNAPSHOT,
+ mConfig,
+ mGatewayStatusCallback,
+ mDeps);
vgc.setIsRunning(false);
vgc.transitionTo(vgc.mDisconnectedState);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
new file mode 100644
index 0000000..3f2b47c
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for VcnGatewayConnection.RetryTimeoutState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnectionTestBase {
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
+ mGatewayConnection.transitionTo(mGatewayConnection.mRetryTimeoutState);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testNewNetworkTriggerRetry() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testSameNetworkDoesNotTriggerRetry() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testNullNetworkTriggersDisconnect() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(null);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testTimeoutElapsingTriggersRetry() throws Exception {
+ mTestLooper.moveTimeForward(mConfig.getRetryIntervalsMs()[0]);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index df1341c..d449eab 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -27,10 +27,13 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.InetAddresses;
import android.net.IpSecConfig;
import android.net.IpSecManager;
import android.net.IpSecTransform;
import android.net.IpSecTunnelInterfaceResponse;
+import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -43,15 +46,23 @@
import com.android.server.IpSecService;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
+import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback;
import org.junit.Before;
import org.mockito.ArgumentCaptor;
+import java.net.InetAddress;
import java.util.Collections;
import java.util.UUID;
public class VcnGatewayConnectionTestBase {
protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID());
+ protected static final InetAddress TEST_DNS_ADDR =
+ InetAddresses.parseNumericAddress("2001:DB8:0:1::");
+ protected static final LinkAddress TEST_INTERNAL_ADDR =
+ new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:0:2::"), 64);
+
protected static final int TEST_IPSEC_SPI_VALUE = 0x1234;
protected static final int TEST_IPSEC_SPI_RESOURCE_ID = 1;
protected static final int TEST_IPSEC_TRANSFORM_RESOURCE_ID = 2;
@@ -80,10 +91,12 @@
@NonNull protected final VcnNetworkProvider mVcnNetworkProvider;
@NonNull protected final VcnContext mVcnContext;
@NonNull protected final VcnGatewayConnectionConfig mConfig;
+ @NonNull protected final VcnGatewayStatusCallback mGatewayStatusCallback;
@NonNull protected final VcnGatewayConnection.Dependencies mDeps;
@NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
@NonNull protected final IpSecService mIpSecSvc;
+ @NonNull protected final ConnectivityManager mConnMgr;
protected VcnIkeSession mMockIkeSession;
protected VcnGatewayConnection mGatewayConnection;
@@ -94,12 +107,17 @@
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
mVcnContext = mock(VcnContext.class);
mConfig = VcnGatewayConnectionConfigTest.buildTestConfig();
+ mGatewayStatusCallback = mock(VcnGatewayStatusCallback.class);
mDeps = mock(VcnGatewayConnection.Dependencies.class);
mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class);
mIpSecSvc = mock(IpSecService.class);
setupIpSecManager(mContext, mIpSecSvc);
+ mConnMgr = mock(ConnectivityManager.class);
+ VcnTestUtils.setupSystemService(
+ mContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+
doReturn(mContext).when(mVcnContext).getContext();
doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
@@ -123,7 +141,12 @@
mGatewayConnection =
new VcnGatewayConnection(
- mVcnContext, TEST_SUB_GRP, TEST_SUBSCRIPTION_SNAPSHOT, mConfig, mDeps);
+ mVcnContext,
+ TEST_SUB_GRP,
+ TEST_SUBSCRIPTION_SNAPSHOT,
+ mConfig,
+ mGatewayStatusCallback,
+ mDeps);
}
protected IpSecTransform makeDummyIpSecTransform() throws Exception {
@@ -137,10 +160,10 @@
return captor.getValue();
}
- protected ChildSessionCallback getChildSessionCallback() {
+ protected VcnChildSessionCallback getChildSessionCallback() {
ArgumentCaptor<ChildSessionCallback> captor =
ArgumentCaptor.forClass(ChildSessionCallback.class);
verify(mDeps).newIkeSession(any(), any(), any(), any(), captor.capture());
- return captor.getValue();
+ return (VcnChildSessionCallback) captor.getValue();
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index 0c1df76..66cbf84 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -16,22 +16,27 @@
package com.android.server.vcn;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.net.NetworkRequest;
import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
import org.junit.Before;
@@ -51,9 +56,13 @@
private VcnContext mVcnContext;
private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
private VcnNetworkProvider mVcnNetworkProvider;
+ private VcnSafemodeCallback mVcnSafemodeCallback;
private Vcn.Dependencies mDeps;
+ private ArgumentCaptor<VcnGatewayStatusCallback> mGatewayStatusCallbackCaptor;
+
private TestLooper mTestLooper;
+ private VcnGatewayConnectionConfig mGatewayConnectionConfig;
private VcnConfig mConfig;
private Vcn mVcn;
@@ -63,6 +72,7 @@
mVcnContext = mock(VcnContext.class);
mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class);
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
+ mVcnSafemodeCallback = mock(VcnSafemodeCallback.class);
mDeps = mock(Vcn.Dependencies.class);
mTestLooper = new TestLooper();
@@ -76,15 +86,26 @@
doAnswer((invocation) -> {
// Mock-within a doAnswer is safe, because it doesn't actually run nested.
return mock(VcnGatewayConnection.class);
- }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any());
+ }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any(), any());
- mConfig =
- new VcnConfig.Builder(mContext)
- .addGatewayConnectionConfig(
- VcnGatewayConnectionConfigTest.buildTestConfig())
- .build();
+ mGatewayStatusCallbackCaptor = ArgumentCaptor.forClass(VcnGatewayStatusCallback.class);
- mVcn = new Vcn(mVcnContext, TEST_SUB_GROUP, mConfig, mSubscriptionSnapshot, mDeps);
+ final VcnConfig.Builder configBuilder = new VcnConfig.Builder(mContext);
+ for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+ configBuilder.addGatewayConnectionConfig(
+ VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(capability));
+ }
+ configBuilder.addGatewayConnectionConfig(VcnGatewayConnectionConfigTest.buildTestConfig());
+ mConfig = configBuilder.build();
+
+ mVcn =
+ new Vcn(
+ mVcnContext,
+ TEST_SUB_GROUP,
+ mConfig,
+ mSubscriptionSnapshot,
+ mVcnSafemodeCallback,
+ mDeps);
}
private NetworkRequestListener verifyAndGetRequestListener() {
@@ -95,23 +116,22 @@
return mNetworkRequestListenerCaptor.getValue();
}
- private NetworkRequest getNetworkRequestWithCapabilities(int[] networkCapabilities) {
- final NetworkRequest.Builder builder = new NetworkRequest.Builder();
- for (final int netCapability : networkCapabilities) {
- builder.addCapability(netCapability);
+ private void startVcnGatewayWithCapabilities(
+ NetworkRequestListener requestListener, int... netCapabilities) {
+ final NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
+ for (final int netCapability : netCapabilities) {
+ requestBuilder.addCapability(netCapability);
}
- return builder.build();
+
+ requestListener.onNetworkRequested(requestBuilder.build(), NETWORK_SCORE, PROVIDER_ID);
+ mTestLooper.dispatchAll();
}
@Test
public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
-
- requestListener.onNetworkRequested(
- getNetworkRequestWithCapabilities(VcnGatewayConnectionConfigTest.EXPOSED_CAPS),
- NETWORK_SCORE,
- PROVIDER_ID);
- mTestLooper.dispatchAll();
+ startVcnGatewayWithCapabilities(
+ requestListener, VcnGatewayConnectionConfigTest.EXPOSED_CAPS);
final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
assertFalse(gatewayConnections.isEmpty());
@@ -126,4 +146,38 @@
verify(gateway).updateSubscriptionSnapshot(eq(updatedSnapshot));
}
}
+
+ @Test
+ public void testGatewayEnteringSafemodeNotifiesVcn() {
+ final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+ for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+ startVcnGatewayWithCapabilities(requestListener, capability);
+ }
+
+ // Each Capability in EXPOSED_CAPS was split into a separate VcnGatewayConnection in #setUp.
+ // Expect one VcnGatewayConnection per capability.
+ final int numExpectedGateways = VcnGatewayConnectionConfigTest.EXPOSED_CAPS.length;
+
+ final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
+ assertEquals(numExpectedGateways, gatewayConnections.size());
+ verify(mDeps, times(numExpectedGateways))
+ .newVcnGatewayConnection(
+ eq(mVcnContext),
+ eq(TEST_SUB_GROUP),
+ eq(mSubscriptionSnapshot),
+ any(),
+ mGatewayStatusCallbackCaptor.capture());
+
+ // Doesn't matter which callback this gets - any Gateway entering Safemode should shut down
+ // all Gateways
+ final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
+ statusCallback.onEnteredSafemode();
+ mTestLooper.dispatchAll();
+
+ for (final VcnGatewayConnection gatewayConnection : gatewayConnections) {
+ verify(gatewayConnection).teardownAsynchronously();
+ }
+ verify(mVcnNetworkProvider).unregisterListener(requestListener);
+ verify(mVcnSafemodeCallback).onEnteredSafemode();
+ }
}
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
deleted file mode 100755
index 28ff606..0000000
--- 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 82d117f..0000000
--- 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 6a5b0e1..0000000
--- 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)