Merge "Correctly fade sparkles in" into sc-dev
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java
index e585d91..4357905 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java
@@ -16,6 +16,7 @@
package android.app.appsearch;
+import static android.app.appsearch.AppSearchResult.RESULT_INVALID_SCHEMA;
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
@@ -27,6 +28,7 @@
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.util.ArraySet;
import com.android.internal.infra.AndroidFuture;
@@ -39,8 +41,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
/**
@@ -55,23 +57,23 @@
private final String mDatabaseName;
private final int mUserId;
private final File mMigratedFile;
- private final Map<String, Integer> mCurrentVersionMap;
- private final Map<String, Integer> mFinalVersionMap;
+ private final Set<String> mDestinationTypes;
private boolean mAreDocumentsMigrated = false;
AppSearchMigrationHelper(@NonNull IAppSearchManager service,
@UserIdInt int userId,
- @NonNull Map<String, Integer> currentVersionMap,
- @NonNull Map<String, Integer> finalVersionMap,
@NonNull String packageName,
- @NonNull String databaseName) throws IOException {
+ @NonNull String databaseName,
+ @NonNull Set<AppSearchSchema> newSchemas) throws IOException {
mService = Objects.requireNonNull(service);
- mCurrentVersionMap = Objects.requireNonNull(currentVersionMap);
- mFinalVersionMap = Objects.requireNonNull(finalVersionMap);
mPackageName = Objects.requireNonNull(packageName);
mDatabaseName = Objects.requireNonNull(databaseName);
mUserId = userId;
mMigratedFile = File.createTempFile(/*prefix=*/"appsearch", /*suffix=*/null);
+ mDestinationTypes = new ArraySet<>(newSchemas.size());
+ for (AppSearchSchema newSchema : newSchemas) {
+ mDestinationTypes.add(newSchema.getSchemaType());
+ }
}
/**
@@ -87,7 +89,8 @@
* GenericDocument} to new version.
*/
@WorkerThread
- public void queryAndTransform(@NonNull String schemaType, @NonNull Migrator migrator)
+ public void queryAndTransform(@NonNull String schemaType, @NonNull Migrator migrator,
+ int currentVersion, int finalVersion)
throws IOException, AppSearchException, InterruptedException, ExecutionException {
File queryFile = File.createTempFile(/*prefix=*/"appsearch", /*suffix=*/null);
try (ParcelFileDescriptor fileDescriptor =
@@ -111,7 +114,7 @@
if (!result.isSuccess()) {
throw new AppSearchException(result.getResultCode(), result.getErrorMessage());
}
- readAndTransform(queryFile, migrator);
+ readAndTransform(queryFile, migrator, currentVersion, finalVersion);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} finally {
@@ -173,8 +176,9 @@
*
* <p>Save migrated {@link GenericDocument}s to the {@link #mMigratedFile}.
*/
- private void readAndTransform(@NonNull File file, @NonNull Migrator migrator)
- throws IOException {
+ private void readAndTransform(@NonNull File file, @NonNull Migrator migrator,
+ int currentVersion, int finalVersion)
+ throws IOException, AppSearchException {
try (DataInputStream inputStream = new DataInputStream(new FileInputStream(file));
DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(
mMigratedFile, /*append=*/ true))) {
@@ -187,9 +191,6 @@
// Nothing wrong. We just finished reading.
}
- int currentVersion = mCurrentVersionMap.get(document.getSchemaType());
- int finalVersion = mFinalVersionMap.get(document.getSchemaType());
-
GenericDocument newDocument;
if (currentVersion < finalVersion) {
newDocument = migrator.onUpgrade(currentVersion, finalVersion, document);
@@ -197,6 +198,18 @@
// currentVersion == finalVersion case won't trigger migration and get here.
newDocument = migrator.onDowngrade(currentVersion, finalVersion, document);
}
+
+ if (!mDestinationTypes.contains(newDocument.getSchemaType())) {
+ // we exit before the new schema has been set to AppSearch. So no
+ // observable changes will be applied to stored schemas and documents.
+ // And the temp file will be deleted at close(), which will be triggered at
+ // the end of try-with-resources block of SearchSessionImpl.
+ throw new AppSearchException(
+ RESULT_INVALID_SCHEMA,
+ "Receive a migrated document with schema type: "
+ + newDocument.getSchemaType()
+ + ". But the schema types doesn't exist in the request");
+ }
writeBundleToOutputStream(outputStream, newDocument.getBundle());
}
mAreDocumentsMigrated = true;
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 4dd1b79..3677489 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -19,7 +19,6 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
-import android.app.appsearch.exceptions.AppSearchException;
import android.app.appsearch.util.SchemaMigrationUtil;
import android.os.Bundle;
import android.os.ParcelableException;
@@ -647,8 +646,8 @@
new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
schemasPackageAccessibleBundles,
request.isForceOverride(),
- mUserId,
request.getVersion(),
+ mUserId,
new IAppSearchResultCallback.Stub() {
public void onResult(AppSearchResult result) {
executor.execute(() -> {
@@ -661,7 +660,7 @@
// Throw exception if there is any deleted types or
// incompatible types. That's the only case we swallowed
// in the AppSearchImpl#setSchema().
- checkDeletedAndIncompatible(
+ SchemaMigrationUtil.checkDeletedAndIncompatible(
setSchemaResponse.getDeletedTypes(),
setSchemaResponse.getIncompatibleTypes());
}
@@ -698,7 +697,7 @@
workExecutor.execute(() -> {
try {
// Migration process
- // 1. Generate the current and the final version map.
+ // 1. Validate and retrieve all active migrators.
AndroidFuture<AppSearchResult<GetSchemaResponse>> getSchemaFuture =
new AndroidFuture<>();
getSchema(callbackExecutor, getSchemaFuture::complete);
@@ -709,11 +708,18 @@
return;
}
GetSchemaResponse getSchemaResponse = getSchemaResult.getResultValue();
- Set<AppSearchSchema> currentSchemas = getSchemaResponse.getSchemas();
- Map<String, Integer> currentVersionMap = SchemaMigrationUtil.buildVersionMap(
- currentSchemas, getSchemaResponse.getVersion());
- Map<String, Integer> finalVersionMap = SchemaMigrationUtil.buildVersionMap(
- request.getSchemas(), request.getVersion());
+ int currentVersion = getSchemaResponse.getVersion();
+ int finalVersion = request.getVersion();
+ Map<String, Migrator> activeMigrators = SchemaMigrationUtil.getActiveMigrators(
+ getSchemaResponse.getSchemas(), request.getMigrators(), currentVersion,
+ finalVersion);
+
+ // No need to trigger migration if no migrator is active.
+ if (activeMigrators.isEmpty()) {
+ setSchemaNoMigrations(request, schemaBundles, schemasPackageAccessibleBundles,
+ callbackExecutor, callback);
+ return;
+ }
// 2. SetSchema with forceOverride=false, to retrieve the list of
// incompatible/deleted types.
@@ -725,8 +731,8 @@
new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
schemasPackageAccessibleBundles,
/*forceOverride=*/ false,
- mUserId,
request.getVersion(),
+ mUserId,
new IAppSearchResultCallback.Stub() {
public void onResult(AppSearchResult result) {
setSchemaFuture.complete(result);
@@ -741,46 +747,27 @@
SetSchemaResponse setSchemaResponse =
new SetSchemaResponse(setSchemaResult.getResultValue());
- // 1. If forceOverride is false, check that all incompatible types will be migrated.
+ // 3. If forceOverride is false, check that all incompatible types will be migrated.
// If some aren't we must throw an error, rather than proceeding and deleting those
// types.
if (!request.isForceOverride()) {
- Set<String> unmigratedTypes =
- SchemaMigrationUtil.getUnmigratedIncompatibleTypes(
- setSchemaResponse.getIncompatibleTypes(),
- request.getMigrators(),
- currentVersionMap,
- finalVersionMap);
-
- // check if there are any unmigrated types or deleted types. If there are, we
- // will throw an exception.
- // Since the force override is false, the schema will not have been set if there
- // are any incompatible or deleted types.
- checkDeletedAndIncompatible(
- setSchemaResponse.getDeletedTypes(), unmigratedTypes);
+ SchemaMigrationUtil.checkDeletedAndIncompatibleAfterMigration(setSchemaResponse,
+ activeMigrators.keySet());
}
- try (AppSearchMigrationHelper migrationHelper =
- new AppSearchMigrationHelper(
- mService, mUserId, currentVersionMap, finalVersionMap,
- mPackageName, mDatabaseName)) {
- Map<String, Migrator> migratorMap = request.getMigrators();
+ try (AppSearchMigrationHelper migrationHelper = new AppSearchMigrationHelper(
+ mService, mUserId, mPackageName, mDatabaseName, request.getSchemas())) {
- // 2. Trigger migration for all migrators.
+ // 4. Trigger migration for all migrators.
// TODO(b/177266929) trigger migration for all types together rather than
// separately.
- Set<String> migratedTypes = new ArraySet<>();
- for (Map.Entry<String, Migrator> entry : migratorMap.entrySet()) {
- String schemaType = entry.getKey();
- Migrator migrator = entry.getValue();
- if (SchemaMigrationUtil.shouldTriggerMigration(
- schemaType, migrator, currentVersionMap, finalVersionMap)) {
- migrationHelper.queryAndTransform(schemaType, migrator);
- migratedTypes.add(schemaType);
- }
+ for (Map.Entry<String, Migrator> entry : activeMigrators.entrySet()) {
+ migrationHelper.queryAndTransform(/*schemaType=*/ entry.getKey(),
+ /*migrator=*/ entry.getValue(), currentVersion,
+ finalVersion);
}
- // 3. SetSchema a second time with forceOverride=true if the first attempted
+ // 5. SetSchema a second time with forceOverride=true if the first attempted
// failed.
if (!setSchemaResponse.getIncompatibleTypes().isEmpty()
|| !setSchemaResponse.getDeletedTypes().isEmpty()) {
@@ -809,13 +796,16 @@
// error in the first setSchema call, all other errors will be thrown at
// the first time.
callbackExecutor.execute(() -> callback.accept(
- AppSearchResult.newFailedResult(setSchemaResult)));
+ AppSearchResult.newFailedResult(setSchema2Result)));
return;
}
}
SetSchemaResponse.Builder responseBuilder = setSchemaResponse.toBuilder()
- .addMigratedTypes(migratedTypes);
+ .addMigratedTypes(activeMigrators.keySet());
+
+ // 6. Put all the migrated documents into the index, now that the new schema is
+ // set.
AppSearchResult<SetSchemaResponse> putResult =
migrationHelper.putMigratedDocuments(responseBuilder);
callbackExecutor.execute(() -> callback.accept(putResult));
@@ -826,17 +816,4 @@
}
});
}
-
- /** Checks the setSchema() call won't delete any types or has incompatible types. */
- //TODO(b/177266929) move this method to util
- private void checkDeletedAndIncompatible(Set<String> deletedTypes,
- Set<String> incompatibleTypes)
- throws AppSearchException {
- if (!deletedTypes.isEmpty() || !incompatibleTypes.isEmpty()) {
- String newMessage = "Schema is incompatible."
- + "\n Deleted types: " + deletedTypes
- + "\n Incompatible types: " + incompatibleTypes;
- throw new AppSearchException(AppSearchResult.RESULT_INVALID_SCHEMA, newMessage);
- }
- }
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index a8ac27c..4d05ad7 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -40,6 +40,7 @@
* packages. The value List contains PackageIdentifier Bundles.
* @param forceOverride Whether to apply the new schema even if it is incompatible. All
* incompatible documents will be deleted.
+ * @param schemaVersion The overall schema version number of the request.
* @param userId Id of the calling user
* @param callback {@link IAppSearchResultCallback#onResult} will be called with an
* {@link AppSearchResult}<{@link Bundle}>, where the value are
@@ -52,8 +53,8 @@
in List<String> schemasNotDisplayedBySystem,
in Map<String, List<Bundle>> schemasPackageAccessibleBundles,
boolean forceOverride,
- in int userId,
in int schemaVersion,
+ in int userId,
in IAppSearchResultCallback callback);
/**
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
index c9473bd..32d7e043 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
@@ -20,12 +20,11 @@
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.Migrator;
+import android.app.appsearch.SetSchemaResponse;
import android.app.appsearch.exceptions.AppSearchException;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Log;
-import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
@@ -36,84 +35,70 @@
* @hide
*/
public final class SchemaMigrationUtil {
- private static final String TAG = "AppSearchMigrateUtil";
-
private SchemaMigrationUtil() {}
- /**
- * Finds out which incompatible schema type won't be migrated by comparing its current and final
- * version number.
- */
+ /** Returns all active {@link Migrator}s that need to be triggered in this migration. */
@NonNull
- public static Set<String> getUnmigratedIncompatibleTypes(
- @NonNull Set<String> incompatibleSchemaTypes,
+ public static Map<String, Migrator> getActiveMigrators(
+ @NonNull Set<AppSearchSchema> existingSchemas,
@NonNull Map<String, Migrator> migrators,
- @NonNull Map<String, Integer> currentVersionMap,
- @NonNull Map<String, Integer> finalVersionMap)
- throws AppSearchException {
- Set<String> unmigratedSchemaTypes = new ArraySet<>();
- for (String unmigratedSchemaType : incompatibleSchemaTypes) {
- Integer currentVersion = currentVersionMap.get(unmigratedSchemaType);
- Integer finalVersion = finalVersionMap.get(unmigratedSchemaType);
- if (currentVersion == null) {
- // impossible, we have done something wrong.
- throw new AppSearchException(
- AppSearchResult.RESULT_UNKNOWN_ERROR,
- "Cannot find the current version number for schema type: "
- + unmigratedSchemaType);
- }
- if (finalVersion == null) {
- // The schema doesn't exist in the SetSchemaRequest.
- unmigratedSchemaTypes.add(unmigratedSchemaType);
- continue;
- }
- // we don't have migrator or won't trigger migration for this schema type.
- Migrator migrator = migrators.get(unmigratedSchemaType);
- if (migrator == null
- || !migrator.shouldMigrate(currentVersion, finalVersion)) {
- unmigratedSchemaTypes.add(unmigratedSchemaType);
+ int currentVersion,
+ int finalVersion) {
+ if (currentVersion == finalVersion) {
+ return Collections.emptyMap();
+ }
+ Set<String> existingTypes = new ArraySet<>(existingSchemas.size());
+ for (AppSearchSchema schema : existingSchemas) {
+ existingTypes.add(schema.getSchemaType());
+ }
+
+ Map<String, Migrator> activeMigrators = new ArrayMap<>();
+ for (Map.Entry<String, Migrator> entry : migrators.entrySet()) {
+ // The device contains the source type, and we should trigger migration for the type.
+ String schemaType = entry.getKey();
+ Migrator migrator = entry.getValue();
+ if (existingTypes.contains(schemaType)
+ && migrator.shouldMigrate(currentVersion, finalVersion)) {
+ activeMigrators.put(schemaType, migrator);
}
}
- return Collections.unmodifiableSet(unmigratedSchemaTypes);
+ return activeMigrators;
}
/**
- * Triggers upgrade or downgrade migration for the given schema type if its version stored in
- * AppSearch is different with the version in the request.
- *
- * @return {@code True} if we trigger the migration for the given type.
+ * Checks the setSchema() call won't delete any types or has incompatible types after all {@link
+ * Migrator} has been triggered..
*/
- public static boolean shouldTriggerMigration(
- @NonNull String schemaType,
- @NonNull Migrator migrator,
- @NonNull Map<String, Integer> currentVersionMap,
- @NonNull Map<String, Integer> finalVersionMap)
+ public static void checkDeletedAndIncompatibleAfterMigration(
+ @NonNull SetSchemaResponse setSchemaResponse, @NonNull Set<String> activeMigrators)
throws AppSearchException {
- Integer currentVersion = currentVersionMap.get(schemaType);
- Integer finalVersion = finalVersionMap.get(schemaType);
- if (currentVersion == null) {
- Log.d(TAG, "The SchemaType: " + schemaType + " not present in AppSearch.");
- return false;
- }
- if (finalVersion == null) {
- throw new AppSearchException(
- AppSearchResult.RESULT_INVALID_ARGUMENT,
- "Receive a migrator for schema type : "
- + schemaType
- + ", but the schema doesn't exist in the request.");
- }
- return migrator.shouldMigrate(currentVersion, finalVersion);
+ Set<String> unmigratedIncompatibleTypes =
+ new ArraySet<>(setSchemaResponse.getIncompatibleTypes());
+ unmigratedIncompatibleTypes.removeAll(activeMigrators);
+
+ Set<String> unmigratedDeletedTypes = new ArraySet<>(setSchemaResponse.getDeletedTypes());
+ unmigratedDeletedTypes.removeAll(activeMigrators);
+
+ // check if there are any unmigrated incompatible types or deleted types. If there
+ // are, we will getActiveMigratorsthrow an exception. That's the only case we
+ // swallowed in the AppSearchImpl#setSchema().
+ // Since the force override is false, the schema will not have been set if there are
+ // any incompatible or deleted types.
+ checkDeletedAndIncompatible(unmigratedDeletedTypes, unmigratedIncompatibleTypes);
}
- /** Builds a Map of SchemaType and its version of given set of {@link AppSearchSchema}. */
- //TODO(b/182620003) remove this method once support migrate to another type
- @NonNull
- public static Map<String, Integer> buildVersionMap(
- @NonNull Collection<AppSearchSchema> schemas, int version) {
- Map<String, Integer> currentVersionMap = new ArrayMap<>(schemas.size());
- for (AppSearchSchema currentSchema : schemas) {
- currentVersionMap.put(currentSchema.getSchemaType(), version);
+ /** Checks the setSchema() call won't delete any types or has incompatible types. */
+ public static void checkDeletedAndIncompatible(
+ @NonNull Set<String> deletedTypes, @NonNull Set<String> incompatibleTypes)
+ throws AppSearchException {
+ if (deletedTypes.size() > 0 || incompatibleTypes.size() > 0) {
+ String newMessage =
+ "Schema is incompatible."
+ + "\n Deleted types: "
+ + deletedTypes
+ + "\n Incompatible types: "
+ + incompatibleTypes;
+ throw new AppSearchException(AppSearchResult.RESULT_INVALID_SCHEMA, newMessage);
}
- return currentVersionMap;
}
}
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 5f3a3ae..b46e85d 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -68,6 +68,10 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
/** TODO(b/142567528): add comments when implement this class */
public class AppSearchManagerService extends SystemService {
@@ -77,6 +81,14 @@
private ImplInstanceManager mImplInstanceManager;
private UserManager mUserManager;
+ // Never call shutdownNow(). It will cancel the futures it's returned. And since
+ // Executor#execute won't return anything, we will hang forever waiting for the execution.
+ // AppSearch multi-thread execution is guarded by Read & Write Lock in AppSearchImpl, all
+ // mutate requests will need to gain write lock and query requests need to gain read lock.
+ private static final Executor EXECUTOR = new ThreadPoolExecutor(/*corePoolSize=*/1,
+ Runtime.getRuntime().availableProcessors(), /*keepAliveTime*/ 60L, TimeUnit.SECONDS,
+ new SynchronousQueue<Runnable>());
+
// Cache of unlocked user ids so we don't have to query UserManager service each time. The
// "locked" suffix refers to the fact that access to the field should be locked; unrelated to
// the unlocked status of user ids.
@@ -157,8 +169,8 @@
@NonNull List<String> schemasNotDisplayedBySystem,
@NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles,
boolean forceOverride,
- @UserIdInt int userId,
int schemaVersion,
+ @UserIdInt int userId,
@NonNull IAppSearchResultCallback callback) {
Preconditions.checkNotNull(packageName);
Preconditions.checkNotNull(databaseName);
@@ -166,41 +178,41 @@
Preconditions.checkNotNull(callback);
int callingUid = Binder.getCallingUid();
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++) {
- schemas.add(new AppSearchSchema(schemaBundles.get(i)));
- }
- Map<String, List<PackageIdentifier>> schemasPackageAccessible =
- new ArrayMap<>(schemasPackageAccessibleBundles.size());
- for (Map.Entry<String, List<Bundle>> entry :
- schemasPackageAccessibleBundles.entrySet()) {
- List<PackageIdentifier> packageIdentifiers =
- new ArrayList<>(entry.getValue().size());
- for (int i = 0; i < entry.getValue().size(); i++) {
- packageIdentifiers.add(new PackageIdentifier(entry.getValue().get(i)));
+ EXECUTOR.execute(() -> {
+ try {
+ verifyUserUnlocked(callingUserId);
+ verifyCallingPackage(callingUid, packageName);
+ List<AppSearchSchema> schemas = new ArrayList<>(schemaBundles.size());
+ for (int i = 0; i < schemaBundles.size(); i++) {
+ schemas.add(new AppSearchSchema(schemaBundles.get(i)));
}
- schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
+ Map<String, List<PackageIdentifier>> schemasPackageAccessible =
+ new ArrayMap<>(schemasPackageAccessibleBundles.size());
+ for (Map.Entry<String, List<Bundle>> entry :
+ schemasPackageAccessibleBundles.entrySet()) {
+ List<PackageIdentifier> packageIdentifiers =
+ new ArrayList<>(entry.getValue().size());
+ for (int i = 0; i < entry.getValue().size(); i++) {
+ packageIdentifiers.add(
+ new PackageIdentifier(entry.getValue().get(i)));
+ }
+ schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
+ }
+ AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId);
+ SetSchemaResponse setSchemaResponse = impl.setSchema(
+ packageName,
+ databaseName,
+ schemas,
+ schemasNotDisplayedBySystem,
+ schemasPackageAccessible,
+ forceOverride,
+ schemaVersion);
+ invokeCallbackOnResult(callback,
+ AppSearchResult.newSuccessfulResult(setSchemaResponse.getBundle()));
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
}
- AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId);
- SetSchemaResponse setSchemaResponse = impl.setSchema(
- packageName,
- databaseName,
- schemas,
- schemasNotDisplayedBySystem,
- schemasPackageAccessible,
- forceOverride,
- schemaVersion);
- invokeCallbackOnResult(callback,
- AppSearchResult.newSuccessfulResult(setSchemaResponse.getBundle()));
- } catch (Throwable t) {
- invokeCallbackOnError(callback, t);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ });
}
@Override
@@ -214,20 +226,20 @@
Preconditions.checkNotNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- verifyUserUnlocked(callingUserId);
- verifyCallingPackage(callingUid, packageName);
- AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(callingUserId);
- GetSchemaResponse response = impl.getSchema(packageName, databaseName);
- invokeCallbackOnResult(
- callback, AppSearchResult.newSuccessfulResult(response.getBundle()));
- } catch (Throwable t) {
- invokeCallbackOnError(callback, t);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ EXECUTOR.execute(() -> {
+ try {
+ verifyUserUnlocked(callingUserId);
+ verifyCallingPackage(callingUid, packageName);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
+ GetSchemaResponse response = impl.getSchema(packageName, databaseName);
+ invokeCallbackOnResult(
+ callback,
+ AppSearchResult.newSuccessfulResult(response.getBundle()));
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
+ }
+ });
}
@Override
@@ -241,19 +253,19 @@
Preconditions.checkNotNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- verifyUserUnlocked(callingUserId);
- verifyCallingPackage(callingUid, packageName);
- AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(callingUserId);
- List<String> namespaces = impl.getNamespaces(packageName, databaseName);
- invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(namespaces));
- } catch (Throwable t) {
- invokeCallbackOnError(callback, t);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ EXECUTOR.execute(() -> {
+ try {
+ verifyUserUnlocked(callingUserId);
+ verifyCallingPackage(callingUid, packageName);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
+ List<String> namespaces = impl.getNamespaces(packageName, databaseName);
+ invokeCallbackOnResult(callback,
+ AppSearchResult.newSuccessfulResult(namespaces));
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
+ }
+ });
}
@Override
@@ -269,31 +281,30 @@
Preconditions.checkNotNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- verifyUserUnlocked(callingUserId);
- verifyCallingPackage(callingUid, packageName);
- AppSearchBatchResult.Builder<String, Void> resultBuilder =
- new AppSearchBatchResult.Builder<>();
- AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(callingUserId);
- for (int i = 0; i < documentBundles.size(); i++) {
- GenericDocument document = new GenericDocument(documentBundles.get(i));
- try {
- // TODO(b/173451571): reduce burden of binder thread by enqueue request onto
- // a separate thread.
- impl.putDocument(packageName, databaseName, document, /*logger=*/ null);
- resultBuilder.setSuccess(document.getUri(), /*result=*/ null);
- } catch (Throwable t) {
- resultBuilder.setResult(document.getUri(), throwableToFailedResult(t));
+ EXECUTOR.execute(() -> {
+ try {
+ verifyUserUnlocked(callingUserId);
+ verifyCallingPackage(callingUid, packageName);
+ AppSearchBatchResult.Builder<String, Void> resultBuilder =
+ new AppSearchBatchResult.Builder<>();
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
+ for (int i = 0; i < documentBundles.size(); i++) {
+ GenericDocument document = new GenericDocument(documentBundles.get(i));
+ try {
+ impl.putDocument(packageName, databaseName, document,
+ /*logger=*/ null);
+ resultBuilder.setSuccess(document.getUri(), /*result=*/ null);
+ } catch (Throwable t) {
+ resultBuilder.setResult(document.getUri(),
+ throwableToFailedResult(t));
+ }
}
+ invokeCallbackOnResult(callback, resultBuilder.build());
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
}
- invokeCallbackOnResult(callback, resultBuilder.build());
- } catch (Throwable t) {
- invokeCallbackOnError(callback, t);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ });
}
@Override
@@ -312,38 +323,36 @@
Preconditions.checkNotNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- verifyUserUnlocked(callingUserId);
- verifyCallingPackage(callingUid, packageName);
- AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
- new AppSearchBatchResult.Builder<>();
- AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(callingUserId);
- for (int i = 0; i < uris.size(); i++) {
- String uri = uris.get(i);
- try {
- GenericDocument document =
- impl.getDocument(
- packageName,
- databaseName,
- namespace,
- uri,
- typePropertyPaths);
- resultBuilder.setSuccess(uri, document.getBundle());
- } catch (Throwable t) {
- resultBuilder.setResult(uri, throwableToFailedResult(t));
+ EXECUTOR.execute(() -> {
+ try {
+ verifyUserUnlocked(callingUserId);
+ verifyCallingPackage(callingUid, packageName);
+ AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
+ new AppSearchBatchResult.Builder<>();
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
+ for (int i = 0; i < uris.size(); i++) {
+ String uri = uris.get(i);
+ try {
+ GenericDocument document =
+ impl.getDocument(
+ packageName,
+ databaseName,
+ namespace,
+ uri,
+ typePropertyPaths);
+ resultBuilder.setSuccess(uri, document.getBundle());
+ } catch (Throwable t) {
+ resultBuilder.setResult(uri, throwableToFailedResult(t));
+ }
}
+ invokeCallbackOnResult(callback, resultBuilder.build());
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
}
- invokeCallbackOnResult(callback, resultBuilder.build());
- } catch (Throwable t) {
- invokeCallbackOnError(callback, t);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ });
}
- // TODO(sidchhabra): Do this in a threadpool.
@Override
public void query(
@NonNull String packageName,
@@ -359,26 +368,25 @@
Preconditions.checkNotNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- verifyUserUnlocked(callingUserId);
- verifyCallingPackage(callingUid, packageName);
- AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(callingUserId);
- SearchResultPage searchResultPage =
- impl.query(
- packageName,
- databaseName,
- queryExpression,
- new SearchSpec(searchSpecBundle));
- invokeCallbackOnResult(
- callback,
- AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
- } catch (Throwable t) {
- invokeCallbackOnError(callback, t);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ EXECUTOR.execute(() -> {
+ try {
+ verifyUserUnlocked(callingUserId);
+ verifyCallingPackage(callingUid, packageName);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
+ SearchResultPage searchResultPage =
+ impl.query(
+ packageName,
+ databaseName,
+ queryExpression,
+ new SearchSpec(searchSpecBundle));
+ invokeCallbackOnResult(
+ callback,
+ AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
+ }
+ });
}
@Override
@@ -394,26 +402,25 @@
Preconditions.checkNotNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- verifyUserUnlocked(callingUserId);
- verifyCallingPackage(callingUid, packageName);
- AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(callingUserId);
- SearchResultPage searchResultPage =
- impl.globalQuery(
- queryExpression,
- new SearchSpec(searchSpecBundle),
- packageName,
- callingUid);
- invokeCallbackOnResult(
- callback,
- AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
- } catch (Throwable t) {
- invokeCallbackOnError(callback, t);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ EXECUTOR.execute(() -> {
+ try {
+ verifyUserUnlocked(callingUserId);
+ verifyCallingPackage(callingUid, packageName);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
+ SearchResultPage searchResultPage =
+ impl.globalQuery(
+ queryExpression,
+ new SearchSpec(searchSpecBundle),
+ packageName,
+ callingUid);
+ invokeCallbackOnResult(
+ callback,
+ AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
+ }
+ });
}
@Override
@@ -424,39 +431,37 @@
Preconditions.checkNotNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
- final long callingIdentity = Binder.clearCallingIdentity();
// TODO(b/162450968) check nextPageToken is being advanced by the same uid as originally
// opened it
- try {
- verifyUserUnlocked(callingUserId);
- AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(callingUserId);
- SearchResultPage searchResultPage = impl.getNextPage(nextPageToken);
- invokeCallbackOnResult(
- callback,
- AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
- } catch (Throwable t) {
- invokeCallbackOnError(callback, t);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ EXECUTOR.execute(() -> {
+ try {
+ verifyUserUnlocked(callingUserId);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
+ SearchResultPage searchResultPage = impl.getNextPage(nextPageToken);
+ invokeCallbackOnResult(
+ callback,
+ AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
+ }
+ });
}
@Override
public void invalidateNextPageToken(long nextPageToken, @UserIdInt int userId) {
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- verifyUserUnlocked(callingUserId);
- AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(callingUserId);
- impl.invalidateNextPageToken(nextPageToken);
- } catch (Throwable t) {
- Log.e(TAG, "Unable to invalidate the query page token", t);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ EXECUTOR.execute(() -> {
+ try {
+ verifyUserUnlocked(callingUserId);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
+ impl.invalidateNextPageToken(nextPageToken);
+ } catch (Throwable t) {
+ Log.e(TAG, "Unable to invalidate the query page token", t);
+ }
+ });
}
@Override
@@ -470,34 +475,34 @@
@NonNull IAppSearchResultCallback callback) {
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- verifyCallingPackage(callingUid, packageName);
- AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(callingUserId);
- // we don't need to append the file. The file is always brand new.
- try (DataOutputStream outputStream = new DataOutputStream(
- new FileOutputStream(fileDescriptor.getFileDescriptor()))) {
- SearchResultPage searchResultPage = impl.query(
- packageName,
- databaseName,
- queryExpression,
- new SearchSpec(searchSpecBundle));
- while (!searchResultPage.getResults().isEmpty()) {
- for (int i = 0; i < searchResultPage.getResults().size(); i++) {
- AppSearchMigrationHelper.writeBundleToOutputStream(
- outputStream, searchResultPage.getResults().get(i)
- .getGenericDocument().getBundle());
+ EXECUTOR.execute(() -> {
+ try {
+ verifyCallingPackage(callingUid, packageName);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
+ // we don't need to append the file. The file is always brand new.
+ try (DataOutputStream outputStream = new DataOutputStream(
+ new FileOutputStream(fileDescriptor.getFileDescriptor()))) {
+ SearchResultPage searchResultPage = impl.query(
+ packageName,
+ databaseName,
+ queryExpression,
+ new SearchSpec(searchSpecBundle));
+ while (!searchResultPage.getResults().isEmpty()) {
+ for (int i = 0; i < searchResultPage.getResults().size(); i++) {
+ AppSearchMigrationHelper.writeBundleToOutputStream(
+ outputStream, searchResultPage.getResults().get(i)
+ .getGenericDocument().getBundle());
+ }
+ searchResultPage = impl.getNextPage(
+ searchResultPage.getNextPageToken());
}
- searchResultPage = impl.getNextPage(searchResultPage.getNextPageToken());
}
+ invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
}
- invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
- } catch (Throwable t) {
- invokeCallbackOnError(callback, t);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ });
}
@Override
@@ -509,46 +514,46 @@
@NonNull IAppSearchResultCallback callback) {
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- verifyCallingPackage(callingUid, packageName);
- AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(callingUserId);
+ EXECUTOR.execute(() -> {
+ try {
+ verifyCallingPackage(callingUid, packageName);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
- GenericDocument document;
- ArrayList<Bundle> migrationFailureBundles = new ArrayList<>();
- try (DataInputStream inputStream = new DataInputStream(
- new FileInputStream(fileDescriptor.getFileDescriptor()))) {
- while (true) {
- try {
- document = AppSearchMigrationHelper
- .readDocumentFromInputStream(inputStream);
- } catch (EOFException e) {
- // nothing wrong, we just finish the reading.
- break;
- }
- try {
- impl.putDocument(packageName, databaseName, document, /*logger=*/ null);
- } catch (Throwable t) {
- migrationFailureBundles.add(
- new SetSchemaResponse.MigrationFailure.Builder()
- .setNamespace(document.getNamespace())
- .setSchemaType(document.getSchemaType())
- .setUri(document.getUri())
- .setAppSearchResult(
- AppSearchResult.throwableToFailedResult(t))
- .build().getBundle());
+ GenericDocument document;
+ ArrayList<Bundle> migrationFailureBundles = new ArrayList<>();
+ try (DataInputStream inputStream = new DataInputStream(
+ new FileInputStream(fileDescriptor.getFileDescriptor()))) {
+ while (true) {
+ try {
+ document = AppSearchMigrationHelper
+ .readDocumentFromInputStream(inputStream);
+ } catch (EOFException e) {
+ // nothing wrong, we just finish the reading.
+ break;
+ }
+ try {
+ impl.putDocument(packageName, databaseName, document,
+ /*logger=*/ null);
+ } catch (Throwable t) {
+ migrationFailureBundles.add(
+ new SetSchemaResponse.MigrationFailure.Builder()
+ .setNamespace(document.getNamespace())
+ .setSchemaType(document.getSchemaType())
+ .setUri(document.getUri())
+ .setAppSearchResult(AppSearchResult
+ .throwableToFailedResult(t))
+ .build().getBundle());
+ }
}
}
+ impl.persistToDisk();
+ invokeCallbackOnResult(callback,
+ AppSearchResult.newSuccessfulResult(migrationFailureBundles));
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
}
- impl.persistToDisk();
- invokeCallbackOnResult(callback,
- AppSearchResult.newSuccessfulResult(migrationFailureBundles));
- } catch (Throwable t) {
- invokeCallbackOnError(callback, t);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ });
}
@Override
@@ -567,26 +572,25 @@
Objects.requireNonNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- verifyUserUnlocked(callingUserId);
+ EXECUTOR.execute(() -> {
+ try {
+ verifyUserUnlocked(callingUserId);
- if (systemUsage) {
- // TODO(b/183031844): Validate that the call comes from the system
+ if (systemUsage) {
+ // TODO(b/183031844): Validate that the call comes from the system
+ }
+
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
+ impl.reportUsage(
+ packageName, databaseName, namespace, uri,
+ usageTimeMillis, systemUsage);
+ invokeCallbackOnResult(
+ callback, AppSearchResult.newSuccessfulResult(/*result=*/ null));
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
}
-
- AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(callingUserId);
- impl.reportUsage(
- packageName, databaseName, namespace, uri,
- usageTimeMillis, systemUsage);
- invokeCallbackOnResult(
- callback, AppSearchResult.newSuccessfulResult(/*result=*/ null));
- } catch (Throwable t) {
- invokeCallbackOnError(callback, t);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ });
}
@Override
@@ -603,29 +607,28 @@
Preconditions.checkNotNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- verifyUserUnlocked(callingUserId);
- verifyCallingPackage(callingUid, packageName);
- AppSearchBatchResult.Builder<String, Void> resultBuilder =
- new AppSearchBatchResult.Builder<>();
- AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(callingUserId);
- for (int i = 0; i < uris.size(); i++) {
- String uri = uris.get(i);
- try {
- impl.remove(packageName, databaseName, namespace, uri);
- resultBuilder.setSuccess(uri, /*result= */ null);
- } catch (Throwable t) {
- resultBuilder.setResult(uri, throwableToFailedResult(t));
+ EXECUTOR.execute(() -> {
+ try {
+ verifyUserUnlocked(callingUserId);
+ verifyCallingPackage(callingUid, packageName);
+ AppSearchBatchResult.Builder<String, Void> resultBuilder =
+ new AppSearchBatchResult.Builder<>();
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
+ for (int i = 0; i < uris.size(); i++) {
+ String uri = uris.get(i);
+ try {
+ impl.remove(packageName, databaseName, namespace, uri);
+ resultBuilder.setSuccess(uri, /*result= */ null);
+ } catch (Throwable t) {
+ resultBuilder.setResult(uri, throwableToFailedResult(t));
+ }
}
+ invokeCallbackOnResult(callback, resultBuilder.build());
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
}
- invokeCallbackOnResult(callback, resultBuilder.build());
- } catch (Throwable t) {
- invokeCallbackOnError(callback, t);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ });
}
@Override
@@ -643,23 +646,22 @@
Preconditions.checkNotNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- verifyUserUnlocked(callingUserId);
- verifyCallingPackage(callingUid, packageName);
- AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(callingUserId);
- impl.removeByQuery(
- packageName,
- databaseName,
- queryExpression,
- new SearchSpec(searchSpecBundle));
- invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
- } catch (Throwable t) {
- invokeCallbackOnError(callback, t);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ EXECUTOR.execute(() -> {
+ try {
+ verifyUserUnlocked(callingUserId);
+ verifyCallingPackage(callingUid, packageName);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
+ impl.removeByQuery(
+ packageName,
+ databaseName,
+ queryExpression,
+ new SearchSpec(searchSpecBundle));
+ invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
+ }
+ });
}
@Override
@@ -673,38 +675,37 @@
Preconditions.checkNotNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- verifyUserUnlocked(callingUserId);
- verifyCallingPackage(callingUid, packageName);
- AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(callingUserId);
- StorageInfo storageInfo = impl.getStorageInfoForDatabase(packageName, databaseName);
- Bundle storageInfoBundle = storageInfo.getBundle();
- invokeCallbackOnResult(
- callback, AppSearchResult.newSuccessfulResult(storageInfoBundle));
- } catch (Throwable t) {
- invokeCallbackOnError(callback, t);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ EXECUTOR.execute(() -> {
+ try {
+ verifyUserUnlocked(callingUserId);
+ verifyCallingPackage(callingUid, packageName);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
+ StorageInfo storageInfo = impl.getStorageInfoForDatabase(packageName,
+ databaseName);
+ Bundle storageInfoBundle = storageInfo.getBundle();
+ invokeCallbackOnResult(
+ callback, AppSearchResult.newSuccessfulResult(storageInfoBundle));
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
+ }
+ });
}
@Override
public void persistToDisk(@UserIdInt int userId) {
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- verifyUserUnlocked(callingUserId);
- AppSearchImpl impl =
- mImplInstanceManager.getAppSearchImpl(callingUserId);
- impl.persistToDisk();
- } catch (Throwable t) {
- Log.e(TAG, "Unable to persist the data to disk", t);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ EXECUTOR.execute(() -> {
+ try {
+ verifyUserUnlocked(callingUserId);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(callingUserId);
+ impl.persistToDisk();
+ } catch (Throwable t) {
+ Log.e(TAG, "Unable to persist the data to disk", t);
+ }
+ });
}
@Override
@@ -712,16 +713,15 @@
Preconditions.checkNotNull(callback);
int callingUid = Binder.getCallingUid();
int callingUserId = handleIncomingUser(userId, callingUid);
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- verifyUserUnlocked(callingUserId);
- mImplInstanceManager.getOrCreateAppSearchImpl(mContext, callingUserId);
- invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
- } catch (Throwable t) {
- invokeCallbackOnError(callback, t);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
+ EXECUTOR.execute(() -> {
+ try {
+ verifyUserUnlocked(callingUserId);
+ mImplInstanceManager.getOrCreateAppSearchImpl(mContext, callingUserId);
+ invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
+ } catch (Throwable t) {
+ invokeCallbackOnError(callback, t);
+ }
+ });
}
private void verifyUserUnlocked(int callingUserId) {
diff --git a/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl b/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl
index 2f21ce3..a10cbbe 100644
--- a/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl
+++ b/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl
@@ -42,4 +42,5 @@
AlarmManager.AlarmClockInfo getNextAlarmClock(int userId);
long currentNetworkTimeMillis();
boolean canScheduleExactAlarms();
+ int getConfigVersion();
}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index 60f6475..e1bfde1 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -64,6 +64,16 @@
*/
public static final int REASON_RESTRICTED_BUCKET =
JobProtoEnums.STOP_REASON_RESTRICTED_BUCKET; // 6.
+ /**
+ * The app was uninstalled.
+ * @hide
+ */
+ public static final int DEBUG_REASON_UNINSTALL = 7;
+ /**
+ * The app's data was cleared.
+ * @hide
+ */
+ public static final int DEBUG_REASON_DATA_CLEARED = 8;
/**
* All the stop reason codes. This should be regarded as an immutable array at runtime.
@@ -187,8 +197,8 @@
*/
public static final int STOP_REASON_APP_STANDBY = 12;
/**
- * The user stopped the job. This can happen either through force-stop, or via adb shell
- * commands.
+ * The user stopped the job. This can happen either through force-stop, adb shell commands,
+ * or uninstalling.
*/
public static final int STOP_REASON_USER = 13;
/** The system is doing some processing that requires stopping this job. */
diff --git a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
index 6ae91a0..6d2bd47 100644
--- a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
@@ -37,7 +37,8 @@
/**
* Cancel the jobs for a given uid (e.g. when app data is cleared)
*/
- void cancelJobsForUid(int uid, @JobParameters.StopReason int reason, String debugReason);
+ void cancelJobsForUid(int uid, @JobParameters.StopReason int reason, int debugReasonCode,
+ String debugReason);
/**
* These are for activity manager to communicate to use what is currently performing backups.
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 901daa7..08e1a95 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -538,6 +538,7 @@
public long PRIORITY_ALARM_DELAY = DEFAULT_PRIORITY_ALARM_DELAY;
private long mLastAllowWhileIdleWhitelistDuration = -1;
+ private int mVersion = 0;
Constants() {
updateAllowWhileIdleWhitelistDurationLocked();
@@ -546,6 +547,12 @@
}
}
+ public int getVersion() {
+ synchronized (mLock) {
+ return mVersion;
+ }
+ }
+
public void start() {
mInjector.registerDeviceConfigListener(this);
onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_ALARM_MANAGER));
@@ -568,6 +575,7 @@
public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
boolean standbyQuotaUpdated = false;
synchronized (mLock) {
+ mVersion++;
for (String name : properties.getKeyset()) {
if (name == null) {
continue;
@@ -749,6 +757,9 @@
pw.increaseIndent();
+ pw.print("version", mVersion);
+ pw.println();
+
pw.print(KEY_MIN_FUTURITY);
pw.print("=");
TimeUtils.formatDuration(MIN_FUTURITY, pw);
@@ -2219,6 +2230,13 @@
}
@Override
+ public int getConfigVersion() {
+ getContext().enforceCallingOrSelfPermission(Manifest.permission.DUMP,
+ "getConfigVersion");
+ return mConstants.getVersion();
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
@@ -4698,6 +4716,10 @@
final String tz = getNextArgRequired();
getBinderService().setTimeZone(tz);
return 0;
+ case "get-config-version":
+ final int version = getBinderService().getConfigVersion();
+ pw.println(version);
+ return 0;
default:
return handleDefaultCommands(cmd);
}
@@ -4718,6 +4740,10 @@
pw.println(" since the Epoch.");
pw.println(" set-timezone TZ");
pw.println(" Set the system timezone to TZ where TZ is an Olson id.");
+ pw.println(" get-config-version");
+ pw.println(" Returns an integer denoting the version of device_config keys the"
+ + " service is sync'ed to. As long as this returns the same version, the values"
+ + " of the config are guaranteed to remain the same.");
}
}
}
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 41b342c..8b9eca6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -747,12 +747,14 @@
+ " in user " + userId);
}
synchronized (mLock) {
- // By the time we get here, the process should have
- // already been stopped, so the app wouldn't get the
- // stop reason, so just put USER instead of UNINSTALL
- // or DISABLED.
+ // There's no guarantee that the process has been
+ // stopped by the time we get here, but since this is
+ // a user-initiated action, it should be fine to just
+ // put USER instead of UNINSTALL or DISABLED.
cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
- JobParameters.STOP_REASON_USER, "app disabled");
+ JobParameters.STOP_REASON_USER,
+ JobParameters.DEBUG_REASON_UNINSTALL,
+ "app disabled");
}
}
} catch (RemoteException|IllegalArgumentException e) {
@@ -783,25 +785,22 @@
} else {
Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
}
- } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
- // If this is an outright uninstall rather than the first half of an
- // app update sequence, cancel the jobs associated with the app.
- if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
- int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
- if (DEBUG) {
- Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
+ } else if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
+ int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ if (DEBUG) {
+ Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
+ }
+ synchronized (mLock) {
+ // There's no guarantee that the process has been stopped by the time we
+ // get here, but since this is generally a user-initiated action, it should
+ // be fine to just put USER instead of UNINSTALL or DISABLED.
+ cancelJobsForPackageAndUidLocked(pkgName, uidRemoved,
+ JobParameters.STOP_REASON_USER,
+ JobParameters.DEBUG_REASON_UNINSTALL, "app uninstalled");
+ for (int c = 0; c < mControllers.size(); ++c) {
+ mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid);
}
- // By the time we get here, the process should have already
- // been stopped, so the app wouldn't get the stop reason,
- // so just put USER instead of UNINSTALL or DISABLED.
- synchronized (mLock) {
- cancelJobsForPackageAndUidLocked(pkgName, uidRemoved,
- JobParameters.STOP_REASON_USER, "app uninstalled");
- for (int c = 0; c < mControllers.size(); ++c) {
- mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid);
- }
- mDebuggableApps.remove(pkgName);
- }
+ mDebuggableApps.remove(pkgName);
}
} else if (Intent.ACTION_USER_ADDED.equals(action)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
@@ -849,7 +848,8 @@
}
synchronized (mLock) {
cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
- JobParameters.STOP_REASON_USER, "app force stopped");
+ JobParameters.STOP_REASON_USER, JobParameters.REASON_CANCELED,
+ "app force stopped");
}
}
}
@@ -1038,7 +1038,7 @@
if (toCancel != null) {
// Implicitly replaces the existing job record with the new instance
cancelJobImplLocked(toCancel, jobStatus, JobParameters.STOP_REASON_CANCELLED_BY_APP,
- "job rescheduled by app");
+ JobParameters.REASON_CANCELED, "job rescheduled by app");
} else {
startTrackingJobLocked(jobStatus, null);
}
@@ -1115,10 +1115,11 @@
final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
for (int i = 0; i < jobsForUser.size(); i++) {
JobStatus toRemove = jobsForUser.get(i);
- // By the time we get here, the process should have already been stopped, so the
- // app wouldn't get the stop reason, so just put USER instead of UNINSTALL.
+ // There's no guarantee that the process has been stopped by the time we get here,
+ // but since this is a user-initiated action, it should be fine to just put USER
+ // instead of UNINSTALL or DISABLED.
cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER,
- "user removed");
+ JobParameters.DEBUG_REASON_UNINSTALL, "user removed");
}
}
@@ -1130,7 +1131,7 @@
}
private void cancelJobsForPackageAndUidLocked(String pkgName, int uid,
- @JobParameters.StopReason int reason, String debugReason) {
+ @JobParameters.StopReason int reason, int debugReasonCode, String debugReason) {
if ("android".equals(pkgName)) {
Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
return;
@@ -1139,7 +1140,7 @@
for (int i = jobsForUid.size() - 1; i >= 0; i--) {
final JobStatus job = jobsForUid.get(i);
if (job.getSourcePackageName().equals(pkgName)) {
- cancelJobImplLocked(job, null, reason, debugReason);
+ cancelJobImplLocked(job, null, reason, debugReasonCode, debugReason);
}
}
}
@@ -1152,7 +1153,7 @@
* @param uid Uid to check against for removal of a job.
*/
public boolean cancelJobsForUid(int uid, @JobParameters.StopReason int reason,
- String debugReason) {
+ int debugReasonCode, String debugReason) {
if (uid == Process.SYSTEM_UID) {
Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
return false;
@@ -1161,9 +1162,9 @@
boolean jobsCanceled = false;
synchronized (mLock) {
final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
- for (int i=0; i<jobsForUid.size(); i++) {
+ for (int i = 0; i < jobsForUid.size(); i++) {
JobStatus toRemove = jobsForUid.get(i);
- cancelJobImplLocked(toRemove, null, reason, debugReason);
+ cancelJobImplLocked(toRemove, null, reason, debugReasonCode, debugReason);
jobsCanceled = true;
}
}
@@ -1185,8 +1186,9 @@
toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
if (toCancel != null) {
cancelJobImplLocked(toCancel, null, reason,
+ JobParameters.REASON_CANCELED,
"cancel() called by app, callingUid=" + callingUid
- + " uid=" + uid + " jobId=" + jobId);
+ + " uid=" + uid + " jobId=" + jobId);
}
return (toCancel != null);
}
@@ -1199,7 +1201,7 @@
* currently scheduled jobs.
*/
private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob,
- @JobParameters.StopReason int reason, String debugReason) {
+ @JobParameters.StopReason int reason, int debugReasonCode, String debugReason) {
if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
cancelled.unprepareLocked();
stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
@@ -1208,8 +1210,7 @@
mJobPackageTracker.noteNonpending(cancelled);
}
// Cancel if running.
- stopJobOnServiceContextLocked(cancelled, reason, JobParameters.REASON_CANCELED,
- debugReason);
+ stopJobOnServiceContextLocked(cancelled, reason, debugReasonCode, debugReason);
// If this is a replacement, bring in the new version of the job
if (incomingJob != null) {
if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString());
@@ -1459,7 +1460,7 @@
Slog.v(TAG, " replacing " + oldJob + " with " + newJob);
}
cancelJobImplLocked(oldJob, newJob, JobParameters.STOP_REASON_SYSTEM_PROCESSING,
- "deferred rtc calculation");
+ JobParameters.REASON_CANCELED, "deferred rtc calculation");
}
}
};
@@ -1482,7 +1483,7 @@
// Register br for package removals and user removals.
final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
@@ -1786,15 +1787,26 @@
* @param needsReschedule Whether the implementing class should reschedule this job.
*/
@Override
- public void onJobCompletedLocked(JobStatus jobStatus, int stopReason, boolean needsReschedule) {
+ public void onJobCompletedLocked(JobStatus jobStatus, int debugStopReason,
+ boolean needsReschedule) {
if (DEBUG) {
- Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
+ Slog.d(TAG, "Completed " + jobStatus + ", reason=" + debugStopReason
+ + ", reschedule=" + needsReschedule);
}
mLastCompletedJobs[mLastCompletedJobIndex] = jobStatus;
mLastCompletedJobTimeElapsed[mLastCompletedJobIndex] = sElapsedRealtimeClock.millis();
mLastCompletedJobIndex = (mLastCompletedJobIndex + 1) % NUM_COMPLETED_JOB_HISTORY;
+ if (debugStopReason == JobParameters.DEBUG_REASON_UNINSTALL
+ || debugStopReason == JobParameters.DEBUG_REASON_DATA_CLEARED) {
+ // The job should have already been cleared from the rest of the JS tracking. No need
+ // to go through all that flow again.
+ jobStatus.unprepareLocked();
+ reportActiveLocked();
+ return;
+ }
+
// Intentionally not checking expedited job quota here. An app can't find out if it's run
// out of quota when it asks JS to reschedule an expedited job. Instead, the rescheduled
// EJ will just be demoted to a regular job if the app has no EJ quota left.
@@ -1805,8 +1817,8 @@
final JobStatus rescheduledJob = needsReschedule
? getRescheduleJobForFailureLocked(jobStatus) : null;
if (rescheduledJob != null
- && (stopReason == JobParameters.REASON_TIMEOUT
- || stopReason == JobParameters.REASON_PREEMPT)) {
+ && (debugStopReason == JobParameters.REASON_TIMEOUT
+ || debugStopReason == JobParameters.REASON_PREEMPT)) {
rescheduledJob.disallowRunInBatterySaverAndDoze();
}
@@ -1911,6 +1923,7 @@
break;
case MSG_STOP_JOB:
cancelJobImplLocked((JobStatus) message.obj, null, message.arg1,
+ JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
"app no longer allowed to run");
break;
@@ -1927,7 +1940,7 @@
if (disabled) {
cancelJobsForUid(uid,
JobParameters.STOP_REASON_BACKGROUND_RESTRICTION,
- "uid gone");
+ JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED, "uid gone");
}
synchronized (mLock) {
mDeviceIdleJobsController.setUidActiveLocked(uid, false);
@@ -1947,7 +1960,7 @@
if (disabled) {
cancelJobsForUid(uid,
JobParameters.STOP_REASON_BACKGROUND_RESTRICTION,
- "app uid idle");
+ JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED, "app uid idle");
}
synchronized (mLock) {
mDeviceIdleJobsController.setUidActiveLocked(uid, false);
@@ -2409,8 +2422,8 @@
@Override
public void cancelJobsForUid(int uid, @JobParameters.StopReason int reason,
- String debugReason) {
- JobSchedulerService.this.cancelJobsForUid(uid, reason, debugReason);
+ int debugReasonCode, String debugReason) {
+ JobSchedulerService.this.cancelJobsForUid(uid, reason, debugReasonCode, debugReason);
}
@Override
@@ -2749,6 +2762,7 @@
try {
JobSchedulerService.this.cancelJobsForUid(uid,
JobParameters.STOP_REASON_CANCELLED_BY_APP,
+ JobParameters.REASON_CANCELED,
"cancelAll() called by app, callingUid=" + uid);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2969,7 +2983,7 @@
if (!hasJobId) {
pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
if (!cancelJobsForUid(pkgUid, JobParameters.STOP_REASON_USER,
- "cancel shell command for package")) {
+ JobParameters.REASON_CANCELED, "cancel shell command for package")) {
pw.println("No matching jobs found.");
}
} else {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index f30b75c..081163b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -121,54 +121,81 @@
private final SparseArray<UidDefaultNetworkCallback> mCurrentDefaultNetworkCallbacks =
new SparseArray<>();
private final Comparator<UidStats> mUidStatsComparator = new Comparator<UidStats>() {
- private int prioritizeExistence(int v1, int v2) {
- if (v1 > 0 && v2 > 0) {
+ private int prioritizeExistenceOver(int threshold, int v1, int v2) {
+ // Check if they're both on the same side of the threshold.
+ if ((v1 > threshold && v2 > threshold) || (v1 <= threshold && v2 <= threshold)) {
return 0;
}
- return v2 - v1;
+ // They're on opposite sides of the threshold.
+ if (v1 > threshold) {
+ return -1;
+ }
+ return 1;
}
@Override
public int compare(UidStats us1, UidStats us2) {
- // TODO: build a better prioritization scheme
- // Some things to use:
- // * Proc state
- // * IMPORTANT_WHILE_IN_FOREGROUND bit
- final int runningPriority = prioritizeExistence(us1.numRunning, us2.numRunning);
+ // Prioritize a UID ahead of another based on:
+ // 1. Already running connectivity jobs (so we don't drop the listener)
+ // 2. Waiting connectivity jobs would be ready with connectivity
+ // 3. An existing network satisfies a waiting connectivity job's requirements
+ // 4. TOP proc state
+ // 5. Existence of treat-as-EJ EJs (not just requested EJs)
+ // 6. FGS proc state
+ // 7. EJ enqueue time
+ // 8. Any other important job priorities/proc states
+ // 9. Enqueue time
+ // TODO: maybe consider number of jobs
+ // TODO: consider IMPORTANT_WHILE_FOREGROUND bit
+ final int runningPriority = prioritizeExistenceOver(0, us1.numRunning, us2.numRunning);
if (runningPriority != 0) {
return runningPriority;
}
// Prioritize any UIDs that have jobs that would be ready ahead of UIDs that don't.
- final int readyWithConnPriority =
- prioritizeExistence(us1.numReadyWithConnectivity, us2.numReadyWithConnectivity);
+ final int readyWithConnPriority = prioritizeExistenceOver(0,
+ us1.numReadyWithConnectivity, us2.numReadyWithConnectivity);
if (readyWithConnPriority != 0) {
return readyWithConnPriority;
}
// They both have jobs that would be ready. Prioritize the UIDs whose requested
// network is available ahead of UIDs that don't have their requested network available.
- final int reqAvailPriority = prioritizeExistence(
+ final int reqAvailPriority = prioritizeExistenceOver(0,
us1.numRequestedNetworkAvailable, us2.numRequestedNetworkAvailable);
if (reqAvailPriority != 0) {
return reqAvailPriority;
}
- // They both have jobs with available networks. Prioritize based on:
- // 1. (eventually) proc state
- // 2. Existence of runnable EJs (not just requested)
- // 3. Enqueue time
- // TODO: maybe consider number of jobs
- final int ejPriority = prioritizeExistence(us1.numEJs, us2.numEJs);
+ // Prioritize the top app. If neither are top apps, then use a later prioritization
+ // check.
+ final int topPriority = prioritizeExistenceOver(JobInfo.PRIORITY_TOP_APP - 1,
+ us1.basePriority, us2.basePriority);
+ if (topPriority != 0) {
+ return topPriority;
+ }
+ // They're either both TOP or both not TOP. Prioritize the app that has runnable EJs
+ // pending.
+ final int ejPriority = prioritizeExistenceOver(0, us1.numEJs, us2.numEJs);
if (ejPriority != 0) {
return ejPriority;
}
- // They both have EJs. Order them by EJ enqueue time to help provide low EJ latency.
+ // They both have runnable EJs.
+ // Prioritize an FGS+ app. If neither are FGS+ apps, then use a later prioritization
+ // check.
+ final int fgsPriority = prioritizeExistenceOver(JobInfo.PRIORITY_FOREGROUND_SERVICE - 1,
+ us1.basePriority, us2.basePriority);
+ if (fgsPriority != 0) {
+ return fgsPriority;
+ }
+ // Order them by EJ enqueue time to help provide low EJ latency.
if (us1.earliestEJEnqueueTime < us2.earliestEJEnqueueTime) {
return -1;
} else if (us1.earliestEJEnqueueTime > us2.earliestEJEnqueueTime) {
return 1;
}
+ // Order by any latent important proc states.
if (us1.basePriority != us2.basePriority) {
return us2.basePriority - us1.basePriority;
}
+ // Order by enqueue time.
if (us1.earliestEnqueueTime < us2.earliestEnqueueTime) {
return -1;
}
@@ -244,7 +271,10 @@
getUidStats(jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true);
uidStats.numReadyWithConnectivity--;
if (jobStatus.madeActive != 0) {
- uidStats.numRunning--;
+ // numRunning would be 0 if the UidStats object didn't exist before this method
+ // was called. getUidStats() handles logging, so just make sure we don't save a
+ // negative value.
+ uidStats.numRunning = Math.max(0, uidStats.numRunning - 1);
}
maybeRevokeStandbyExceptionLocked(jobStatus);
postAdjustCallbacks();
@@ -477,7 +507,7 @@
UidStats uidStats = mUidStats.get(uid);
if (uidStats != null && uidStats.basePriority != newPriority) {
uidStats.basePriority = newPriority;
- maybeAdjustRegisteredCallbacksLocked();
+ postAdjustCallbacks();
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 91189e4..158a0b9 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -735,6 +735,7 @@
mTempAllowlistCache.delete(uid);
mTempAllowlistGraceCache.delete(uid);
mTopAppCache.delete(uid);
+ mTopAppTrackers.delete(uid);
mTopAppGraceCache.delete(uid);
}
diff --git a/apex/media/framework/api/system-current.txt b/apex/media/framework/api/system-current.txt
index 7df0b4b..ce68447 100644
--- a/apex/media/framework/api/system-current.txt
+++ b/apex/media/framework/api/system-current.txt
@@ -25,7 +25,7 @@
}
public static final class MediaTranscodeManager.TranscodingSession {
- method public void addClientUid(int);
+ method public boolean addClientUid(int);
method public void cancel();
method @NonNull public java.util.List<java.lang.Integer> getClientUids();
method public int getErrorCode();
diff --git a/apex/media/framework/java/android/media/MediaTranscodeManager.java b/apex/media/framework/java/android/media/MediaTranscodeManager.java
index a7de602..d7e9609 100644
--- a/apex/media/framework/java/android/media/MediaTranscodeManager.java
+++ b/apex/media/framework/java/android/media/MediaTranscodeManager.java
@@ -1361,8 +1361,6 @@
private @TranscodingSessionErrorCode int mErrorCode = ERROR_NONE;
@GuardedBy("mLock")
private boolean mHasRetried = false;
- @GuardedBy("mLock")
- private @NonNull List<Integer> mClientUidList = new ArrayList<>();
// The original request that associated with this session.
private final TranscodingRequest mRequest;
@@ -1381,7 +1379,6 @@
mListenerExecutor = executor;
mListener = listener;
mRequest = request;
- mClientUidList.add(request.getClientUid());
}
/**
@@ -1532,17 +1529,31 @@
* Only privilege caller with android.permission.WRITE_MEDIA_STORAGE could add the
* uid. Note that the permission check happens on the service side upon starting the
* transcoding. If the client does not have the permission, the transcoding will fail.
+ * @param uid the additional client uid to be added.
+ * @return true if successfully added, false otherwise.
*/
- public void addClientUid(int uid) {
+ public boolean addClientUid(int uid) {
if (uid < 0) {
throw new IllegalArgumentException("Invalid Uid");
}
- synchronized (mLock) {
- if (!mClientUidList.contains(uid)) {
- // see ag/14023202 for implementation
- mClientUidList.add(uid);
- }
+
+ // Get the client interface.
+ ITranscodingClient client = mManager.getTranscodingClient();
+ if (client == null) {
+ Log.e(TAG, "Service is dead...");
+ return false;
}
+
+ try {
+ if (!client.addClientUid(mSessionId, uid)) {
+ Log.e(TAG, "Failed to add client uid");
+ return false;
+ }
+ } catch (Exception ex) {
+ Log.e(TAG, "Failed to get client uids due to " + ex);
+ return false;
+ }
+ return true;
}
/**
@@ -1551,9 +1562,25 @@
*/
@NonNull
public List<Integer> getClientUids() {
- synchronized (mLock) {
- return mClientUidList;
+ List<Integer> uidList = new ArrayList<Integer>();
+
+ // Get the client interface.
+ ITranscodingClient client = mManager.getTranscodingClient();
+ if (client == null) {
+ Log.e(TAG, "Service is dead...");
+ return uidList;
}
+
+ try {
+ int[] clientUids = client.getClientUids(mSessionId);
+ for (int i : clientUids) {
+ uidList.add(i);
+ }
+ } catch (Exception ex) {
+ Log.e(TAG, "Failed to get client uids due to " + ex);
+ }
+
+ return uidList;
}
/**
diff --git a/api/dump_api_shas.sh b/api/dump_api_shas.sh
new file mode 100755
index 0000000..c023b31
--- /dev/null
+++ b/api/dump_api_shas.sh
@@ -0,0 +1,56 @@
+#!/bin/bash -e
+# This script dumps the git SHAs of all commits inside api tracking directories.
+# It can used by tools wanting to track API changes, and the primary original
+# purpose is to verify verify all API change SHAs have been tracked by the
+# server-side API-council tools.
+#
+# The only argument is used to specify a git commit range to filter by.
+#
+# Example invocation (API changes between O and P):
+# frameworks/base/api/dump_api_shas.sh origin/oreo-dev..origin/pie-dev
+
+set -o pipefail
+
+eecho() { echo $@ >&2 ; }
+
+if [[ $1 == *..* ]]; then
+ exclude=${1/..*}
+ include=${1/*..}
+else
+ eecho No range or invalid range specified, defaulting to all commits from HEAD.
+ exclude=
+ include=HEAD
+fi
+
+eecho -n building queryview...
+{ source build/envsetup.sh && lunch aosp_arm && m queryview; } >/dev/null 2>&1 \
+ || { eecho failed; exit 1; }
+eecho "done"
+
+# This finds the directories where the dependant java_sdk_libs are defined
+bpdirs=$(
+ bazel query --config=queryview --output=package \
+ 'kind(java_sdk_library, deps(//frameworks/base/api/..., 1))' 2>/dev/null
+ echo frameworks/base/core/api # Not a java_sdk_library.
+ echo frameworks/base/services/api # Not a java_sdk_library.
+)
+
+# Find relevant api subdirectories
+apidirs=$(
+ find $bpdirs -type f -name '*current.txt' -path '*/api/*' \
+ | xargs realpath --relative-to=$(pwd) | xargs dirname | sort | uniq
+)
+
+# Dump sorted SHAs of commits in these directories
+{ for d in $apidirs; do
+ ( cd $d
+ eecho inspecting $d
+ exclude_arg=$(test -n "$exclude" && {
+ git rev-parse -q --verify $exclude > /dev/null && echo "--not $exclude" \
+ || eecho "$d has no revision $exclude, including all commits"; } || true)
+ for f in $(find . -name '*current.txt'); do
+ git --no-pager log --pretty=format:%H --no-merges --follow $include $exclude_arg -- $f
+ echo # No trailing newline with --no-pager
+ done
+ )
+done; } | sort | uniq
diff --git a/config/hiddenapi-force-blocked.txt b/boot/hiddenapi/hiddenapi-force-blocked.txt
similarity index 100%
rename from config/hiddenapi-force-blocked.txt
rename to boot/hiddenapi/hiddenapi-force-blocked.txt
diff --git a/config/hiddenapi-max-target-o.txt b/boot/hiddenapi/hiddenapi-max-target-o.txt
similarity index 100%
rename from config/hiddenapi-max-target-o.txt
rename to boot/hiddenapi/hiddenapi-max-target-o.txt
diff --git a/config/hiddenapi-max-target-p.txt b/boot/hiddenapi/hiddenapi-max-target-p.txt
similarity index 100%
rename from config/hiddenapi-max-target-p.txt
rename to boot/hiddenapi/hiddenapi-max-target-p.txt
diff --git a/config/hiddenapi-max-target-q.txt b/boot/hiddenapi/hiddenapi-max-target-q.txt
similarity index 100%
rename from config/hiddenapi-max-target-q.txt
rename to boot/hiddenapi/hiddenapi-max-target-q.txt
diff --git a/config/hiddenapi-max-target-r-loprio.txt b/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt
similarity index 100%
rename from config/hiddenapi-max-target-r-loprio.txt
rename to boot/hiddenapi/hiddenapi-max-target-r-loprio.txt
diff --git a/config/hiddenapi-unsupported-packages.txt b/boot/hiddenapi/hiddenapi-unsupported-packages.txt
similarity index 100%
rename from config/hiddenapi-unsupported-packages.txt
rename to boot/hiddenapi/hiddenapi-unsupported-packages.txt
diff --git a/config/hiddenapi-unsupported.txt b/boot/hiddenapi/hiddenapi-unsupported.txt
similarity index 100%
rename from config/hiddenapi-unsupported.txt
rename to boot/hiddenapi/hiddenapi-unsupported.txt
diff --git a/core/api/current.txt b/core/api/current.txt
index 0871287..88080f0 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1182,7 +1182,7 @@
field public static final int reqNavigation = 16843306; // 0x101022a
field public static final int reqTouchScreen = 16843303; // 0x1010227
field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603
- field public static final int requestOptimizedExternalStorageAccess;
+ field public static final int requestRawExternalStorageAccess;
field public static final int requireDeviceScreenOn;
field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
field public static final int required = 16843406; // 0x101028e
@@ -8552,8 +8552,8 @@
ctor public AppWidgetProviderInfo(android.os.Parcel);
method public android.appwidget.AppWidgetProviderInfo clone();
method public int describeContents();
+ method @NonNull public android.content.pm.ActivityInfo getActivityInfo();
method public final android.os.UserHandle getProfile();
- method @NonNull public android.content.pm.ActivityInfo getProviderInfo();
method @Nullable public final CharSequence loadDescription(@NonNull android.content.Context);
method public final android.graphics.drawable.Drawable loadIcon(@NonNull android.content.Context, int);
method public final String loadLabel(android.content.pm.PackageManager);
@@ -12441,7 +12441,7 @@
method public void setAppIcon(@Nullable android.graphics.Bitmap);
method public void setAppLabel(@Nullable CharSequence);
method public void setAppPackageName(@Nullable String);
- method public void setAutoRevokePermissionsMode(boolean);
+ method @Deprecated public void setAutoRevokePermissionsMode(boolean);
method public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams);
method public void setInstallLocation(int);
method public void setInstallReason(int);
@@ -20332,6 +20332,7 @@
field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe
field public static final int ENCODING_DTS = 7; // 0x7
field public static final int ENCODING_DTS_HD = 8; // 0x8
+ field public static final int ENCODING_DTS_UHD = 27; // 0x1b
field public static final int ENCODING_E_AC3 = 6; // 0x6
field public static final int ENCODING_E_AC3_JOC = 18; // 0x12
field public static final int ENCODING_IEC61937 = 13; // 0xd
@@ -52857,7 +52858,7 @@
method @NonNull public android.view.translation.TranslationResponse.Builder setFinalResponse(boolean);
method @NonNull public android.view.translation.TranslationResponse.Builder setTranslationResponseValue(int, @NonNull android.view.translation.TranslationResponseValue);
method @NonNull public android.view.translation.TranslationResponse.Builder setTranslationResponseValues(@NonNull android.util.SparseArray<android.view.translation.TranslationResponseValue>);
- method @NonNull public android.view.translation.TranslationResponse.Builder setTranslationStatus(int);
+ method @Deprecated @NonNull public android.view.translation.TranslationResponse.Builder setTranslationStatus(int);
method @NonNull public android.view.translation.TranslationResponse.Builder setViewTranslationResponse(int, @NonNull android.view.translation.ViewTranslationResponse);
method @NonNull public android.view.translation.TranslationResponse.Builder setViewTranslationResponses(@NonNull android.util.SparseArray<android.view.translation.ViewTranslationResponse>);
}
@@ -55492,6 +55493,7 @@
public class RemoteViews implements android.view.LayoutInflater.Filter android.os.Parcelable {
ctor public RemoteViews(String, int);
+ ctor public RemoteViews(@NonNull String, @LayoutRes int, @IdRes int);
ctor public RemoteViews(android.widget.RemoteViews, android.widget.RemoteViews);
ctor public RemoteViews(@NonNull java.util.Map<android.util.SizeF,android.widget.RemoteViews>);
ctor public RemoteViews(android.widget.RemoteViews);
@@ -55565,7 +55567,6 @@
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 setViewId(@IdRes int);
method public void setViewLayoutHeight(@IdRes int, float, int);
method public void setViewLayoutHeightDimen(@IdRes int, @DimenRes int);
method public void setViewLayoutMargin(@IdRes int, int, float, int);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 869d790..5379468 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -65,10 +65,6 @@
field public static final String TEST_NETWORK_SERVICE = "test_network";
}
- public class Intent implements java.lang.Cloneable android.os.Parcelable {
- field public static final String ACTION_CLEAR_DNS_CACHE = "android.intent.action.CLEAR_DNS_CACHE";
- }
-
}
package android.content.pm {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 40f26e8..0772478 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -69,6 +69,7 @@
field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
field public static final String BROADCAST_CLOSE_SYSTEM_DIALOGS = "android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS";
field @Deprecated public static final String BROADCAST_NETWORK_PRIVILEGED = "android.permission.BROADCAST_NETWORK_PRIVILEGED";
+ field public static final String BYPASS_ROLE_QUALIFICATION = "android.permission.BYPASS_ROLE_QUALIFICATION";
field public static final String CAMERA_DISABLE_TRANSMIT_LED = "android.permission.CAMERA_DISABLE_TRANSMIT_LED";
field public static final String CAMERA_OPEN_CLOSE_LISTENER = "android.permission.CAMERA_OPEN_CLOSE_LISTENER";
field public static final String CAPTURE_AUDIO_HOTWORD = "android.permission.CAPTURE_AUDIO_HOTWORD";
@@ -2508,7 +2509,7 @@
package android.content.pm {
public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
- method @Nullable public Boolean hasRequestOptimizedExternalStorageAccess();
+ method @Nullable public Boolean hasRequestRawExternalStorageAccess();
method public boolean isEncryptionAware();
method public boolean isInstantApp();
method public boolean isOem();
@@ -9439,7 +9440,7 @@
}
public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
- method @Nullable public int[] getAttestationIds();
+ method @NonNull public int[] getAttestationIds();
method public int getNamespace();
}
@@ -14171,7 +14172,7 @@
public final class DistanceMeasurement implements android.os.Parcelable {
method public int describeContents();
method @FloatRange(from=0.0, to=1.0) public double getConfidenceLevel();
- method public double getErrorMeters();
+ method @FloatRange(from=0.0) public double getErrorMeters();
method public double getMeters();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.uwb.DistanceMeasurement> CREATOR;
@@ -14180,8 +14181,8 @@
public static final class DistanceMeasurement.Builder {
ctor public DistanceMeasurement.Builder();
method @NonNull public android.uwb.DistanceMeasurement build();
- method @NonNull public android.uwb.DistanceMeasurement.Builder setConfidenceLevel(double);
- method @NonNull public android.uwb.DistanceMeasurement.Builder setErrorMeters(double);
+ method @NonNull public android.uwb.DistanceMeasurement.Builder setConfidenceLevel(@FloatRange(from=0.0, to=1.0) double);
+ method @NonNull public android.uwb.DistanceMeasurement.Builder setErrorMeters(@FloatRange(from=0.0) double);
method @NonNull public android.uwb.DistanceMeasurement.Builder setMeters(double);
}
@@ -14240,7 +14241,7 @@
method public void onStartFailed(int, @NonNull android.os.PersistableBundle);
method public void onStarted(@NonNull android.os.PersistableBundle);
method public void onStopFailed(int, @NonNull android.os.PersistableBundle);
- method public void onStopped();
+ method public void onStopped(int, @NonNull android.os.PersistableBundle);
field public static final int REASON_BAD_PARAMETERS = 3; // 0x3
field public static final int REASON_GENERIC_ERROR = 4; // 0x4
field public static final int REASON_LOCAL_REQUEST = 1; // 0x1
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index a2634da..906524e 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -233,6 +233,8 @@
field public static final String KEY_BG_STATE_SETTLE_TIME = "bg_state_settle_time";
field public static final String KEY_FG_SERVICE_STATE_SETTLE_TIME = "fg_service_state_settle_time";
field public static final String KEY_TOP_STATE_SETTLE_TIME = "top_state_settle_time";
+ field public static final String OPSTR_ACTIVITY_RECOGNITION = "android:activity_recognition";
+ field public static final String OPSTR_ACTIVITY_RECOGNITION_SOURCE = "android:activity_recognition_source";
field public static final String OPSTR_MANAGE_ONGOING_CALLS = "android:manage_ongoing_calls";
field public static final String OPSTR_PHONE_CALL_CAMERA = "android:phone_call_camera";
field public static final String OPSTR_PHONE_CALL_MICROPHONE = "android:phone_call_microphone";
@@ -281,6 +283,11 @@
method public abstract void onHomeVisibilityChanged(boolean);
}
+ public class KeyguardManager {
+ method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"}) public boolean checkLock(int, @Nullable byte[]);
+ method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"}) public boolean setLock(int, @Nullable byte[], int, @Nullable byte[]);
+ }
+
public class Notification implements android.os.Parcelable {
method public boolean shouldShowForegroundImmediately();
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 36c66e5..db42803 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -794,6 +794,9 @@
/** @hide Flag for registerUidObserver: report uid cached state has changed. */
public static final int UID_OBSERVER_CACHED = 1<<4;
+ /** @hide Flag for registerUidObserver: report uid capability has changed. */
+ public static final int UID_OBSERVER_CAPABILITY = 1<<5;
+
/** @hide Mode for {@link IActivityManager#isAppStartModeDisabled}: normal free-to-run operation. */
public static final int APP_START_MODE_NORMAL = 0;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e50432e..35890c8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4335,17 +4335,10 @@
private String getBackupAgentName(CreateBackupAgentData data) {
String agentName = data.appInfo.backupAgentName;
- if (!UserHandle.isCore(data.appInfo.uid)
- && data.operationType == BackupManager.OperationType.MIGRATION) {
- // If this is a migration, use the default backup agent regardless of the app's
- // preferences.
+ // full backup operation but no app-supplied agent? use the default implementation
+ if (agentName == null && (data.backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL
+ || data.backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL)) {
agentName = DEFAULT_FULL_BACKUP_AGENT;
- } else {
- // full backup operation but no app-supplied agent? use the default implementation
- if (agentName == null && (data.backupMode == ApplicationThreadConstants.BACKUP_MODE_FULL
- || data.backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL)) {
- agentName = DEFAULT_FULL_BACKUP_AGENT;
- }
}
return agentName;
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 436007ca..8e2626a 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1225,9 +1225,19 @@
/** @hide */
public static final int OP_UWB_RANGING = AppProtoEnums.APP_OP_UWB_RANGING;
+ /**
+ * Activity recognition being accessed by an activity recognition source, which
+ * is a component that already has access since it is the one that detects
+ * activity recognition.
+ *
+ * @hide
+ */
+ public static final int OP_ACTIVITY_RECOGNITION_SOURCE =
+ AppProtoEnums.APP_OP_ACTIVITY_RECOGNITION_SOURCE;
+
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static final int _NUM_OP = 113;
+ public static final int _NUM_OP = 114;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1478,6 +1488,7 @@
public static final String OPSTR_USE_BIOMETRIC = "android:use_biometric";
/** @hide Recognize physical activity. */
+ @TestApi
public static final String OPSTR_ACTIVITY_RECOGNITION = "android:activity_recognition";
/** @hide Financial app read sms. */
@@ -1643,6 +1654,17 @@
/** @hide */
public static final String OPSTR_UWB_RANGING = "android:uwb_ranging";
+ /**
+ * Activity recognition being accessed by an activity recognition source, which
+ * is a component that already has access since it is the one that detects
+ * activity recognition.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String OPSTR_ACTIVITY_RECOGNITION_SOURCE =
+ "android:activity_recognition_source";
+
/** {@link #sAppOpsToNote} not initialized yet for this op */
private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
/** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -1853,6 +1875,7 @@
OP_MANAGE_MEDIA, // MANAGE_MEDIA
OP_BLUETOOTH_CONNECT, // OP_BLUETOOTH_CONNECT
OP_UWB_RANGING, // OP_UWB_RANGING
+ OP_ACTIVITY_RECOGNITION_SOURCE // OP_ACTIVITY_RECOGNITION_SOURCE
};
/**
@@ -1972,6 +1995,7 @@
OPSTR_MANAGE_MEDIA,
OPSTR_BLUETOOTH_CONNECT,
OPSTR_UWB_RANGING,
+ OPSTR_ACTIVITY_RECOGNITION_SOURCE
};
/**
@@ -2091,7 +2115,8 @@
"COARSE_LOCATION_SOURCE",
"MANAGE_MEDIA",
"BLUETOOTH_CONNECT",
- "UWB_RANGING"
+ "UWB_RANGING",
+ "ACTIVITY_RECOGNITION_SOURCE"
};
/**
@@ -2213,6 +2238,7 @@
Manifest.permission.MANAGE_MEDIA,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.UWB_RANGING,
+ null, // no permission for OP_ACTIVITY_RECOGNITION_SOURCE,
};
/**
@@ -2334,6 +2360,7 @@
null, // MANAGE_MEDIA
null, // BLUETOOTH_CONNECT
null, // UWB_RANGING
+ null, // ACTIVITY_RECOGNITION_SOURCE
};
/**
@@ -2454,6 +2481,7 @@
null, // MANAGE_MEDIA
null, // BLUETOOTH_CONNECT
null, // UWB_RANGING
+ null // ACTIVITY_RECOGNITION_SOURCE
};
/**
@@ -2573,6 +2601,7 @@
AppOpsManager.MODE_DEFAULT, // MANAGE_MEDIA
AppOpsManager.MODE_ALLOWED, // BLUETOOTH_CONNECT
AppOpsManager.MODE_ALLOWED, // UWB_RANGING
+ AppOpsManager.MODE_ALLOWED, // ACTIVITY_RECOGNITION_SOURCE
};
/**
@@ -2696,6 +2725,7 @@
false, // MANAGE_MEDIA
false, // BLUETOOTH_CONNECT
false, // UWB_RANGING
+ false, // ACTIVITY_RECOGNITION_SOURCE
};
/**
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 4326c2d..dc71a32 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -24,6 +24,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.PasswordMetrics;
@@ -51,6 +52,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
import com.android.internal.widget.LockscreenCredential;
+import com.android.internal.widget.VerifyCredentialResponse;
import java.nio.charset.Charset;
import java.util.Arrays;
@@ -696,14 +698,15 @@
}
private boolean checkInitialLockMethodUsage() {
- if (mContext.checkCallingOrSelfPermission(Manifest.permission.SET_INITIAL_LOCK)
- != PackageManager.PERMISSION_GRANTED) {
+ if (!hasPermission(Manifest.permission.SET_INITIAL_LOCK)) {
throw new SecurityException("Requires SET_INITIAL_LOCK permission.");
}
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
- return false;
- }
- return true;
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ }
+
+ private boolean hasPermission(String permission) {
+ return PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
+ permission);
}
/**
@@ -792,38 +795,14 @@
Log.e(TAG, "Password is not valid, rejecting call to setLock");
return false;
}
- boolean success = false;
+ boolean success;
try {
- switch (lockType) {
- case PASSWORD:
- CharSequence passwordStr = new String(password, Charset.forName("UTF-8"));
- lockPatternUtils.setLockCredential(
- LockscreenCredential.createPassword(passwordStr),
- /* savedPassword= */ LockscreenCredential.createNone(),
- userId);
- success = true;
- break;
- case PIN:
- CharSequence pinStr = new String(password);
- lockPatternUtils.setLockCredential(
- LockscreenCredential.createPin(pinStr),
- /* savedPassword= */ LockscreenCredential.createNone(),
- userId);
- success = true;
- break;
- case PATTERN:
- List<LockPatternView.Cell> pattern =
- LockPatternUtils.byteArrayToPattern(password);
- lockPatternUtils.setLockCredential(
- LockscreenCredential.createPattern(pattern),
- /* savedPassword= */ LockscreenCredential.createNone(),
- userId);
- pattern.clear();
- success = true;
- break;
- default:
- Log.e(TAG, "Unknown lock type, returning a failure");
- }
+ LockscreenCredential credential = createLockscreenCredential(
+ lockType, password);
+ success = lockPatternUtils.setLockCredential(
+ credential,
+ /* savedPassword= */ LockscreenCredential.createNone(),
+ userId);
} catch (Exception e) {
Log.e(TAG, "Save lock exception", e);
success = false;
@@ -832,4 +811,81 @@
}
return success;
}
+
+ /**
+ * Set the lockscreen password to {@code newPassword} after validating the current password
+ * against {@code currentPassword}.
+ * <p>If no password is currently set, {@code currentPassword} should be set to {@code null}.
+ * <p>To clear the current password, {@code newPassword} should be set to {@code null}.
+ *
+ * @return {@code true} if password successfully set.
+ *
+ * @throws IllegalArgumentException if {@code newLockType} or {@code currentLockType}
+ * is invalid.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(anyOf = {
+ Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS,
+ Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE
+ })
+ public boolean setLock(@LockTypes int newLockType, @Nullable byte[] newPassword,
+ @LockTypes int currentLockType, @Nullable byte[] currentPassword) {
+ final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
+ final int userId = mContext.getUserId();
+ LockscreenCredential currentCredential = createLockscreenCredential(
+ currentLockType, currentPassword);
+ LockscreenCredential newCredential = createLockscreenCredential(
+ newLockType, newPassword);
+ return lockPatternUtils.setLockCredential(newCredential, currentCredential, userId);
+ }
+
+ /**
+ * Verifies the current lock credentials against {@code password}.
+ * <p>To check if no password is set, {@code password} should be set to {@code null}.
+ *
+ * @return {@code true} if credentials match
+ *
+ * @throws IllegalArgumentException if {@code lockType} is invalid.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(anyOf = {
+ Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS,
+ Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE
+ })
+ public boolean checkLock(@LockTypes int lockType, @Nullable byte[] password) {
+ final LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
+ final LockscreenCredential credential = createLockscreenCredential(
+ lockType, password);
+ final VerifyCredentialResponse response = lockPatternUtils.verifyCredential(
+ credential, mContext.getUserId(), /* flags= */ 0);
+ if (response == null) {
+ return false;
+ }
+ return response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK;
+ }
+
+ private LockscreenCredential createLockscreenCredential(
+ @LockTypes int lockType, @Nullable byte[] password) {
+ if (password == null) {
+ return LockscreenCredential.createNone();
+ }
+ switch (lockType) {
+ case PASSWORD:
+ CharSequence passwordStr = new String(password, Charset.forName("UTF-8"));
+ return LockscreenCredential.createPassword(passwordStr);
+ case PIN:
+ CharSequence pinStr = new String(password);
+ return LockscreenCredential.createPin(pinStr);
+ case PATTERN:
+ List<LockPatternView.Cell> pattern =
+ LockPatternUtils.byteArrayToPattern(password);
+ return LockscreenCredential.createPattern(pattern);
+ default:
+ throw new IllegalArgumentException("Unknown lock type " + lockType);
+ }
+ }
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index f0d580f..da03a3d 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -419,6 +419,14 @@
@Retention(RetentionPolicy.SOURCE)
public @interface Importance {}
+ /** @hide */
+ @IntDef(prefix = { "BUBBLE_PREFERENCE_" }, value = {
+ BUBBLE_PREFERENCE_NONE, BUBBLE_PREFERENCE_SELECTED,
+ BUBBLE_PREFERENCE_ALL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BubblePreference {}
+
/**
* Activity Action: Launch an Automatic Zen Rule configuration screen
* <p>
@@ -1379,7 +1387,7 @@
* @see Notification#getBubbleMetadata()
* @return the users' bubble preference for the app.
*/
- public int getBubblePreference() {
+ public @BubblePreference int getBubblePreference() {
INotificationManager service = getService();
try {
return service.getBubblePreferenceForPackage(mContext.getPackageName(),
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 94a4fde0..0841910 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -544,11 +544,6 @@
}
private Set<String> getExtraExcludeDirsIfAny(Context context) throws IOException {
- if (isDeviceToDeviceMigration()) {
- return Collections.emptySet();
- }
-
- // If this is not a migration, also exclude no-backup and cache dirs.
Set<String> excludedDirs = new HashSet<>();
excludedDirs.add(context.getCacheDir().getCanonicalPath());
excludedDirs.add(context.getCodeCacheDir().getCanonicalPath());
@@ -556,10 +551,6 @@
return Collections.unmodifiableSet(excludedDirs);
}
- private boolean isDeviceToDeviceMigration() {
- return mOperationType == OperationType.MIGRATION;
- }
-
/** @hide */
@VisibleForTesting
public IncludeExcludeRules getIncludeExcludeRules(FullBackup.BackupScheme backupScheme)
@@ -905,11 +896,6 @@
}
private boolean isFileEligibleForRestore(File destination) throws IOException {
- if (isDeviceToDeviceMigration()) {
- // Everything is eligible for device-to-device migration.
- return true;
- }
-
FullBackup.BackupScheme bs = FullBackup.getBackupScheme(this, mOperationType);
if (!bs.isFullRestoreEnabled()) {
if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
diff --git a/core/java/android/app/time/LocationTimeZoneManager.java b/core/java/android/app/time/LocationTimeZoneManager.java
index 066aada..f506f12 100644
--- a/core/java/android/app/time/LocationTimeZoneManager.java
+++ b/core/java/android/app/time/LocationTimeZoneManager.java
@@ -50,31 +50,6 @@
public static final String SHELL_COMMAND_STOP = "stop";
/**
- * A shell command that can put providers into different modes. Takes effect next time the
- * service is started.
- */
- public static final String SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE =
- "set_provider_mode_override";
-
- /**
- * The default provider mode.
- * For use with {@link #SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE}.
- */
- public static final String PROVIDER_MODE_OVERRIDE_NONE = "none";
-
- /**
- * The "simulated" provider mode.
- * For use with {@link #SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE}.
- */
- public static final String PROVIDER_MODE_OVERRIDE_SIMULATED = "simulated";
-
- /**
- * The "disabled" provider mode (equivalent to there being no provider configured).
- * For use with {@link #SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE}.
- */
- public static final String PROVIDER_MODE_OVERRIDE_DISABLED = "disabled";
-
- /**
* A shell command that tells the service to record state information during tests. The next
* argument value is "true" or "false".
*/
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 1cbb2fb..3db1885 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -479,7 +479,7 @@
* Returns the broadcast receiver that is providing this widget.
*/
@NonNull
- public ActivityInfo getProviderInfo() {
+ public ActivityInfo getActivityInfo() {
return providerInfo;
}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 2ce7156..0116db0 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -375,6 +375,14 @@
* Calling app must check for feature presence of
* {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} before calling this API.
*
+ * For Bluetooth LE devices this is based on scanning for device with the given address.
+ * For Bluetooth classic devices this is triggered when the device connects/disconnects.
+ * WiFi devices are not supported.
+ *
+ * If a Bluetooth LE device wants to use a rotating mac address, it is recommended to use
+ * Resolvable Private Address, and ensure the device is bonded to the phone so that android OS
+ * is able to resolve the address.
+ *
* @param deviceAddress a previously-associated companion device's address
*
* @throws DeviceNotAssociatedException if the given device was not previously associated
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index cadbd60..11adfa3 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -51,6 +51,25 @@
*/
@SystemService(Context.CLIPBOARD_SERVICE)
public class ClipboardManager extends android.text.ClipboardManager {
+
+ /**
+ * DeviceConfig property, within the clipboard namespace, that determines whether notifications
+ * are shown when an app accesses clipboard. This may be overridden by a user-controlled
+ * setting.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_SHOW_ACCESS_NOTIFICATIONS =
+ "show_access_notifications";
+
+ /**
+ * Default value for the DeviceConfig property that determines whether notifications are shown
+ * when an app accesses clipboard.
+ *
+ * @hide
+ */
+ public static final boolean DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS = true;
+
private final Context mContext;
private final Handler mHandler;
private final IClipboard mService;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 2c77372..183e73c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2401,14 +2401,6 @@
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_TIMEZONE_CHANGED = "android.intent.action.TIMEZONE_CHANGED";
/**
- * Clear DNS Cache Action: This is broadcast when networks have changed and old
- * DNS entries should be tossed.
- * @hide
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- public static final String ACTION_CLEAR_DNS_CACHE = "android.intent.action.CLEAR_DNS_CACHE";
- /**
* Alarm Changed Action: This is broadcast when the AlarmClock
* application's alarm is set or unset. It is used by the
* AlarmClock application and the StatusBar service.
@@ -6569,6 +6561,10 @@
* any affinities needed to have that task in the proper state (either
* moving activities to or from it), or simply resetting that task to
* its initial state if needed.
+ *
+ * @see android.R.attr#allowTaskReparenting
+ * @see android.R.attr#clearTaskOnLaunch
+ * @see android.R.attr#finishOnTaskLaunch
*/
public static final int FLAG_ACTIVITY_RESET_TASK_IF_NEEDED = 0x00200000;
/**
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 6badf0e0..6ad204e 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1436,11 +1436,11 @@
private @NativeHeapZeroInitialized int nativeHeapZeroInitialized = ZEROINIT_DEFAULT;
/**
- * If {@code true} this app requests optimized external storage access.
+ * If {@code true} this app requests raw external storage access.
* The request may not be honored due to policy or other reasons.
*/
@Nullable
- private Boolean requestOptimizedExternalStorageAccess;
+ private Boolean requestRawExternalStorageAccess;
/**
* Represents the default policy. The actual policy used will depend on other properties of
@@ -1598,9 +1598,9 @@
if (nativeHeapZeroInitialized != ZEROINIT_DEFAULT) {
pw.println(prefix + "nativeHeapZeroInitialized=" + nativeHeapZeroInitialized);
}
- if (requestOptimizedExternalStorageAccess != null) {
- pw.println(prefix + "requestOptimizedExternalStorageAccess="
- + requestOptimizedExternalStorageAccess);
+ if (requestRawExternalStorageAccess != null) {
+ pw.println(prefix + "requestRawExternalStorageAccess="
+ + requestRawExternalStorageAccess);
}
}
super.dumpBack(pw, prefix);
@@ -1829,7 +1829,7 @@
gwpAsanMode = orig.gwpAsanMode;
memtagMode = orig.memtagMode;
nativeHeapZeroInitialized = orig.nativeHeapZeroInitialized;
- requestOptimizedExternalStorageAccess = orig.requestOptimizedExternalStorageAccess;
+ requestRawExternalStorageAccess = orig.requestRawExternalStorageAccess;
}
public String toString() {
@@ -1918,7 +1918,7 @@
dest.writeInt(gwpAsanMode);
dest.writeInt(memtagMode);
dest.writeInt(nativeHeapZeroInitialized);
- sForBoolean.parcel(requestOptimizedExternalStorageAccess, dest, parcelableFlags);
+ sForBoolean.parcel(requestRawExternalStorageAccess, dest, parcelableFlags);
}
public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
@@ -2004,7 +2004,7 @@
gwpAsanMode = source.readInt();
memtagMode = source.readInt();
nativeHeapZeroInitialized = source.readInt();
- requestOptimizedExternalStorageAccess = sForBoolean.unparcel(source);
+ requestRawExternalStorageAccess = sForBoolean.unparcel(source);
}
/**
@@ -2121,10 +2121,10 @@
/**
* @return
* <ul>
- * <li>{@code true} if this app requested optimized external storage access
- * <li>{@code false} if this app requests to disable optimized external storage access.
+ * <li>{@code true} if this app requested raw external storage access
+ * <li>{@code false} if this app requests to disable raw external storage access.
* <li>{@code null} if the app didn't specify
- * {@link android.R.styleable#AndroidManifestApplication_requestOptimizedExternalStorageAccess}
+ * {@link android.R.styleable#AndroidManifestApplication_requestRawExternalStorageAccess}
* in its manifest file.
* </ul>
*
@@ -2132,8 +2132,8 @@
*/
@SystemApi
@Nullable
- public Boolean hasRequestOptimizedExternalStorageAccess() {
- return requestOptimizedExternalStorageAccess;
+ public Boolean hasRequestRawExternalStorageAccess() {
+ return requestRawExternalStorageAccess;
}
/**
@@ -2421,8 +2421,8 @@
nativeHeapZeroInitialized = value;
}
/** {@hide} */
- public void setRequestOptimizedExternalStorageAccess(@Nullable Boolean value) {
- requestOptimizedExternalStorageAccess = value;
+ public void setRequestRawExternalStorageAccess(@Nullable Boolean value) {
+ requestRawExternalStorageAccess = value;
}
/** {@hide} */
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 3be7f74..0be77e0 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1808,7 +1808,10 @@
* If user explicitly enabled or disabled it via settings, this call is ignored.
*
* @param shouldAutoRevoke whether permissions should be auto-revoked.
+ *
+ * @deprecated No longer used
*/
+ @Deprecated
public void setAutoRevokePermissionsMode(boolean shouldAutoRevoke) {
autoRevokePermissionsMode = shouldAutoRevoke ? MODE_ALLOWED : MODE_IGNORED;
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 1c65e00..8dcba7f 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -259,8 +259,8 @@
ParsingPackage setNativeHeapZeroInitialized(
@ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized);
- ParsingPackage setRequestOptimizedExternalStorageAccess(
- @Nullable Boolean requestOptimizedExternalStorageAccess);
+ ParsingPackage setRequestRawExternalStorageAccess(
+ @Nullable Boolean requestRawExternalStorageAccess);
ParsingPackage setCrossProfile(boolean crossProfile);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 97e1b54..ea7135e 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -400,7 +400,7 @@
@Nullable
@DataClass.ParcelWith(ForBoolean.class)
- private Boolean requestOptimizedExternalStorageAccess;
+ private Boolean requestRawExternalStorageAccess;
// TODO(chiuwinson): Non-null
@Nullable
@@ -1086,7 +1086,7 @@
appInfo.setGwpAsanMode(gwpAsanMode);
appInfo.setMemtagMode(memtagMode);
appInfo.setNativeHeapZeroInitialized(nativeHeapZeroInitialized);
- appInfo.setRequestOptimizedExternalStorageAccess(requestOptimizedExternalStorageAccess);
+ appInfo.setRequestRawExternalStorageAccess(requestRawExternalStorageAccess);
appInfo.setBaseCodePath(mBaseApkPath);
appInfo.setBaseResourcePath(mBaseApkPath);
appInfo.setCodePath(mPath);
@@ -1223,7 +1223,7 @@
dest.writeMap(this.mProperties);
dest.writeInt(this.memtagMode);
dest.writeInt(this.nativeHeapZeroInitialized);
- sForBoolean.parcel(this.requestOptimizedExternalStorageAccess, dest, flags);
+ sForBoolean.parcel(this.requestRawExternalStorageAccess, dest, flags);
}
public ParsingPackageImpl(Parcel in) {
@@ -1348,7 +1348,7 @@
this.mProperties = in.readHashMap(boot);
this.memtagMode = in.readInt();
this.nativeHeapZeroInitialized = in.readInt();
- this.requestOptimizedExternalStorageAccess = sForBoolean.unparcel(in);
+ this.requestRawExternalStorageAccess = sForBoolean.unparcel(in);
assignDerivedFields();
}
@@ -2131,8 +2131,8 @@
@Nullable
@Override
- public Boolean hasRequestOptimizedExternalStorageAccess() {
- return requestOptimizedExternalStorageAccess;
+ public Boolean hasRequestRawExternalStorageAccess() {
+ return requestRawExternalStorageAccess;
}
@Override
@@ -2586,8 +2586,8 @@
}
@Override
- public ParsingPackageImpl setRequestOptimizedExternalStorageAccess(@Nullable Boolean value) {
- requestOptimizedExternalStorageAccess = value;
+ public ParsingPackageImpl setRequestRawExternalStorageAccess(@Nullable Boolean value) {
+ requestRawExternalStorageAccess = value;
return this;
}
@Override
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index cfd828e..4d4cc1a 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -905,7 +905,7 @@
int getNativeHeapZeroInitialized();
@Nullable
- Boolean hasRequestOptimizedExternalStorageAccess();
+ Boolean hasRequestRawExternalStorageAccess();
// TODO(b/135203078): Hide and enforce going through PackageInfoUtils
ApplicationInfo toAppInfoWithoutState();
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 4e7bd70..a1ffc0c 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -2019,9 +2019,9 @@
v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
}
if (sa.hasValue(
- R.styleable.AndroidManifestApplication_requestOptimizedExternalStorageAccess)) {
- pkg.setRequestOptimizedExternalStorageAccess(sa.getBoolean(R.styleable
- .AndroidManifestApplication_requestOptimizedExternalStorageAccess,
+ R.styleable.AndroidManifestApplication_requestRawExternalStorageAccess)) {
+ pkg.setRequestRawExternalStorageAccess(sa.getBoolean(R.styleable
+ .AndroidManifestApplication_requestRawExternalStorageAccess,
false));
}
} finally {
diff --git a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
index 9290497..bfbcfd8 100644
--- a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
+++ b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
@@ -275,7 +275,7 @@
* Returns the index of collection
*/
public @IntRange(from = 0) int getIndex() {
- return 0;
+ return mIndex;
}
}
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index d7bb226..ba6fc6e 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -53,6 +53,11 @@
in PerUidReadTimeouts[] perUidReadTimeouts);
/**
+ * PM/system is done with this storage, ok to increase timeouts.
+ */
+ void onInstallationComplete(int storageId);
+
+ /**
* Bind-mounts a path under a storage to a full path. Can be permanent or temporary.
*/
const int BIND_TEMPORARY = 0;
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 2a42b98..6e25968 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -205,16 +205,26 @@
/**
* Resets the states and unbinds storage instances for an installation session.
*/
- public void cleanUp() {
- if (mDefaultStorage == null) {
- return;
+ public void cleanUpAndMarkComplete() {
+ IncrementalStorage defaultStorage = cleanUp();
+ if (defaultStorage != null) {
+ defaultStorage.onInstallationComplete();
+ }
+ }
+
+ private IncrementalStorage cleanUp() {
+ IncrementalStorage defaultStorage = mDefaultStorage;
+ mInheritedStorage = null;
+ mDefaultStorage = null;
+ if (defaultStorage == null) {
+ return null;
}
try {
mIncrementalManager.unregisterLoadingProgressCallbacks(mStageDir.getAbsolutePath());
- mDefaultStorage.unBind(mStageDir.getAbsolutePath());
+ defaultStorage.unBind(mStageDir.getAbsolutePath());
} catch (IOException ignored) {
}
- mDefaultStorage = null;
+ return defaultStorage;
}
}
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index 7cf0144..c19e29f 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -398,7 +398,7 @@
}
/**
- * Iinitializes and starts the DataLoader.
+ * Initializes and starts the DataLoader.
* This makes sure all install-time parameters are applied.
* Does not affect persistent DataLoader params.
* @return True if start request was successfully queued.
@@ -419,6 +419,18 @@
}
}
+ /**
+ * Marks the completion of installation.
+ */
+ public void onInstallationComplete() {
+ try {
+ mService.onInstallationComplete(mId);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+
private static final int UUID_BYTE_SIZE = 16;
/**
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 751e9aa..1a40f06 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -35,6 +35,7 @@
import android.app.PropertyInvalidatedCache;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
@@ -42,9 +43,7 @@
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.permission.SplitPermissionInfoParcelable;
-import android.location.LocationManager;
import android.media.AudioManager;
-import android.content.AttributionSource;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -52,8 +51,8 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemClock;
import android.os.UserHandle;
-import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
@@ -104,6 +103,20 @@
public static final long CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS = 146211400;
/**
+ * The time to wait in between refreshing the exempted indicator role packages
+ */
+ private static final long EXEMPTED_INDICATOR_ROLE_UPDATE_FREQUENCY_MS = 15000;
+
+ private static long sLastIndicatorUpdateTime = -1;
+
+ private static final int[] EXEMPTED_ROLES = {R.string.config_systemAmbientAudioIntelligence,
+ R.string.config_systemUiIntelligence, R.string.config_systemAudioIntelligence,
+ R.string.config_systemNotificationIntelligence, R.string.config_systemTextIntelligence,
+ R.string.config_systemVisualIntelligence};
+
+ private static final String[] INDICATOR_EXEMPTED_PACKAGES = new String[EXEMPTED_ROLES.length];
+
+ /**
* Note: Changing this won't do anything on its own - you should also change the filtering in
* {@link #shouldTraceGrant}.
*
@@ -873,21 +886,31 @@
}
/**
- * Check if this package/op combination is exempted from indicators
- * @return
+ * Determine if a package should be shown in indicators. Only a select few roles, and the
+ * system app itself, are hidden. These values are updated at most every 15 seconds.
* @hide
*/
- public static boolean isSpecialCaseShownIndicator(@NonNull Context context,
+ public static boolean shouldShowPackageForIndicatorCached(@NonNull Context context,
@NonNull String packageName) {
-
- if (packageName.equals(SYSTEM_PKG)) {
+ if (SYSTEM_PKG.equals(packageName)) {
return false;
}
+ long now = SystemClock.elapsedRealtime();
+ if (sLastIndicatorUpdateTime == -1
+ || (now - sLastIndicatorUpdateTime) > EXEMPTED_INDICATOR_ROLE_UPDATE_FREQUENCY_MS) {
+ sLastIndicatorUpdateTime = now;
+ for (int i = 0; i < EXEMPTED_ROLES.length; i++) {
+ INDICATOR_EXEMPTED_PACKAGES[i] = context.getString(EXEMPTED_ROLES[i]);
+ }
+ }
+ for (int i = 0; i < EXEMPTED_ROLES.length; i++) {
+ String exemptedPackage = INDICATOR_EXEMPTED_PACKAGES[i];
+ if (exemptedPackage != null && exemptedPackage.equals(packageName)) {
+ return false;
+ }
+ }
- return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "permissions_hub_2_enabled",
- false)
- || packageName.equals(context.getString(R.string.config_systemSpeechRecognizer))
- || context.getSystemService(LocationManager.class).isProviderPackage(packageName);
+ return true;
}
/**
* Gets the list of packages that have permissions that specified
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index b427df7..53ba259 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -26,8 +26,6 @@
import static android.app.AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE;
import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO;
import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
-import static android.app.AppOpsManager.opToPermission;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED;
import static android.media.AudioSystem.MODE_IN_COMMUNICATION;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
@@ -284,10 +282,6 @@
continue;
}
- if (!shouldShowPermissionsHub() && !isUserSensitive(packageName, user, op)) {
- continue;
- }
-
boolean isRunning = attrOpEntry.isRunning()
|| lastAccessTime >= runningThreshold;
@@ -375,7 +369,7 @@
// for it's uid and package name, save it.
int usageId = usage.getPackageIdHash();
OpUsage lastMostRecent = mostRecentUsages.get(usageId);
- if (!usage.packageName.equals(SYSTEM_PKG) && (lastMostRecent == null
+ if (shouldShowPackage(usage.packageName) && (lastMostRecent == null
|| usage.lastAccessTime > lastMostRecent.lastAccessTime)) {
mostRecentUsages.put(usageId, usage);
}
@@ -401,8 +395,7 @@
// We are missing the proxy usage. This may be because it's a one-step trusted
// proxy. Check if we should show the proxy label, and show it, if so.
OpUsage proxy = currentUsage.proxy;
- if (PermissionManager.isSpecialCaseShownIndicator(mContext, proxy.packageName)
- || isUserSensitive(proxy.packageName, proxy.getUser(), proxy.op)) {
+ if (shouldShowPackage(proxy.packageName)) {
currentUsage = proxy;
// We've effectively added one usage, so increment the max number of usages
maxUsages++;
@@ -411,7 +404,6 @@
}
}
-
if (currentUsage == null || iterNum == maxUsages
|| currentUsage.getPackageIdHash() == start.getPackageIdHash()) {
// We have an invalid state, or a cycle, so break
@@ -421,7 +413,7 @@
proxyPackages.add(currentUsage.getPackageIdHash());
// Don't add an app label for the main app, or the system app
if (!currentUsage.packageName.equals(start.packageName)
- && !currentUsage.packageName.equals(SYSTEM_PKG)) {
+ && shouldShowPackage(currentUsage.packageName)) {
try {
PackageManager userPkgManager =
getUserContext(currentUsage.getUser()).getPackageManager();
@@ -451,17 +443,8 @@
return usagesAndLabels;
}
- private boolean isUserSensitive(String packageName, UserHandle user, String op) {
- if (op.equals(OPSTR_PHONE_CALL_CAMERA) || op.equals(OPSTR_PHONE_CALL_MICROPHONE)) {
- return true;
- }
-
- if (opToPermission(op) == null) {
- return false;
- }
-
- int permFlags = mPkgManager.getPermissionFlags(opToPermission(op), packageName, user);
- return (permFlags & FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
+ private boolean shouldShowPackage(String packageName) {
+ return PermissionManager.shouldShowPackageForIndicatorCached(mContext, packageName);
}
/**
diff --git a/core/java/android/service/timezone/TimeZoneProviderService.java b/core/java/android/service/timezone/TimeZoneProviderService.java
index a9348c6..a2b22e8 100644
--- a/core/java/android/service/timezone/TimeZoneProviderService.java
+++ b/core/java/android/service/timezone/TimeZoneProviderService.java
@@ -50,35 +50,49 @@
*
* <p>Once stopped or failed, providers are required to stop generating callbacks.
*
- * <p>Provider discovery:
- *
- * <p>You must declare the service in your manifest file with the
- * {@link android.Manifest.permission#BIND_TIME_ZONE_PROVIDER_SERVICE} permission,
- * and include an intent filter with the necessary action indicating what type of provider it is.
- *
- * <p>Device configuration can influence how {@link TimeZoneProviderService}s are discovered.
- * In one mode, there can be multiple {@link TimeZoneProviderService}s configured with the same
- * action, and the one with the highest "serviceVersion" metadata will be used.
- *
- * <p>{@link TimeZoneProviderService}s may be deployed into processes that run once-per-user
- * or once-per-device (i.e. they service multiple users). The "serviceIsMultiuser" metadata must
- * be set accordingly.
- *
* <p>Provider types:
*
* <p>Android supports up to two <em>location-derived</em> time zone providers. These are called the
- * "primary" and "secondary" location time zone provider. The primary location time zone provider is
- * started first and will be used until it becomes uncertain or fails, at which point the secondary
- * provider will be started.
+ * "primary" and "secondary" location time zone providers. When a location-derived time zone is
+ * required, the primary location time zone provider is started first and used until it becomes
+ * uncertain or fails, at which point the secondary provider will be started. The secondary will be
+ * started and stopped as needed.
*
- * <p>Location-derived time zone providers are configured using {@link
- * #PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE} and {@link
- * #SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE} intent-filter actions respectively.
- * Besides declaring the android:permission attribute mentioned above, the application supplying a
- * location provider must be granted the {@link
+ * <p>Provider discovery:
+ *
+ * <p>Each provider is optional and can be disabled. When enabled, a provider's package name must
+ * be explicitly configured in the system server, see {@code
+ * config_primaryLocationTimeZoneProviderPackageName} and {@code
+ * config_secondaryLocationTimeZoneProviderPackageName} for details.
+ *
+ * <p>You must declare the service in the AndroidManifest of the app hosting the provider with the
+ * {@link android.Manifest.permission#BIND_TIME_ZONE_PROVIDER_SERVICE} permission,
+ * and include an intent filter with the necessary action indicating that it is the primary
+ * provider ({@link #PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE}) or the secondary
+ * provider ({@link #SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE}).
+ *
+ * <p>Besides declaring the android:permission attribute mentioned above, the application supplying
+ * a location provider must be granted the {@link
* android.Manifest.permission#INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE} permission to be
* accepted by the system server.
*
+ * <p>{@link TimeZoneProviderService}s may be deployed into processes that run once-per-user
+ * or once-per-device (i.e. they service multiple users). See serviceIsMultiuser metadata below for
+ * configuration details.
+ *
+ * <p>The service may specify metadata on its capabilities:
+ *
+ * <ul>
+ * <li>
+ * "serviceIsMultiuser": A boolean property, indicating if the service wishes to take
+ * responsibility for handling changes to the current user on the device. If true, the
+ * service will always be bound from the system user. If false, the service will always be
+ * bound from the current user. If the current user changes, the old binding will be
+ * released, and a new binding established under the new user. Assumed to be false if not
+ * specified.
+ * </li>
+ * </ul>
+ *
* <p>For example:
* <pre>
* <uses-permission
@@ -86,7 +100,7 @@
*
* ...
*
- * <service android:name=".FooTimeZoneProviderService"
+ * <service android:name=".ExampleTimeZoneProviderService"
* android:exported="true"
* android:permission="android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE">
* <intent-filter>
@@ -94,7 +108,6 @@
* android:name="android.service.timezone.SecondaryLocationTimeZoneProviderService"
* />
* </intent-filter>
- * <meta-data android:name="serviceVersion" android:value="1" />
* <meta-data android:name="serviceIsMultiuser" android:value="true" />
* </service>
* </pre>
diff --git a/core/java/android/util/Slog.java b/core/java/android/util/Slog.java
index 78c4739..117d75e 100644
--- a/core/java/android/util/Slog.java
+++ b/core/java/android/util/Slog.java
@@ -16,15 +16,9 @@
package android.util;
-import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
-import com.android.internal.annotations.GuardedBy;
-
-import java.util.Formatter;
-import java.util.Locale;
-
/**
* API for sending log output to the {@link Log#LOG_ID_SYSTEM} buffer.
*
@@ -34,12 +28,6 @@
*/
public final class Slog {
- @GuardedBy("Slog.class")
- private static StringBuilder sMessageBuilder;
-
- @GuardedBy("Slog.class")
- private static Formatter sFormatter;
-
private Slog() {
}
@@ -53,24 +41,6 @@
msg + '\n' + Log.getStackTraceString(tr));
}
- /**
- * Logs a {@link Log.VERBOSE} message.
- *
- * <p><strong>Note: </strong>the message will only be formatted if {@link Log#WARN} logging is
- * enabled for the given {@code tag}, but the compiler will still create an intermediate array
- * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
- * calling this method in a critical path, make sure to explicitly do the check before calling
- * it.
- *
- * @deprecated use {@code com.android.server.utils.SLogF} instead.
- */
- @Deprecated
- public static void v(String tag, String format, @Nullable Object... args) {
- if (!Log.isLoggable(tag, Log.VERBOSE)) return;
-
- v(tag, getMessage(format, args));
- }
-
@UnsupportedAppUsage
public static int d(String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.DEBUG, tag, msg);
@@ -82,24 +52,6 @@
msg + '\n' + Log.getStackTraceString(tr));
}
- /**
- * Logs a {@link Log.DEBUG} message.
- *
- * <p><strong>Note: </strong>the message will only be formatted if {@link Log#WARN} logging is
- * enabled for the given {@code tag}, but the compiler will still create an intermediate array
- * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
- * calling this method in a critical path, make sure to explicitly do the check before calling
- * it.
- *
- * @deprecated use {@code com.android.server.utils.SLogF} instead.
- */
- @Deprecated
- public static void d(String tag, String format, @Nullable Object... args) {
- if (!Log.isLoggable(tag, Log.DEBUG)) return;
-
- d(tag, getMessage(format, args));
- }
-
@UnsupportedAppUsage
public static int i(String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag, msg);
@@ -110,24 +62,6 @@
msg + '\n' + Log.getStackTraceString(tr));
}
- /**
- * Logs a {@link Log.INFO} message.
- *
- * <p><strong>Note: </strong>the message will only be formatted if {@link Log#WARN} logging is
- * enabled for the given {@code tag}, but the compiler will still create an intermediate array
- * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
- * calling this method in a critical path, make sure to explicitly do the check before calling
- * it.
- *
- * @deprecated use {@code com.android.server.utils.SLogF} instead.
- */
- @Deprecated
- public static void i(String tag, String format, @Nullable Object... args) {
- if (!Log.isLoggable(tag, Log.INFO)) return;
-
- i(tag, getMessage(format, args));
- }
-
@UnsupportedAppUsage
public static int w(String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, msg);
@@ -143,42 +77,6 @@
return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, Log.getStackTraceString(tr));
}
- /**
- * Logs a {@link Log.WARN} message.
- *
- * <p><strong>Note: </strong>the message will only be formatted if {@link Log#WARN} logging is
- * enabled for the given {@code tag}, but the compiler will still create an intermediate array
- * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
- * calling this method in a critical path, make sure to explicitly do the check before calling
- * it.
- *
- * @deprecated use {@code com.android.server.utils.SLogF} instead.
- */
- @Deprecated
- public static void w(String tag, String format, @Nullable Object... args) {
- if (!Log.isLoggable(tag, Log.WARN)) return;
-
- w(tag, getMessage(format, args));
- }
-
- /**
- * Logs a {@link Log.WARN} message with an exception
- *
- * <p><strong>Note: </strong>the message will only be formatted if {@link Log#WARN} logging is
- * enabled for the given {@code tag}, but the compiler will still create an intermediate array
- * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
- * calling this method in a critical path, make sure to explicitly do the check before calling
- * it.
- *
- * @deprecated use {@code com.android.server.utils.SLogF} instead.
- */
- @Deprecated
- public static void w(String tag, Exception exception, String format, @Nullable Object... args) {
- if (!Log.isLoggable(tag, Log.WARN)) return;
-
- w(tag, getMessage(format, args), exception);
- }
-
@UnsupportedAppUsage
public static int e(String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.ERROR, tag, msg);
@@ -191,42 +89,6 @@
}
/**
- * Logs a {@link Log.ERROR} message.
- *
- * <p><strong>Note: </strong>the message will only be formatted if {@link Log#WARN} logging is
- * enabled for the given {@code tag}, but the compiler will still create an intermediate array
- * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
- * calling this method in a critical path, make sure to explicitly do the check before calling
- * it.
- *
- * @deprecated use {@code com.android.server.utils.SLogF} instead.
- */
- @Deprecated
- public static void e(String tag, String format, @Nullable Object... args) {
- if (!Log.isLoggable(tag, Log.ERROR)) return;
-
- e(tag, getMessage(format, args));
- }
-
- /**
- * Logs a {@link Log.ERROR} message with an exception
- *
- * <p><strong>Note: </strong>the message will only be formatted if {@link Log#WARN} logging is
- * enabled for the given {@code tag}, but the compiler will still create an intermediate array
- * of the objects for the {@code vargars}, which could affect garbage collection. So, if you're
- * calling this method in a critical path, make sure to explicitly do the check before calling
- * it.
- *
- * @deprecated use {@code com.android.server.utils.SLogF} instead.
- */
- @Deprecated
- public static void e(String tag, Exception exception, String format, @Nullable Object... args) {
- if (!Log.isLoggable(tag, Log.ERROR)) return;
-
- e(tag, getMessage(format, args), exception);
- }
-
- /**
* Like {@link Log#wtf(String, String)}, but will never cause the caller to crash, and
* will always be handled asynchronously. Primarily for use by coding running within
* the system process.
@@ -237,27 +99,6 @@
}
/**
- * Logs a {@code wtf} message.
- *
- * @deprecated use {@code com.android.server.utils.SLogF} instead.
- */
- @Deprecated
- public static void wtf(String tag, String format, @Nullable Object... args) {
- wtf(tag, getMessage(format, args));
- }
-
- /**
- * Logs a {@code wtf} message with an exception.
- *
- * @deprecated use {@code com.android.server.utils.SLogF} instead.
- */
- @Deprecated
- public static void wtf(String tag, Exception exception, String format,
- @Nullable Object... args) {
- wtf(tag, getMessage(format, args), exception);
- }
-
- /**
* Like {@link #wtf(String, String)}, but does not output anything to the log.
*/
public static void wtfQuiet(String tag, String msg) {
@@ -297,18 +138,4 @@
public static int println(int priority, String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, priority, tag, msg);
}
-
- private static String getMessage(String format, @Nullable Object... args) {
- synchronized (Slog.class) {
- if (sMessageBuilder == null) {
- // Lazy load so they're not created if not used by the process
- sMessageBuilder = new StringBuilder();
- sFormatter = new Formatter(sMessageBuilder, Locale.ENGLISH);
- }
- sFormatter.format(format, args);
- String message = sMessageBuilder.toString();
- sMessageBuilder.setLength(0);
- return message;
- }
- }
}
diff --git a/core/java/android/uwb/DistanceMeasurement.java b/core/java/android/uwb/DistanceMeasurement.java
index 2a9bbdf..9856553 100644
--- a/core/java/android/uwb/DistanceMeasurement.java
+++ b/core/java/android/uwb/DistanceMeasurement.java
@@ -60,6 +60,7 @@
*
* @return error of distance measurement in meters
*/
+ @FloatRange(from = 0.0)
public double getErrorMeters() {
return mErrorMeters;
}
@@ -162,7 +163,7 @@
* @throws IllegalArgumentException if error is negative or NaN
*/
@NonNull
- public Builder setErrorMeters(double errorMeters) {
+ public Builder setErrorMeters(@FloatRange(from = 0.0) double errorMeters) {
if (Double.isNaN(errorMeters) || errorMeters < 0.0) {
throw new IllegalArgumentException(
"errorMeters must be >= 0.0 and not NaN: " + errorMeters);
@@ -178,7 +179,8 @@
* @throws IllegalArgumentException if confidence level is not in the range of [0.0, 1.0]
*/
@NonNull
- public Builder setConfidenceLevel(double confidenceLevel) {
+ public Builder setConfidenceLevel(
+ @FloatRange(from = 0.0, to = 1.0) double confidenceLevel) {
if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
throw new IllegalArgumentException(
"confidenceLevel must be in the range [0.0, 1.0]: " + confidenceLevel);
diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl
index 4036892..30da248 100644
--- a/core/java/android/uwb/IUwbAdapter.aidl
+++ b/core/java/android/uwb/IUwbAdapter.aidl
@@ -160,14 +160,4 @@
* closed.
*/
const int RANGING_SESSION_CLOSE_THRESHOLD_MS = 3000; // Value TBD
-
- /**
- * Ranging scheduling time unit (RSTU) for High Rate Pulse (HRP) PHY
- */
- const int HIGH_RATE_PULSE_CHIRPS_PER_RSTU = 416;
-
- /**
- * Ranging scheduling time unit (RSTU) for Low Rate Pulse (LRP) PHY
- */
- const int LOW_RATE_PULSE_CHIRPS_PER_RSTU = 1;
}
diff --git a/core/java/android/uwb/IUwbRangingCallbacks.aidl b/core/java/android/uwb/IUwbRangingCallbacks.aidl
index f71f3ff..f15debb 100644
--- a/core/java/android/uwb/IUwbRangingCallbacks.aidl
+++ b/core/java/android/uwb/IUwbRangingCallbacks.aidl
@@ -92,9 +92,13 @@
* Called when the ranging session has been stopped
*
* @param sessionHandle the session the callback is being invoked for
+ * @param reason the reason the session was stopped
+ * @param parameters protocol specific parameters
*/
- void onRangingStopped(in SessionHandle sessionHandle);
+ void onRangingStopped(in SessionHandle sessionHandle,
+ RangingChangeReason reason,
+ in PersistableBundle parameters);
/**
* Called when a ranging session fails to stop
diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java
index 5c7f0f5..ff8b912 100644
--- a/core/java/android/uwb/RangingManager.java
+++ b/core/java/android/uwb/RangingManager.java
@@ -171,7 +171,8 @@
}
@Override
- public void onRangingStopped(SessionHandle sessionHandle) {
+ public void onRangingStopped(SessionHandle sessionHandle, @RangingChangeReason int reason,
+ PersistableBundle params) {
synchronized (this) {
if (!hasSession(sessionHandle)) {
Log.w(TAG, "onRangingStopped - received unexpected SessionHandle: "
@@ -180,7 +181,7 @@
}
RangingSession session = mRangingSessionTable.get(sessionHandle);
- session.onRangingStopped();
+ session.onRangingStopped(convertToReason(reason), params);
}
}
diff --git a/core/java/android/uwb/RangingSession.java b/core/java/android/uwb/RangingSession.java
index 52ec5bd..345b69d 100644
--- a/core/java/android/uwb/RangingSession.java
+++ b/core/java/android/uwb/RangingSession.java
@@ -191,8 +191,11 @@
/**
* Invoked when a request to stop the session succeeds
+ *
+ * @param reason reason for the session stop
+ * @param parameters protocol specific parameters related to the stop reason
*/
- void onStopped();
+ void onStopped(@Reason int reason, @NonNull PersistableBundle parameters);
/**
* Invoked when a request to stop the session fails
@@ -434,14 +437,15 @@
/**
* @hide
*/
- public void onRangingStopped() {
+ public void onRangingStopped(@Callback.Reason int reason,
+ @NonNull PersistableBundle params) {
if (mState == State.CLOSED) {
Log.w(TAG, "onRangingStopped invoked for a closed session");
return;
}
mState = State.IDLE;
- executeCallback(() -> mCallback.onStopped());
+ executeCallback(() -> mCallback.onStopped(reason, params));
}
/**
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index c001ec9..c201e3b 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1131,7 +1131,7 @@
continue;
}
final InsetsSourceControl control = consumer.getControl();
- if (control != null) {
+ if (control != null && control.getLeash() != null) {
controls.put(consumer.getType(), new InsetsSourceControl(control));
typesReady |= toPublicType(consumer.getType());
} else if (animationType == ANIMATION_TYPE_SHOW) {
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index bc50dbe..8e50fed 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -33,6 +33,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.util.Log;
import android.util.imetracing.ImeTracing;
@@ -240,10 +241,6 @@
mHasWindowFocus = false;
}
- boolean hasWindowFocus() {
- return mHasWindowFocus;
- }
-
boolean hasViewFocusWhenWindowFocusGain() {
return mHasViewFocusWhenWindowFocusGain;
}
@@ -366,7 +363,16 @@
protected void setRequestedVisible(boolean requestedVisible) {
if (mRequestedVisible != requestedVisible) {
mRequestedVisible = requestedVisible;
- mIsAnimationPending = false;
+
+ // We need an animation later if the leash of a real control (which has an insets hint)
+ // is not ready. The !mIsAnimationPending check is in case that the requested visibility
+ // is changed twice before playing the animation -- we don't need an animation in this
+ // case.
+ mIsAnimationPending = !mIsAnimationPending
+ && mSourceControl != null
+ && mSourceControl.getLeash() == null
+ && !Insets.NONE.equals(mSourceControl.getInsetsHint());
+
mController.onRequestedVisibilityChanged(this);
if (DEBUG) Log.d(TAG, "setRequestedVisible: " + requestedVisible);
}
diff --git a/core/java/android/view/translation/TranslationResponse.java b/core/java/android/view/translation/TranslationResponse.java
index 35040f1..f9b7012 100644
--- a/core/java/android/view/translation/TranslationResponse.java
+++ b/core/java/android/view/translation/TranslationResponse.java
@@ -78,6 +78,13 @@
abstract static class BaseBuilder {
/**
+ * @deprecated Use {@link Builder#Builder(int)}.
+ * @hide
+ */
+ @Deprecated
+ public abstract Builder setTranslationStatus(@TranslationStatus int value);
+
+ /**
* Adds {@link TranslationResponseValue} to be translated. The input
* TranslationResponseValue format should match those provided by the
* {@link android.view.translation.Translator}'s destSpec.
@@ -137,7 +144,7 @@
- // Code below generated by codegen v1.0.22.
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -362,6 +369,8 @@
* The translation result status code.
*/
@DataClass.Generated.Member
+ @Override
+ @Deprecated
public @NonNull Builder setTranslationStatus(@TranslationStatus int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x1;
@@ -438,10 +447,10 @@
}
@DataClass.Generated(
- time = 1616189850232L,
- codegenVersion = "1.0.22",
+ time = 1617218030666L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/translation/TranslationResponse.java",
- inputSignatures = "public static final int TRANSLATION_STATUS_SUCCESS\npublic static final int TRANSLATION_STATUS_UNKNOWN_ERROR\npublic static final int TRANSLATION_STATUS_CONTEXT_UNSUPPORTED\nprivate final @android.view.translation.TranslationResponse.TranslationStatus int mTranslationStatus\nprivate final @android.annotation.NonNull android.util.SparseArray<android.view.translation.TranslationResponseValue> mTranslationResponseValues\nprivate final @android.annotation.NonNull android.util.SparseArray<android.view.translation.ViewTranslationResponse> mViewTranslationResponses\nprivate final boolean mFinalResponse\nprivate static android.util.SparseArray<android.view.translation.TranslationResponseValue> defaultTranslationResponseValues()\nprivate static android.util.SparseArray<android.view.translation.ViewTranslationResponse> defaultViewTranslationResponses()\nprivate static boolean defaultFinalResponse()\nclass TranslationResponse extends java.lang.Object implements [android.os.Parcelable]\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setTranslationResponseValue(int,android.view.translation.TranslationResponseValue)\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setViewTranslationResponse(int,android.view.translation.ViewTranslationResponse)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genHiddenConstDefs=true)\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setTranslationResponseValue(int,android.view.translation.TranslationResponseValue)\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setViewTranslationResponse(int,android.view.translation.ViewTranslationResponse)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "public static final int TRANSLATION_STATUS_SUCCESS\npublic static final int TRANSLATION_STATUS_UNKNOWN_ERROR\npublic static final int TRANSLATION_STATUS_CONTEXT_UNSUPPORTED\nprivate final @android.view.translation.TranslationResponse.TranslationStatus int mTranslationStatus\nprivate final @android.annotation.NonNull android.util.SparseArray<android.view.translation.TranslationResponseValue> mTranslationResponseValues\nprivate final @android.annotation.NonNull android.util.SparseArray<android.view.translation.ViewTranslationResponse> mViewTranslationResponses\nprivate final boolean mFinalResponse\nprivate static android.util.SparseArray<android.view.translation.TranslationResponseValue> defaultTranslationResponseValues()\nprivate static android.util.SparseArray<android.view.translation.ViewTranslationResponse> defaultViewTranslationResponses()\nprivate static boolean defaultFinalResponse()\nclass TranslationResponse extends java.lang.Object implements [android.os.Parcelable]\npublic abstract @java.lang.Deprecated android.view.translation.TranslationResponse.Builder setTranslationStatus(int)\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setTranslationResponseValue(int,android.view.translation.TranslationResponseValue)\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setViewTranslationResponse(int,android.view.translation.ViewTranslationResponse)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genHiddenConstDefs=true)\npublic abstract @java.lang.Deprecated android.view.translation.TranslationResponse.Builder setTranslationStatus(int)\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setTranslationResponseValue(int,android.view.translation.TranslationResponseValue)\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setViewTranslationResponse(int,android.view.translation.ViewTranslationResponse)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 29c78b5..f49aa74 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -3263,16 +3263,14 @@
/**
* Create a new RemoteViews object that will display the views contained
- * in the specified layout file.
+ * in the specified layout file and change the id of the root view to the specified one.
*
- * @param packageName Name of the package that contains the layout resource.
- * @param userId The user under which the package is running.
- * @param layoutId The id of the layout resource.
- *
- * @hide
+ * @param packageName Name of the package that contains the layout resource
+ * @param layoutId The id of the layout resource
*/
- public RemoteViews(String packageName, int userId, @LayoutRes int layoutId) {
- this(getApplicationInfo(packageName, userId), layoutId);
+ public RemoteViews(@NonNull String packageName, @LayoutRes int layoutId, @IdRes int viewId) {
+ this(packageName, layoutId);
+ this.mViewId = viewId;
}
/**
@@ -3727,7 +3725,7 @@
* The {@code stableId} will be used to identify a potential view to recycled when the remote
* view is inflated. Views can be re-used if inserted in the same order, potentially with
* some views appearing / disappearing. To be recycled the view must not change the layout
- * used to inflate it or its view id (see {@link RemoteViews#setViewId}).
+ * used to inflate it or its view id (see {@link RemoteViews#RemoteViews(String, int, int)}).
*
* Note: if a view is re-used, all the actions will be re-applied on it. However, its properties
* are not reset, so what was applied in previous round will have an effect. As a view may be
@@ -6340,25 +6338,9 @@
}
/**
- * Set the ID of the top-level view of the XML layout.
- *
- * The view's ID is changed right after inflation, before it gets added to its parent. The ID
- * of a given view can never change after the initial inflation.
- *
- * Set to {@link View#NO_ID} to reset and simply keep the id defined in the XML layout.
- *
- * @throws UnsupportedOperationException if the method is called on a RemoteViews defined in
- * term of other RemoteViews (e.g. {@link #RemoteViews(RemoteViews, RemoteViews)}).
+ * Get the ID of the top-level view of the XML layout, if set using
+ * {@link RemoteViews#RemoteViews(String, int, int)}.
*/
- public void setViewId(@IdRes int viewId) {
- if (hasMultipleLayouts()) {
- throw new UnsupportedOperationException(
- "The viewId can only be set on RemoteViews defined from a XML layout.");
- }
- mViewId = viewId;
- }
-
- /** Get the ID of the top-level view of the XML layout, as set by {@link #setViewId}. */
public @IdRes int getViewId() {
return mViewId;
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 98c75b9..1000914 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -18,6 +18,7 @@
import android.annotation.AnyThread;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.Uri;
import android.os.IBinder;
@@ -28,6 +29,8 @@
import com.android.internal.annotations.GuardedBy;
+import java.util.Objects;
+
/**
* A utility class to take care of boilerplate code around IPCs.
*/
@@ -47,7 +50,7 @@
* @param privOps Binder interface to be set
*/
@AnyThread
- public synchronized void set(IInputMethodPrivilegedOperations privOps) {
+ public synchronized void set(@NonNull IInputMethodPrivilegedOperations privOps) {
if (mPrivOps != null) {
throw new IllegalStateException(
"IInputMethodPrivilegedOperations must be set at most once."
@@ -90,7 +93,8 @@
* @param privOps Binder interface to be set
*/
@AnyThread
- public void set(IInputMethodPrivilegedOperations privOps) {
+ public void set(@NonNull IInputMethodPrivilegedOperations privOps) {
+ Objects.requireNonNull(privOps, "privOps must not be null");
mOps.set(privOps);
}
diff --git a/core/java/com/android/internal/os/BINDER_OWNERS b/core/java/com/android/internal/os/BINDER_OWNERS
new file mode 100644
index 0000000..9f68a32
--- /dev/null
+++ b/core/java/com/android/internal/os/BINDER_OWNERS
@@ -0,0 +1,2 @@
+dplotnikov@google.com
+gaillard@google.com
diff --git a/core/java/com/android/internal/os/OWNERS b/core/java/com/android/internal/os/OWNERS
index 3f01ebb..ea3b3a7 100644
--- a/core/java/com/android/internal/os/OWNERS
+++ b/core/java/com/android/internal/os/OWNERS
@@ -1,6 +1,7 @@
per-file *Power* = file:/services/core/java/com/android/server/power/OWNERS
per-file *Zygote* = file:/ZYGOTE_OWNERS
per-file *Cpu* = file:CPU_OWNERS
+per-file *Binder* = file:BINDER_OWNERS
# BatteryStats
per-file BatterySipper.java = file:/BATTERY_STATS_OWNERS
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 654b461..d16d9c6 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -95,4 +95,5 @@
boolean hasSecureLockScreen();
boolean tryUnlockWithCachedUnifiedChallenge(int userId);
void removeCachedUnifiedChallenge(int userId);
+ void updateEncryptionPassword(int type, in byte[] password);
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index a161f18..a0e50be 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -34,7 +34,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.UserInfo;
-import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -59,18 +58,13 @@
import com.google.android.collect.Lists;
-import libcore.util.HexEncoding;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.List;
-import java.util.StringJoiner;
/**
* Utilities for the lock pattern and its settings.
@@ -186,7 +180,7 @@
public static final String SYNTHETIC_PASSWORD_HANDLE_KEY = "sp-handle";
public static final String SYNTHETIC_PASSWORD_ENABLED_KEY = "enable-sp";
public static final int SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT = 1;
- private static final String HISTORY_DELIMITER = ",";
+ public static final String PASSWORD_HISTORY_DELIMITER = ",";
@UnsupportedAppUsage
private final Context mContext;
@@ -559,9 +553,11 @@
if(passwordHistoryLength == 0) {
return false;
}
- String legacyHash = legacyPasswordToHash(passwordToCheck, userId);
- String passwordHash = passwordToHistoryHash(passwordToCheck, hashFactor, userId);
- String[] history = passwordHistory.split(HISTORY_DELIMITER);
+ byte[] salt = getSalt(userId).getBytes();
+ String legacyHash = LockscreenCredential.legacyPasswordToHash(passwordToCheck, salt);
+ String passwordHash = LockscreenCredential.passwordToHistoryHash(
+ passwordToCheck, salt, hashFactor);
+ String[] history = passwordHistory.split(PASSWORD_HISTORY_DELIMITER);
// Password History may be too long...
for (int i = 0; i < Math.min(passwordHistoryLength, history.length); i++) {
if (history[i].equals(legacyHash) || history[i].equals(passwordHash)) {
@@ -701,20 +697,9 @@
} catch (RemoteException e) {
throw new RuntimeException("Unable to save lock password", e);
}
-
- onPostPasswordChanged(newCredential, userHandle);
return true;
}
- private void onPostPasswordChanged(LockscreenCredential newCredential, int userHandle) {
- updateEncryptionPasswordIfNeeded(newCredential, userHandle);
- if (newCredential.isPattern()) {
- reportPatternWasChosen(userHandle);
- }
- updatePasswordHistory(newCredential, userHandle);
- reportEnabledTrustAgentsChanged(userHandle);
- }
-
private void updateCryptoUserInfo(int userId) {
if (userId != UserHandle.USER_SYSTEM) {
return;
@@ -781,100 +766,6 @@
return getDeviceOwnerInfo() != null;
}
- /** Update the encryption password if it is enabled **/
- private void updateEncryptionPassword(final int type, final byte[] password) {
- if (!hasSecureLockScreen() && password != null && password.length != 0) {
- throw new UnsupportedOperationException(
- "This operation requires the lock screen feature.");
- }
- if (!isDeviceEncryptionEnabled()) {
- return;
- }
- final IBinder service = ServiceManager.getService("mount");
- if (service == null) {
- Log.e(TAG, "Could not find the mount service to update the encryption password");
- return;
- }
-
- // TODO(b/120484642): This is a location where we still use a String for vold
- String passwordString = password != null ? new String(password) : null;
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... dummy) {
- IStorageManager storageManager = IStorageManager.Stub.asInterface(service);
- try {
- storageManager.changeEncryptionPassword(type, passwordString);
- } catch (RemoteException e) {
- Log.e(TAG, "Error changing encryption password", e);
- }
- return null;
- }
- }.execute();
- }
-
- /**
- * Update device encryption password if calling user is USER_SYSTEM and device supports
- * encryption.
- */
- private void updateEncryptionPasswordIfNeeded(LockscreenCredential credential, int userHandle) {
- // Update the device encryption password.
- if (userHandle != UserHandle.USER_SYSTEM || !isDeviceEncryptionEnabled()) {
- return;
- }
- if (!shouldEncryptWithCredentials(true)) {
- updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
- return;
- }
- if (credential.isNone()) {
- // Set the encryption password to default.
- setCredentialRequiredToDecrypt(false);
- }
- updateEncryptionPassword(credential.getStorageCryptType(), credential.getCredential());
- }
-
- /**
- * Store the hash of the *current* password in the password history list, if device policy
- * enforces password history requirement.
- */
- private void updatePasswordHistory(LockscreenCredential password, int userHandle) {
- if (password.isNone()) {
- return;
- }
- if (password.isPattern()) {
- // Do not keep track of historical patterns
- return;
- }
- // Add the password to the password history. We assume all
- // password hashes have the same length for simplicity of implementation.
- String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle);
- if (passwordHistory == null) {
- passwordHistory = "";
- }
- int passwordHistoryLength = getRequestedPasswordHistoryLength(userHandle);
- if (passwordHistoryLength == 0) {
- passwordHistory = "";
- } else {
- final byte[] hashFactor = getPasswordHistoryHashFactor(password, userHandle);
- String hash = passwordToHistoryHash(password.getCredential(), hashFactor, userHandle);
- if (hash == null) {
- Log.e(TAG, "Compute new style password hash failed, fallback to legacy style");
- hash = legacyPasswordToHash(password.getCredential(), userHandle);
- }
- if (TextUtils.isEmpty(passwordHistory)) {
- passwordHistory = hash;
- } else {
- String[] history = passwordHistory.split(HISTORY_DELIMITER);
- StringJoiner joiner = new StringJoiner(HISTORY_DELIMITER);
- joiner.add(hash);
- for (int i = 0; i < passwordHistoryLength - 1 && i < history.length; i++) {
- joiner.add(history[i]);
- }
- passwordHistory = joiner.toString();
- }
- }
- setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle);
- }
-
/**
* Determine if the device supports encryption, even if it's set to default. This
* differs from isDeviceEncrypted() in that it returns true even if the device is
@@ -898,7 +789,11 @@
* Clears the encryption password.
*/
public void clearEncryptionPassword() {
- updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
+ try {
+ getLockSettings().updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't clear encryption password");
+ }
}
/**
@@ -1045,56 +940,9 @@
* @param password the gesture pattern.
*
* @return the hash of the pattern in a byte array.
- * TODO: move to LockscreenCredential class
*/
public String legacyPasswordToHash(byte[] password, int userId) {
- if (password == null || password.length == 0) {
- return null;
- }
-
- try {
- // Previously the password was passed as a String with the following code:
- // byte[] saltedPassword = (password + getSalt(userId)).getBytes();
- // The code below creates the identical digest preimage using byte arrays:
- byte[] salt = getSalt(userId).getBytes();
- byte[] saltedPassword = Arrays.copyOf(password, password.length + salt.length);
- System.arraycopy(salt, 0, saltedPassword, password.length, salt.length);
- byte[] sha1 = MessageDigest.getInstance("SHA-1").digest(saltedPassword);
- byte[] md5 = MessageDigest.getInstance("MD5").digest(saltedPassword);
-
- byte[] combined = new byte[sha1.length + md5.length];
- System.arraycopy(sha1, 0, combined, 0, sha1.length);
- System.arraycopy(md5, 0, combined, sha1.length, md5.length);
-
- final char[] hexEncoded = HexEncoding.encode(combined);
- Arrays.fill(saltedPassword, (byte) 0);
- return new String(hexEncoded);
- } catch (NoSuchAlgorithmException e) {
- throw new AssertionError("Missing digest algorithm: ", e);
- }
- }
-
- /**
- * Hash the password for password history check purpose.
- * TODO: move to LockscreenCredential class
- */
- private String passwordToHistoryHash(byte[] passwordToHash, byte[] hashFactor, int userId) {
- if (passwordToHash == null || passwordToHash.length == 0 || hashFactor == null) {
- return null;
- }
- try {
- MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
- sha256.update(hashFactor);
- byte[] salt = getSalt(userId).getBytes();
- byte[] saltedPassword = Arrays.copyOf(passwordToHash, passwordToHash.length
- + salt.length);
- System.arraycopy(salt, 0, saltedPassword, passwordToHash.length, salt.length);
- sha256.update(saltedPassword);
- Arrays.fill(saltedPassword, (byte) 0);
- return new String(HexEncoding.encode(sha256.digest()));
- } catch (NoSuchAlgorithmException e) {
- throw new AssertionError("Missing digest algorithm: ", e);
- }
+ return LockscreenCredential.legacyPasswordToHash(password, getSalt(userId).getBytes());
}
/**
@@ -1396,14 +1244,6 @@
}
}
- private boolean isDoNotAskCredentialsOnBootSet() {
- return getDevicePolicyManager().getDoNotAskCredentialsOnBoot();
- }
-
- private boolean shouldEncryptWithCredentials(boolean defaultValue) {
- return isCredentialRequiredToDecrypt(defaultValue) && !isDoNotAskCredentialsOnBootSet();
- }
-
private void throwIfCalledOnMainThread() {
if (Looper.getMainLooper().isCurrentThread()) {
throw new IllegalStateException("should not be called from the main thread.");
@@ -1590,12 +1430,7 @@
credential.checkLength();
LockSettingsInternal localService = getLockSettingsInternal();
- if (!localService.setLockCredentialWithToken(credential, tokenHandle, token, userHandle)) {
- return false;
- }
-
- onPostPasswordChanged(credential, userHandle);
- return true;
+ return localService.setLockCredentialWithToken(credential, tokenHandle, token, userHandle);
}
/**
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java
index a488449..361ba95 100644
--- a/core/java/com/android/internal/widget/LockscreenCredential.java
+++ b/core/java/com/android/internal/widget/LockscreenCredential.java
@@ -31,6 +31,10 @@
import com.android.internal.util.Preconditions;
+import libcore.util.HexEncoding;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -276,6 +280,82 @@
return getType() == storedCredentialType;
}
+ /**
+ * Hash the password for password history check purpose.
+ */
+ public String passwordToHistoryHash(byte[] salt, byte[] hashFactor) {
+ return passwordToHistoryHash(mCredential, salt, hashFactor);
+ }
+
+ /**
+ * Hash the password for password history check purpose.
+ */
+ public static String passwordToHistoryHash(
+ byte[] passwordToHash, byte[] salt, byte[] hashFactor) {
+ if (passwordToHash == null || passwordToHash.length == 0
+ || hashFactor == null || salt == null) {
+ return null;
+ }
+ try {
+ MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
+ sha256.update(hashFactor);
+ byte[] saltedPassword = Arrays.copyOf(passwordToHash, passwordToHash.length
+ + salt.length);
+ System.arraycopy(salt, 0, saltedPassword, passwordToHash.length, salt.length);
+ sha256.update(saltedPassword);
+ Arrays.fill(saltedPassword, (byte) 0);
+ return new String(HexEncoding.encode(sha256.digest()));
+ } catch (NoSuchAlgorithmException e) {
+ throw new AssertionError("Missing digest algorithm: ", e);
+ }
+ }
+
+ /**
+ * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash.
+ * Not the most secure, but it is at least a second level of protection. First level is that
+ * the file is in a location only readable by the system process.
+ *
+ * @return the hash of the pattern in a byte array.
+ */
+ public String legacyPasswordToHash(byte[] salt) {
+ return legacyPasswordToHash(mCredential, salt);
+ }
+
+ /**
+ * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash.
+ * Not the most secure, but it is at least a second level of protection. First level is that
+ * the file is in a location only readable by the system process.
+ *
+ * @param password the gesture pattern.
+ *
+ * @return the hash of the pattern in a byte array.
+ */
+ public static String legacyPasswordToHash(byte[] password, byte[] salt) {
+ if (password == null || password.length == 0 || salt == null) {
+ return null;
+ }
+
+ try {
+ // Previously the password was passed as a String with the following code:
+ // byte[] saltedPassword = (password + salt).getBytes();
+ // The code below creates the identical digest preimage using byte arrays:
+ byte[] saltedPassword = Arrays.copyOf(password, password.length + salt.length);
+ System.arraycopy(salt, 0, saltedPassword, password.length, salt.length);
+ byte[] sha1 = MessageDigest.getInstance("SHA-1").digest(saltedPassword);
+ byte[] md5 = MessageDigest.getInstance("MD5").digest(saltedPassword);
+
+ byte[] combined = new byte[sha1.length + md5.length];
+ System.arraycopy(sha1, 0, combined, 0, sha1.length);
+ System.arraycopy(md5, 0, combined, sha1.length, md5.length);
+
+ final char[] hexEncoded = HexEncoding.encode(combined);
+ Arrays.fill(saltedPassword, (byte) 0);
+ return new String(hexEncoded);
+ } catch (NoSuchAlgorithmException e) {
+ throw new AssertionError("Missing digest algorithm: ", e);
+ }
+ }
+
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mType);
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index b207ad3..04528e9 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -16,6 +16,8 @@
#define ATRACE_TAG ATRACE_TAG_RESOURCES
+#include "signal.h"
+
#include "android-base/logging.h"
#include "android-base/macros.h"
#include "android-base/stringprintf.h"
@@ -353,8 +355,59 @@
return reinterpret_cast<jlong>(apk_assets.release());
}
+// STOPSHIP (b/159041693): Revert signal handler when reason for issue is found.
+static thread_local std::stringstream destroy_info;
+static struct sigaction old_handler_action;
+
+static void DestroyErrorHandler(int sig, siginfo_t* info, void* ucontext) {
+ if (sig != SIGSEGV) {
+ return;
+ }
+
+ LOG(ERROR) << "(b/159041693) - Failed to destroy ApkAssets " << destroy_info.str();
+ if (old_handler_action.sa_handler == SIG_DFL) {
+ // reset the action to default and re-raise the signal. It will kill the process
+ signal(sig, SIG_DFL);
+ raise(sig);
+ return;
+ }
+ if (old_handler_action.sa_handler == SIG_IGN) {
+ // ignoring SIGBUS won't help us much, as we'll get back right here after retrying.
+ return;
+ }
+ if (old_handler_action.sa_flags & SA_SIGINFO) {
+ old_handler_action.sa_sigaction(sig, info, ucontext);
+ } else {
+ old_handler_action.sa_handler(sig);
+ }
+}
+
static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
- delete reinterpret_cast<ApkAssets*>(ptr);
+ auto apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ destroy_info << "{ptr=" << apk_assets;
+ if (apk_assets != nullptr) {
+ destroy_info << ", name='" << apk_assets->GetDebugName() << "'"
+ << ", idmap=" << apk_assets->GetLoadedIdmap()
+ << ", {arsc=" << apk_assets->GetLoadedArsc();
+ if (auto arsc = apk_assets->GetLoadedArsc()) {
+ destroy_info << ", strings=" << arsc->GetStringPool()
+ << ", packages=" << &arsc->GetPackages()
+ << " [";
+ for (auto& package : arsc->GetPackages()) {
+ destroy_info << "{unique_ptr=" << &package
+ << ", package=" << package.get() << "},";
+ }
+ destroy_info << "]";
+ }
+ destroy_info << "}";
+ }
+ destroy_info << "}";
+
+ delete apk_assets;
+
+ // Deleting the apk assets did not lead to a crash.
+ destroy_info.str("");
+ destroy_info.clear();
}
static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
@@ -507,6 +560,17 @@
jclass parcelFd = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
gParcelFileDescriptorOffsets.detachFd = GetMethodIDOrDie(env, parcelFd, "detachFd", "()I");
+ // STOPSHIP (b/159041693): Revert signal handler when reason for issue is found.
+ sigset_t allowed;
+ sigemptyset(&allowed);
+ sigaddset(&allowed, SIGSEGV);
+ pthread_sigmask(SIG_UNBLOCK, &allowed, nullptr);
+ struct sigaction action = {
+ .sa_flags = SA_SIGINFO,
+ .sa_sigaction = &DestroyErrorHandler,
+ };
+ sigaction(SIGSEGV, &action, &old_handler_action);
+
return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods,
arraysize(gApkAssetsMethods));
}
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index 5630a1e..7fde92c 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -45,6 +45,7 @@
#define ENCODING_MPEGH_BL_L4 24
#define ENCODING_MPEGH_LC_L3 25
#define ENCODING_MPEGH_LC_L4 26
+#define ENCODING_DTS_UHD 27
#define ENCODING_INVALID 0
#define ENCODING_DEFAULT 1
@@ -110,6 +111,8 @@
return AUDIO_FORMAT_MPEGH_LC_L3;
case ENCODING_MPEGH_LC_L4:
return AUDIO_FORMAT_MPEGH_LC_L4;
+ case ENCODING_DTS_UHD:
+ return AUDIO_FORMAT_DTS_UHD;
default:
return AUDIO_FORMAT_INVALID;
}
@@ -179,6 +182,8 @@
return ENCODING_MPEGH_LC_L3;
case AUDIO_FORMAT_MPEGH_LC_L4:
return ENCODING_MPEGH_LC_L4;
+ case AUDIO_FORMAT_DTS_UHD:
+ return ENCODING_DTS_UHD;
case AUDIO_FORMAT_DEFAULT:
return ENCODING_DEFAULT;
default:
diff --git a/core/proto/android/app/activitymanager.proto b/core/proto/android/app/activitymanager.proto
index ead7e16..a21d1d4 100644
--- a/core/proto/android/app/activitymanager.proto
+++ b/core/proto/android/app/activitymanager.proto
@@ -33,4 +33,6 @@
UID_OBSERVER_FLAG_ACTIVE = 4;
// report uid cached state has changed, original value is 1 << 4
UID_OBSERVER_FLAG_CACHED = 5;
+ // report uid capability has changed, original value is 1 << 5
+ UID_OBSERVER_FLAG_CAPABILITY = 6;
}
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 74a37ca..17dc4589 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -778,6 +778,7 @@
CHANGE_ACTIVE = 2;
CHANGE_CACHED = 3;
CHANGE_UNCACHED = 4;
+ CHANGE_CAPABILITY = 5;
}
repeated Change last_reported_changes = 8;
optional int32 num_procs = 9;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 46f2f3e..58abfeb 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -345,7 +345,7 @@
<protected-broadcast android:name="android.nfc.handover.intent.action.HANDOVER_SEND_MULTIPLE" />
<protected-broadcast android:name="com.android.nfc.handover.action.CANCEL_HANDOVER_TRANSFER" />
- <protected-broadcast android:name="android.intent.action.CLEAR_DNS_CACHE" />
+ <protected-broadcast android:name="android.net.action.CLEAR_DNS_CACHE" />
<protected-broadcast android:name="android.intent.action.PROXY_CHANGE" />
<protected-broadcast android:name="android.os.UpdateLock.UPDATE_LOCK_CHANGED" />
@@ -1390,7 +1390,7 @@
<!-- Required to be able to discover and connect to nearby Bluetooth devices.
<p>Protection level: dangerous -->
<permission-group android:name="android.permission-group.NEARBY_DEVICES"
- android:icon="@drawable/ic_qs_bluetooth"
+ android:icon="@drawable/perm_group_nearby_devices"
android:label="@string/permgrouplab_nearby_devices"
android:description="@string/permgroupdesc_nearby_devices"
android:priority="750" />
@@ -4240,6 +4240,15 @@
<permission android:name="android.permission.MANAGE_ROLE_HOLDERS"
android:protectionLevel="signature|installer" />
+ <!-- @SystemApi Allows an application to bypass role qualification. This allows switching role
+ holders to otherwise non eligible holders. Only the shell is allowed to do this, the
+ qualification for the shell role itself cannot be bypassed, and each role needs to
+ explicitly allow bypassing qualification in its definition. The bypass state will not be
+ persisted across reboot.
+ @hide -->
+ <permission android:name="android.permission.BYPASS_ROLE_QUALIFICATION"
+ android:protectionLevel="internal|role" />
+
<!-- @SystemApi Allows an application to observe role holder changes.
@hide -->
<permission android:name="android.permission.OBSERVE_ROLE_HOLDERS"
diff --git a/core/res/res/drawable/perm_group_nearby_devices.xml b/core/res/res/drawable/perm_group_nearby_devices.xml
new file mode 100644
index 0000000..84cc432
--- /dev/null
+++ b/core/res/res/drawable/perm_group_nearby_devices.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ 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="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="#000000"
+ android:pathData="M12,16.427L7.574,12 12,7.574 16.426,12zM10.58,2.59l-8,8c-0.78,0.78 -0.78,2.05 0,2.83l8,8c0.78,0.78 2.05,0.78 2.83,0l8,-8c0.78,-0.78 0.78,-2.05 0,-2.83l-8,-8c-0.78,-0.79 -2.04,-0.79 -2.83,0zM13.39,17.81L12,19.2l-1.39,-1.39 -4.42,-4.42L4.8,12l1.39,-1.39 4.42,-4.42L12,4.8l1.39,1.39 4.42,4.42L19.2,12l-1.39,1.39 -4.42,4.42z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 986bb82..c51b2d8 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1867,16 +1867,21 @@
-->
<attr name="preserveLegacyExternalStorage" format="boolean" />
- <!-- If {@code true} this app would like optimized external storage access.
+ <!-- If {@code true} this app would like raw external storage access.
<p> This flag can only be used by apps holding
<ul>
<li>{@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} permission or
<li>{@link android.app.role}#SYSTEM_GALLERY role.
</ul>
- When the flag is set, bulk file path operations will be optimized.
+ <p> When the flag is set, all file path access on external storage will bypass database
+ operations that update MediaStore collection. Raw external storage access as a side effect
+ can improve performance of bulk file path operations but can cause unexpected behavior in
+ apps due to inconsistencies in MediaStore collection and lower file system.
+ When the flag is set, app should scan the file after file path operations to ensure
+ consistency of MediaStore collection.
- The default value is {@code true} if
+ <p> The default value is {@code true} if
<ul>
<li>app has {@link android.Manifest.permission#MANAGE_EXTERNAL_STORAGE} permission and
targets targetSDK<=30.
@@ -1884,7 +1889,7 @@
</ul>
{@code false} otherwise.
-->
- <attr name="requestOptimizedExternalStorageAccess" format="boolean" />
+ <attr name="requestRawExternalStorageAccess" format="boolean" />
<!-- If {@code true} this app declares that it should be visible to all other apps on
device, regardless of what they declare via the {@code queries} tags in their
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fc2bdf3..49bd853 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1637,32 +1637,22 @@
config_timeZoneRulesUpdateTrackingEnabled are true.] -->
<integer name="config_timeZoneRulesCheckRetryCount">5</integer>
- <!-- Whether the geolocation time zone detection feature is enabled. -->
+ <!-- Whether the geolocation time zone detection feature is enabled. Setting this to false means
+ the feature cannot be used. Setting this to true means it may be used if other
+ configuration allows (see provider configuration below, also compile time overlays). -->
<bool name="config_enableGeolocationTimeZoneDetection" translatable="false">true</bool>
- <!-- Whether the primary LocationTimeZoneProvider is enabled device.
+ <!-- Whether the primary LocationTimeZoneProvider is enabled.
Ignored if config_enableGeolocationTimeZoneDetection is false -->
<bool name="config_enablePrimaryLocationTimeZoneProvider" translatable="false">false</bool>
- <!-- Used when enablePrimaryLocationTimeZoneProvider is true. Controls whether to enable primary
- location time zone provider overlay which allows the primary location time zone provider to
- be replaced by an app at run-time. When disabled, only the
- config_primaryLocationTimeZoneProviderPackageName package will be searched for the primary
- location time zone provider, otherwise any system package is eligible. Anyone who wants to
- disable the runtime overlay mechanism can set it to false. -->
- <bool name="config_enablePrimaryLocationTimeZoneOverlay" translatable="false">false</bool>
- <!-- Package name providing the primary location time zone provider. Used only when
- config_enablePrimaryLocationTimeZoneOverlay is false. -->
+ <!-- The package name providing the primary location time zone provider.
+ Only used when config_enableGeolocationTimeZoneDetection and
+ enablePrimaryLocationTimeZoneProvider are true. -->
<string name="config_primaryLocationTimeZoneProviderPackageName" translatable="false">@null</string>
- <!-- Whether the secondary LocationTimeZoneProvider is enabled device.
+ <!-- Whether the secondary LocationTimeZoneProvider is enabled.
Ignored if config_enableGeolocationTimeZoneDetection is false -->
<bool name="config_enableSecondaryLocationTimeZoneProvider" translatable="false">true</bool>
- <!-- Used when enableSecondaryLocationTimeZoneProvider is true. Controls whether to enable
- secondary location time zone provider overlay which allows the primary location time zone
- provider to config_secondaryLocationTimeZoneProviderPackageName package will be searched
- for the secondary location time zone provider, otherwise any system package is eligible.
- Anyone who wants to disable the runtime overlay mechanism can set it to false. -->
- <bool name="config_enableSecondaryLocationTimeZoneOverlay" translatable="false">false</bool>
<!-- Package name providing the secondary location time zone provider. Used only when
config_enableSecondaryLocationTimeZoneOverlay is false.
@@ -1942,11 +1932,13 @@
<!-- The name of the package that will hold the speech recognizer role by default. -->
<string name="config_systemSpeechRecognizer" translatable="false"></string>
<!-- The name of the package that will hold the system Wi-Fi coex manager role. -->
- <string name="config_systemWifiCoexManager" translateable="false"></string>
+ <string name="config_systemWifiCoexManager" translatable="false"></string>
<!-- The name of the package that will hold the wellbeing role. -->
<string name="config_systemWellbeing" translatable="false"></string>
<!-- The name of the package that will hold the television notification handler role -->
<string name="config_systemTelevisionNotificationHandler" translatable="false"></string>
+ <!-- The name of the package that will hold the system activity recognizer role. -->
+ <string name="config_systemActivityRecognizer" translatable="false"></string>
<!-- The name of the package that will be allowed to change its components' label/icon. -->
<string name="config_overrideComponentUiPackage" translatable="false"></string>
@@ -2580,10 +2572,6 @@
<!-- Set to true if after a provisioning apn the radio should be restarted -->
<bool name="config_restartRadioAfterProvisioning">false</bool>
- <!-- Boolean indicating if RADIO POWER OFF is required on receiving SIM REFRESH with RESET.
- This will be handled by modem if it is false. -->
- <bool name="config_requireRadioPowerOffOnSimRefreshReset">false</bool>
-
<!-- Vibrator pattern to be used as the default for notifications
that specify DEFAULT_VIBRATE.
-->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b402c95..3a5621b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3092,7 +3092,7 @@
<public name="attributionTags"/>
<public name="suppressesSpellChecker" />
<public name="usesPermissionFlags" />
- <public name="requestOptimizedExternalStorageAccess" />
+ <public name="requestRawExternalStorageAccess" />
<!-- @hide @SystemApi -->
<public name="playHomeTransitionSound" />
<public name="lStar" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 61e766e..90e9c09 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -823,9 +823,9 @@
<string name="permgroupdesc_camera">take pictures and record video</string>
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=40]-->
- <string name="permgrouplab_nearby_devices">Nearby Bluetooth Devices</string>
+ <string name="permgrouplab_nearby_devices">Nearby devices</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE]-->
- <string name="permgroupdesc_nearby_devices">discover and connect to nearby Bluetooth devices</string>
+ <string name="permgroupdesc_nearby_devices">discover and connect to nearby devices</string>
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgrouplab_calllog">Call logs</string>
@@ -1486,9 +1486,9 @@
<string name="permdesc_bluetooth_connect" product="default">Allows the app to connect to paired Bluetooth devices</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=50]-->
- <string name="permlab_uwb_ranging">range to devices using ultra-wideband</string>
+ <string name="permlab_uwb_ranging">determine relative position between nearby Ultra-Wideband devices</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=120]-->
- <string name="permdesc_uwb_ranging" product="default">Allows the app to range to devices using ultra-wideband</string>
+ <string name="permdesc_uwb_ranging">Allow the app to determine relative position between nearby Ultra-Wideband devices</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_preferredPaymentInfo">Preferred NFC Payment Service Information</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index eb1fe1d..dbfb030 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -350,7 +350,6 @@
<java-symbol type="bool" name="config_camera_sound_forced" />
<java-symbol type="bool" name="config_dontPreferApn" />
<java-symbol type="bool" name="config_restartRadioAfterProvisioning" />
- <java-symbol type="bool" name="config_requireRadioPowerOffOnSimRefreshReset" />
<java-symbol type="bool" name="config_speed_up_audio_on_mt_calls" />
<java-symbol type="bool" name="config_useFixedVolume" />
<java-symbol type="bool" name="config_enableMultiUserUI"/>
@@ -2180,10 +2179,8 @@
<java-symbol type="bool" name="config_enableGnssTimeUpdateService" />
<java-symbol type="bool" name="config_enableGeolocationTimeZoneDetection" />
<java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneProvider" />
- <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneOverlay" />
<java-symbol type="string" name="config_primaryLocationTimeZoneProviderPackageName" />
<java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneProvider" />
- <java-symbol type="bool" name="config_enableSecondaryLocationTimeZoneOverlay" />
<java-symbol type="string" name="config_secondaryLocationTimeZoneProviderPackageName" />
<java-symbol type="layout" name="resolver_list" />
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
index dd60dd4..1a63660 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
@@ -44,8 +44,6 @@
import com.android.bandwidthtest.NetworkState.StateTransitionDirection;
import com.android.internal.util.AsyncChannel;
-import junit.framework.Assert;
-
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.List;
@@ -76,7 +74,11 @@
private WifiManager mWifiManager;
private Context mContext;
// Verify connectivity state
- private static final int NUM_NETWORK_TYPES = ConnectivityManager.MAX_NETWORK_TYPE + 1;
+ // ConnectivityManager.TYPE_* is deprecated and no longer extended, so use the max public
+ // network type - TYPE_VPN should be enough.
+ // TODO: Replace registering CONNECTIVITY_ACTION with registering NetworkCallback and check
+ // network by NetworkCapabilities.TRANSPORT_* and NetworkCapabilities.hasTransport() instead.
+ private static final int NUM_NETWORK_TYPES = ConnectivityManager.TYPE_VPN + 1;
private NetworkState[] mConnectivityState = new NetworkState[NUM_NETWORK_TYPES];
public ConnectionUtil(Context context) {
diff --git a/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java
index 05bab1c..9d77d16 100644
--- a/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java
@@ -17,6 +17,8 @@
package com.android.internal.widget;
+import static com.google.common.truth.Truth.assertThat;
+
import android.test.AndroidTestCase;
import java.util.Arrays;
@@ -175,6 +177,81 @@
assertEquals(credential, credential.duplicate());
}
+ public void testPasswordToHistoryHash() {
+ String password = "1234";
+ LockscreenCredential credential = LockscreenCredential.createPassword(password);
+ String hashFactor = "6637D20C0798382D9F1304861C81DE222BC6CB7183623C67DA99B115A7AF702D";
+ String salt = "6d5331dd120077a0";
+ String expectedHash = "BCFB17409F2CD0A00D8627F76D080FB547B0B6A30CB7A375A34720D2312EDAC7";
+
+ assertThat(
+ credential.passwordToHistoryHash(salt.getBytes(), hashFactor.getBytes()))
+ .isEqualTo(expectedHash);
+ assertThat(
+ LockscreenCredential.passwordToHistoryHash(
+ password.getBytes(), salt.getBytes(), hashFactor.getBytes()))
+ .isEqualTo(expectedHash);
+ }
+
+ public void testPasswordToHistoryHashInvalidInput() {
+ String password = "1234";
+ LockscreenCredential credential = LockscreenCredential.createPassword(password);
+ String hashFactor = "6637D20C0798382D9F1304861C81DE222BC6CB7183623C67DA99B115A7AF702D";
+ String salt = "6d5331dd120077a0";
+
+ assertThat(
+ credential.passwordToHistoryHash(/* salt= */ null, hashFactor.getBytes()))
+ .isNull();
+ assertThat(
+ LockscreenCredential.passwordToHistoryHash(
+ password.getBytes(), /* salt= */ null, hashFactor.getBytes()))
+ .isNull();
+
+ assertThat(
+ credential.passwordToHistoryHash(salt.getBytes(), /* hashFactor= */ null))
+ .isNull();
+ assertThat(
+ LockscreenCredential.passwordToHistoryHash(
+ password.getBytes(), salt.getBytes(), /* hashFactor= */ null))
+ .isNull();
+
+ assertThat(
+ LockscreenCredential.passwordToHistoryHash(
+ /* password= */ null, salt.getBytes(), hashFactor.getBytes()))
+ .isNull();
+ }
+
+ public void testLegacyPasswordToHash() {
+ String password = "1234";
+ LockscreenCredential credential = LockscreenCredential.createPassword(password);
+ String salt = "6d5331dd120077a0";
+ String expectedHash =
+ "2DD04348ADBF8F4CABD7F722DC2E2887FAD4B6020A0C3E02C831E09946F0554FDC13B155";
+
+ assertThat(
+ credential.legacyPasswordToHash(salt.getBytes()))
+ .isEqualTo(expectedHash);
+ assertThat(
+ LockscreenCredential.legacyPasswordToHash(
+ password.getBytes(), salt.getBytes()))
+ .isEqualTo(expectedHash);
+ }
+
+ public void testLegacyPasswordToHashInvalidInput() {
+ String password = "1234";
+ LockscreenCredential credential = LockscreenCredential.createPassword(password);
+ String salt = "6d5331dd120077a0";
+
+ assertThat(credential.legacyPasswordToHash(/* salt= */ null)).isNull();
+ assertThat(LockscreenCredential.legacyPasswordToHash(
+ password.getBytes(), /* salt= */ null)).isNull();
+
+ assertThat(
+ LockscreenCredential.legacyPasswordToHash(
+ /* password= */ null, salt.getBytes()))
+ .isNull();
+ }
+
private LockscreenCredential createPattern(String patternString) {
return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
patternString.getBytes()));
diff --git a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
index 8271bed..5de6d42 100644
--- a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
+++ b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
@@ -128,8 +128,8 @@
rangingManager.onRangingReconfigureFailed(handle, REASON, PARAMS);
verify(callback, times(1)).onReconfigureFailed(eq(REASON), eq(PARAMS));
- rangingManager.onRangingStopped(handle);
- verify(callback, times(1)).onStopped();
+ rangingManager.onRangingStopped(handle, REASON, PARAMS);
+ verify(callback, times(1)).onStopped(eq(REASON), eq(PARAMS));
rangingManager.onRangingStopFailed(handle, REASON, PARAMS);
verify(callback, times(1)).onStopFailed(eq(REASON), eq(PARAMS));
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 0987f77..489da16 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -429,6 +429,7 @@
<permission name="android.permission.LOG_COMPAT_CHANGE" />
<permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
<permission name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG" />
+ <permission name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD" />
<!-- Permissions required to test ambient display. -->
<permission name="android.permission.READ_DREAM_STATE" />
<permission name="android.permission.WRITE_DREAM_STATE" />
diff --git a/graphics/java/android/graphics/drawable/RippleAnimationSession.java b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
index 9ee1ef1..c317831 100644
--- a/graphics/java/android/graphics/drawable/RippleAnimationSession.java
+++ b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
@@ -89,7 +89,7 @@
}
public boolean shouldAnimateSparkle() {
- return mAnimateSparkle;
+ return mAnimateSparkle && ValueAnimator.getDurationScale() > 0;
}
public float getSparklePhase() {
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 1f9022b..a6aa4f2 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -353,7 +353,7 @@
boolean userPresenceRequired,
byte[] attestationChallenge,
boolean devicePropertiesAttestationIncluded,
- int[] attestationIds,
+ @NonNull int[] attestationIds,
boolean uniqueIdIncluded,
boolean userAuthenticationValidWhileOnBody,
boolean invalidatedByBiometricEnrollment,
@@ -779,9 +779,8 @@
* @return integer array representing the requested device IDs to attest.
*/
@SystemApi
- @Nullable
- public int[] getAttestationIds() {
- return Utils.cloneIfNotNull(mAttestationIds);
+ public @NonNull int[] getAttestationIds() {
+ return mAttestationIds.clone();
}
/**
@@ -911,7 +910,7 @@
private boolean mUserPresenceRequired = false;
private byte[] mAttestationChallenge = null;
private boolean mDevicePropertiesAttestationIncluded = false;
- private int[] mAttestationIds = null;
+ private int[] mAttestationIds = new int[0];
private boolean mUniqueIdIncluded = false;
private boolean mUserAuthenticationValidWhileOnBody;
private boolean mInvalidatedByBiometricEnrollment = true;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index c26d9f583..dc7f3dd 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -655,7 +655,7 @@
}
int[] idTypes = mSpec.getAttestationIds();
- if (idTypes == null) {
+ if (idTypes.length == 0) {
return;
}
final Set<Integer> idTypesSet = new ArraySet<>(idTypes.length);
diff --git a/libs/hwui/effects/StretchEffect.cpp b/libs/hwui/effects/StretchEffect.cpp
index d7162b9..de14beb 100644
--- a/libs/hwui/effects/StretchEffect.cpp
+++ b/libs/hwui/effects/StretchEffect.cpp
@@ -171,13 +171,13 @@
static const float ZERO = 0.f;
static const float CONTENT_DISTANCE_STRETCHED = 1.f;
-sk_sp<SkImageFilter> StretchEffect::getImageFilter(const sk_sp<SkImage>& snapshotImage) const {
+sk_sp<SkShader> StretchEffect::getShader(const sk_sp<SkImage>& snapshotImage) const {
if (isEmpty()) {
return nullptr;
}
- if (mStretchFilter != nullptr) {
- return mStretchFilter;
+ if (mStretchShader != nullptr) {
+ return mStretchShader;
}
float viewportWidth = stretchArea.width();
@@ -212,10 +212,9 @@
mBuilder->uniform("viewportWidth").set(&viewportWidth, 1);
mBuilder->uniform("viewportHeight").set(&viewportHeight, 1);
- mStretchFilter = SkImageFilters::Shader(mBuilder->makeShader(nullptr, false),
- SkRect{0, 0, viewportWidth, viewportHeight});
+ mStretchShader = mBuilder->makeShader(nullptr, false);
- return mStretchFilter;
+ return mStretchShader;
}
sk_sp<SkRuntimeEffect> StretchEffect::getStretchEffect() {
diff --git a/libs/hwui/effects/StretchEffect.h b/libs/hwui/effects/StretchEffect.h
index 8221b41..546d53b 100644
--- a/libs/hwui/effects/StretchEffect.h
+++ b/libs/hwui/effects/StretchEffect.h
@@ -53,7 +53,7 @@
StretchEffect& operator=(const StretchEffect& other) {
this->stretchArea = other.stretchArea;
this->mStretchDirection = other.mStretchDirection;
- this->mStretchFilter = nullptr;
+ this->mStretchShader = other.mStretchShader;
this->maxStretchAmountX = other.maxStretchAmountX;
this->maxStretchAmountY = other.maxStretchAmountY;
return *this;
@@ -76,14 +76,14 @@
maxStretchAmountY = std::max(maxStretchAmountY, other.maxStretchAmountY);
}
- sk_sp<SkImageFilter> getImageFilter(const sk_sp<SkImage>& snapshotImage) const;
+ sk_sp<SkShader> getShader(const sk_sp<SkImage>& snapshotImage) const;
SkRect stretchArea {0, 0, 0, 0};
float maxStretchAmountX = 0;
float maxStretchAmountY = 0;
void setStretchDirection(const SkVector& direction) {
- mStretchFilter = nullptr;
+ mStretchShader = nullptr;
mStretchDirection = direction;
}
@@ -93,7 +93,7 @@
static sk_sp<SkRuntimeEffect> getStretchEffect();
mutable SkVector mStretchDirection{0, 0};
mutable std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
- mutable sk_sp<SkImageFilter> mStretchFilter;
+ mutable sk_sp<SkShader> mStretchShader;
};
} // namespace android::uirenderer
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 5627a7e..77d99a6 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -180,20 +180,7 @@
paint->setColorFilter(sk_ref_sp(properties.getColorFilter()));
sk_sp<SkImageFilter> imageFilter = sk_ref_sp(properties.getImageFilter());
- sk_sp<SkImageFilter> stretchFilter =
- properties.getStretchEffect().getImageFilter(snapshotImage);
- sk_sp<SkImageFilter> filter;
- if (imageFilter && stretchFilter) {
- filter = SkImageFilters::Compose(
- std::move(stretchFilter),
- std::move(imageFilter)
- );
- } else if (stretchFilter) {
- filter = std::move(stretchFilter);
- } else {
- filter = std::move(imageFilter);
- }
- paint->setImageFilter(std::move(filter));
+ paint->setImageFilter(std::move(imageFilter));
return true;
}
return false;
@@ -262,8 +249,16 @@
TransformCanvas transformCanvas(canvas);
displayList->draw(&transformCanvas);
}
- canvas->drawImageRect(snapshotImage, bounds, bounds, sampling, &paint,
- SkCanvas::kStrict_SrcRectConstraint);
+
+ const StretchEffect& stretch = properties.layerProperties().getStretchEffect();
+ if (stretch.isEmpty()) {
+ canvas->drawImageRect(snapshotImage, bounds, bounds, sampling, &paint,
+ SkCanvas::kStrict_SrcRectConstraint);
+ } else {
+ sk_sp<SkShader> stretchShader = stretch.getShader(snapshotImage);
+ paint.setShader(stretchShader);
+ canvas->drawRect(bounds, paint);
+ }
if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index eedb996..24f553f 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -324,6 +324,8 @@
public static final int ENCODING_MPEGH_LC_L3 = 25;
/** Audio data format: MPEG-H low complexity profile, level 4 */
public static final int ENCODING_MPEGH_LC_L4 = 26;
+ /** Audio data format: DTS UHD compressed */
+ public static final int ENCODING_DTS_UHD = 27;
/** @hide */
public static String toLogFriendlyEncoding(int enc) {
@@ -380,6 +382,8 @@
return "ENCODING_MPEGH_LC_L3";
case ENCODING_MPEGH_LC_L4:
return "ENCODING_MPEGH_LC_L4";
+ case ENCODING_DTS_UHD:
+ return "ENCODING_DTS_UHD";
default :
return "invalid encoding " + enc;
}
@@ -615,6 +619,7 @@
case ENCODING_MPEGH_BL_L4:
case ENCODING_MPEGH_LC_L3:
case ENCODING_MPEGH_LC_L4:
+ case ENCODING_DTS_UHD:
return true;
default:
return false;
@@ -650,6 +655,7 @@
case ENCODING_MPEGH_BL_L4:
case ENCODING_MPEGH_LC_L3:
case ENCODING_MPEGH_LC_L4:
+ case ENCODING_DTS_UHD:
return true;
default:
return false;
@@ -688,6 +694,7 @@
case ENCODING_MPEGH_BL_L4:
case ENCODING_MPEGH_LC_L3:
case ENCODING_MPEGH_LC_L4:
+ case ENCODING_DTS_UHD:
return false;
case ENCODING_INVALID:
default:
@@ -726,6 +733,7 @@
case ENCODING_MPEGH_BL_L4:
case ENCODING_MPEGH_LC_L3:
case ENCODING_MPEGH_LC_L4:
+ case ENCODING_DTS_UHD:
return false;
case ENCODING_INVALID:
default:
@@ -1012,6 +1020,7 @@
case ENCODING_MPEGH_BL_L4:
case ENCODING_MPEGH_LC_L3:
case ENCODING_MPEGH_LC_L4:
+ case ENCODING_DTS_UHD:
mEncoding = encoding;
break;
case ENCODING_INVALID:
@@ -1238,7 +1247,8 @@
ENCODING_MPEGH_BL_L3,
ENCODING_MPEGH_BL_L4,
ENCODING_MPEGH_LC_L3,
- ENCODING_MPEGH_LC_L4 }
+ ENCODING_MPEGH_LC_L4,
+ ENCODING_DTS_UHD }
)
@Retention(RetentionPolicy.SOURCE)
public @interface Encoding {}
@@ -1258,6 +1268,7 @@
ENCODING_MPEGH_BL_L4,
ENCODING_MPEGH_LC_L3,
ENCODING_MPEGH_LC_L4,
+ ENCODING_DTS_UHD
};
/** @hide */
@@ -1274,7 +1285,8 @@
ENCODING_MPEGH_BL_L3,
ENCODING_MPEGH_BL_L4,
ENCODING_MPEGH_LC_L3,
- ENCODING_MPEGH_LC_L4 }
+ ENCODING_MPEGH_LC_L4,
+ ENCODING_DTS_UHD }
)
@Retention(RetentionPolicy.SOURCE)
public @interface SurroundSoundEncoding {}
@@ -1316,6 +1328,8 @@
return "MPEG-H 3D Audio low complexity profile level 3";
case ENCODING_MPEGH_LC_L4:
return "MPEG-H 3D Audio low complexity profile level 4";
+ case ENCODING_DTS_UHD:
+ return "DTS UHD";
default:
return "Unknown surround sound format";
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 7220379..5f60fb6 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -428,6 +428,8 @@
return "AUDIO_FORMAT_MAT_2_0"; // (MAT | MAT_SUB_2_0)
case /* AUDIO_FORMAT_MAT_2_1 */ 0x24000003:
return "AUDIO_FORMAT_MAT_2_1"; // (MAT | MAT_SUB_2_1)
+ case /* AUDIO_FORMAT_DTS_UHD */ 0x2E000000:
+ return "AUDIO_FORMAT_DTS_UHD";
default:
return "AUDIO_FORMAT_(" + audioFormat + ")";
}
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
index 9c77c85..ac5cb70 100644
--- a/packages/Connectivity/framework/api/current.txt
+++ b/packages/Connectivity/framework/api/current.txt
@@ -68,7 +68,6 @@
method public boolean bindProcessToNetwork(@Nullable android.net.Network);
method @NonNull public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull android.net.IpSecManager.UdpEncapsulationSocket, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network getActiveNetwork();
- method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public android.net.Network getActiveNetworkForUid(int);
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo getActiveNetworkInfo();
method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.NetworkInfo[] getAllNetworkInfo();
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public android.net.Network[] getAllNetworks();
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index 513b630..8dfdd614 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -26,6 +26,7 @@
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);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void startCaptivePortalApp(@NonNull android.net.Network);
method public void systemReady();
+ field public static final String ACTION_CLEAR_DNS_CACHE = "android.net.action.CLEAR_DNS_CACHE";
field public static final String ACTION_PROMPT_LOST_VALIDATION = "android.net.action.PROMPT_LOST_VALIDATION";
field public static final String ACTION_PROMPT_PARTIAL_CONNECTIVITY = "android.net.action.PROMPT_PARTIAL_CONNECTIVITY";
field public static final String ACTION_PROMPT_UNVALIDATED = "android.net.action.PROMPT_UNVALIDATED";
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index e2db2d6..3a9731f 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -450,6 +450,15 @@
"android.net.action.PROMPT_PARTIAL_CONNECTIVITY";
/**
+ * Clear DNS Cache Action: This is broadcast when networks have changed and old
+ * DNS entries should be cleared.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final String ACTION_CLEAR_DNS_CACHE = "android.net.action.CLEAR_DNS_CACHE";
+
+ /**
* Invalid tethering type.
* @see #startTethering(int, boolean, OnStartTetheringCallback)
* @hide
@@ -1193,7 +1202,8 @@
*
* @return a {@link Network} object for the current default network for the
* given UID or {@code null} if no default network is currently active
- * TODO: b/183465229 Cleanup getActiveNetworkForUid once b/165835257 is fixed
+ *
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.NETWORK_STACK)
@Nullable
diff --git a/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java b/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
index 5a76cd6..2bb006d 100644
--- a/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
+++ b/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
@@ -40,6 +40,23 @@
*/
@SystemApi
public final class OemNetworkPreferences implements Parcelable {
+ // Valid production preferences must be > 0, negative values reserved for testing
+ /**
+ * This preference is only to be used for testing and nothing else.
+ * Use only TRANSPORT_TEST transport networks.
+ * @hide
+ */
+ public static final int OEM_NETWORK_PREFERENCE_TEST_ONLY = -2;
+
+ /**
+ * This preference is only to be used for testing and nothing else.
+ * If an unmetered network is available, use it.
+ * Otherwise, if a network with the TRANSPORT_TEST transport is available, use it.
+ * Otherwise, use the general default network.
+ * @hide
+ */
+ public static final int OEM_NETWORK_PREFERENCE_TEST = -1;
+
/**
* Default in case this value is not set. Using it will result in an error.
*/
@@ -69,6 +86,12 @@
*/
public static final int OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY = 4;
+ /**
+ * The max allowed value for an OEM network preference.
+ * @hide
+ */
+ public static final int OEM_NETWORK_PREFERENCE_MAX = OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
+
@NonNull
private final Bundle mNetworkMappings;
@@ -96,7 +119,7 @@
@Override
public String toString() {
- return "OemNetworkPreferences{" + "mNetworkMappings=" + mNetworkMappings + '}';
+ return "OemNetworkPreferences{" + "mNetworkMappings=" + getNetworkPreferences() + '}';
}
@Override
@@ -185,6 +208,8 @@
/** @hide */
@IntDef(prefix = "OEM_NETWORK_PREFERENCE_", value = {
+ OEM_NETWORK_PREFERENCE_TEST_ONLY,
+ OEM_NETWORK_PREFERENCE_TEST,
OEM_NETWORK_PREFERENCE_UNINITIALIZED,
OEM_NETWORK_PREFERENCE_OEM_PAID,
OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK,
@@ -205,6 +230,10 @@
@NonNull
public static String oemNetworkPreferenceToString(@OemNetworkPreference int value) {
switch (value) {
+ case OEM_NETWORK_PREFERENCE_TEST_ONLY:
+ return "OEM_NETWORK_PREFERENCE_TEST_ONLY";
+ case OEM_NETWORK_PREFERENCE_TEST:
+ return "OEM_NETWORK_PREFERENCE_TEST";
case OEM_NETWORK_PREFERENCE_UNINITIALIZED:
return "OEM_NETWORK_PREFERENCE_UNINITIALIZED";
case OEM_NETWORK_PREFERENCE_OEM_PAID:
diff --git a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
index b0a9b95..397b0f9 100644
--- a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
+++ b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
@@ -165,9 +165,8 @@
}
private List<EmergencyNumber> getPromotedEmergencyNumbers(int categories) {
- // TODO(b/171542607): Use platform API when its bug is fixed.
- Map<Integer, List<EmergencyNumber>> allLists = filterEmergencyNumbersByCategories(
- mTelephonyManager.getEmergencyNumberList(), categories);
+ Map<Integer, List<EmergencyNumber>> allLists = mTelephonyManager.getEmergencyNumberList(
+ categories);
if (allLists == null || allLists.isEmpty()) {
Log.w(TAG, "Unable to retrieve emergency number lists!");
return new ArrayList<>();
@@ -212,28 +211,4 @@
}
return promotedEmergencyNumberLists.get(SubscriptionManager.getDefaultSubscriptionId());
}
-
- /**
- * Filter emergency numbers with categories.
- */
- private Map<Integer, List<EmergencyNumber>> filterEmergencyNumbersByCategories(
- Map<Integer, List<EmergencyNumber>> emergencyNumberList, int categories) {
- Map<Integer, List<EmergencyNumber>> filteredMap = new ArrayMap<>();
- if (emergencyNumberList == null) {
- return filteredMap;
- }
- for (Integer subscriptionId : emergencyNumberList.keySet()) {
- List<EmergencyNumber> allNumbersForSub = emergencyNumberList.get(
- subscriptionId);
- List<EmergencyNumber> numbersForCategoriesPerSub = new ArrayList<>();
- for (EmergencyNumber number : allNumbersForSub) {
- if (number.isInEmergencyServiceCategories(categories)) {
- numbersForCategoriesPerSub.add(number);
- }
- }
- filteredMap.put(
- subscriptionId, numbersForCategoriesPerSub);
- }
- return filteredMap;
- }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java
index 695b5b6..40339de 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java
@@ -23,6 +23,7 @@
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;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
@@ -126,7 +127,7 @@
List<EmergencyNumber> numbersForSubId = new ArrayList<>();
numbersForSubId.add(emergencyNumber);
numbers.put(subId, numbersForSubId);
- when(mTelephonyManager.getEmergencyNumberList()).thenReturn(numbers);
+ when(mTelephonyManager.getEmergencyNumberList(anyInt())).thenReturn(numbers);
when(emergencyNumber.getNumber()).thenReturn(TELEPHONY_EMERGENCY_NUMBER);
}
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 5a3298d..16937b7 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -176,6 +176,7 @@
<uses-permission android:name="android.permission.SET_TIME_ZONE" />
<uses-permission android:name="android.permission.DISABLE_HIDDEN_API_CHECKS" />
<uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" />
+ <uses-permission android:name="android.permission.BYPASS_ROLE_QUALIFICATION" />
<uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS" />
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
<uses-permission android:name="android.permission.STATUS_BAR" />
@@ -265,10 +266,11 @@
<!-- permissions required for CTS test - PhoneStateListenerTest -->
<uses-permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH" />
- <!-- Permissions required for ganting and logging -->
+ <!-- Permissions required for granting and logging -->
<uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
<uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
<uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"/>
+ <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
<!-- Permission required for CTS test - BatterySaverTest -->
<uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
diff --git a/packages/SystemUI/docs/falsing.md b/packages/SystemUI/docs/falsing.md
index 09215ac..0a60270 100644
--- a/packages/SystemUI/docs/falsing.md
+++ b/packages/SystemUI/docs/falsing.md
@@ -70,17 +70,17 @@
### Single Tap
-`FalsingManager#isFalseTap(boolean robustCheck, double falsePenalty)`. This
-method tells the `FalsingManager` that you want to validate a single tap. It
+`FalsingManager#isSimpleTape()`. This method
+performs a only very basic checking, checking that observed `MotionEvent`s are
+all within some small x & y region ("touch slop"). Useful for only the most simple of scenarios,
+you probably want `FalsingManager#isFalseTap` method for most cases.
+
+`FalsingManager#isFalseTap(@Penalty int penalty)`. This
+method tells the `FalsingManager` that you want to thoroughly validate a single tap. It
returns true if it thinks the tap should be rejected (i.e. the tap looks more
like a swipe) and false otherwise.
-`robustCheck` determines what heuristics are used. If set to false, the method
-performs a only very basic checking, checking that observed `MotionEvent`s are
-all within some small x & y region ("touch slop").
-
-When `robustCheck` is set to true, several more advanced rules are additionally
-applied:
+It runs through the following heuristics to validate a tap:
1. If the device recognizes a face (i.e. face-auth) the tap is **accepted**.
2. If the tap is the _second_ tap in recent history and looks like a valid Double Tap
@@ -90,19 +90,18 @@
4. Otherwise the tap is **accepted**.
All the above rules are applied only after first confirming the gesture does
-in fact look like a basic tap.
+in fact look like a simple tap.
-`falsePenalty` is a measure of how much the `HistoryTracker`'s belief should be
+`penalty` is a measure of how much the `HistoryTracker`'s belief should be
penalized in the event that the tap is rejected. This value is only used if
-`robustCheck` is set to true.
+the gesture fails to validate as a simple tap.
-A value of `0` means no change in belief. A value of `1` means a _very_ strong
-confidence in a false tap. In general, as a single tap on the screen is not
-verifiable, a small value should be supplied - on the order of `0.1`. Pass `0`
-if you don't want to penalize belief at all. Pass a higher value
-the earlier in the UX flow your interaction occurs. Once an owner is farther
-along in a UX flow (multiple taps or swipes), its safer to assume that a single
-accidental tap should cause less of a penalty.
+The `@FalsingManager.Penalty` values are fairly straightforward, but note that you
+should generally be choosing `LOW_PENALTY`. It is inherently difficult to know if a
+tap is truly false or not, so a single mis-tap should apply only a small penalty.
+If the owner is further along in a UX flow, and is still mis-tapping, it may make more
+sense to increase the penalty as mis-taps should be less likely to occur after
+several successful gestures.
### Double Tap
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
index 1fde6c9..4142e51 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
@@ -16,6 +16,7 @@
package com.android.systemui.plugins;
+import android.annotation.IntDef;
import android.net.Uri;
import android.view.MotionEvent;
@@ -24,6 +25,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* Interface that decides whether a touch on the phone was accidental. i.e. Pocket Dialing.
@@ -34,6 +37,20 @@
public interface FalsingManager {
int VERSION = 6;
+ int NO_PENALTY = 0;
+ int LOW_PENALTY = 1;
+ int MODERATE_PENALTY = 2;
+ int HIGH_PENALTY = 3;
+
+ @IntDef({
+ NO_PENALTY,
+ LOW_PENALTY,
+ MODERATE_PENALTY,
+ HIGH_PENALTY
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Penalty {}
+
void onSuccessfulUnlock();
boolean isUnlockingDisabled();
@@ -41,23 +58,31 @@
/** Returns true if the gesture should be rejected. */
boolean isFalseTouch(int interactionType);
+
/**
- * Returns true if the FalsingManager thinks the last gesure was not a valid tap.
+ * Does basic checking to see if gesture looks like a tap.
*
- * The first parameter, robustCheck, distinctly changes behavior. When set to false,
- * this method simply looks at the last gesture and returns whether it is a tap or not, (as
- * opposed to a swipe or other non-tap gesture). When set to true, a more thorough analysis
- * is performed that can include historical interactions and other contextual cues to see
+ * Only does the most basic of checks. No penalty is applied if this method returns false.
+ *
+ * For more robust analysis, use {@link #isFalseTap(int)}.
+ */
+ boolean isSimpleTap();
+
+ /**
+ * Returns true if the FalsingManager thinks the last gesture was not a valid tap.
+ *
+ * This method runs a more thorough analysis than the similar {@link #isSimpleTap()},
+ * that can include historical interactions and other contextual cues to see
* if the tap looks accidental.
*
- * Set robustCheck to true if you want to validate a tap for launching an action, like opening
- * a notification. Set to false if you simply want to know if the last gesture looked like a
- * tap.
+ * Use this method to validate a tap for launching an action, like opening
+ * a notification.
*
- * The second parameter, falsePenalty, indicates how much this should affect future gesture
- * classifications if this tap looks like a false.
+ * The only parameter, penalty, indicates how much this should affect future gesture
+ * classifications if this tap looks like a false. As single taps are hard to confirm as false
+ * or otherwise, a low penalty value is encouraged unless context indicates otherwise.
*/
- boolean isFalseTap(boolean robustCheck, double falsePenalty);
+ boolean isFalseTap(@Penalty int penalty);
/**
* Returns true if the last two gestures do not look like a double tap.
diff --git a/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml b/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml
new file mode 100644
index 0000000..bdd6270
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ 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">
+ <solid android:color="?android:attr/colorAccent" />
+ <corners android:radius="@dimen/ongoing_call_chip_corner_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_call_chip.xml b/packages/SystemUI/res/layout/ongoing_call_chip.xml
new file mode 100644
index 0000000..c90fc31
--- /dev/null
+++ b/packages/SystemUI/res/layout/ongoing_call_chip.xml
@@ -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.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/ongoing_call_chip"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/ongoing_appops_chip_height"
+ android:layout_gravity="center_vertical|start"
+ android:gravity="center_vertical"
+ android:background="@drawable/ongoing_call_chip_bg"
+ android:paddingStart="@dimen/ongoing_call_chip_side_padding"
+ android:paddingEnd="@dimen/ongoing_call_chip_side_padding"
+ android:visibility="gone"
+>
+
+ <ImageView
+ android:src="@*android:drawable/ic_phone"
+ android:layout_width="@dimen/ongoing_call_chip_icon_size"
+ android:layout_height="@dimen/ongoing_call_chip_icon_size"
+ android:paddingEnd="@dimen/ongoing_call_chip_icon_text_padding"
+ android:tint="?android:attr/colorPrimary"
+ />
+
+ <!-- TODO(b/183229367): The text in this view isn't quite centered within the chip. -->
+ <!-- TODO(b/183229367): This text view's width shouldn't change as the time increases. -->
+ <Chronometer
+ android:id="@+id/ongoing_call_chip_time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:gravity="center"
+ android:textAppearance="@android:style/TextAppearance.Material.Small"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
+ android:textColor="?android:attr/colorPrimary"
+ />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index f8db97d..8b244c7 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -82,6 +82,8 @@
android:gravity="center_vertical|start"
/>
+ <include layout="@layout/ongoing_call_chip" />
+
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/notification_icon_area"
android:layout_width="0dp"
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 4fdeb6f..cd2395e 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -29,4 +29,10 @@
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
+ <!-- Screenshots -->
+ <style name="LongScreenshotActivity" parent="@android:style/Theme.DeviceDefault.DayNight">
+ <item name="android:windowLightStatusBar">false</item>
+ <item name="android:windowLightNavigationBar">false</item>
+ </style>
+
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 237edf8..e834f3f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1442,4 +1442,11 @@
<dimen name="wallet_empty_state_corner_radius">24dp</dimen>
<dimen name="wallet_tile_card_view_height">32dp</dimen>
<dimen name="wallet_tile_card_view_width">50dp</dimen>
+
+ <!-- Ongoing call chip -->
+ <dimen name="ongoing_call_chip_side_padding">12dp</dimen>
+ <dimen name="ongoing_call_chip_icon_size">16dp</dimen>
+ <!-- The padding between the icon and the text. -->
+ <dimen name="ongoing_call_chip_icon_text_padding">4dp</dimen>
+ <dimen name="ongoing_call_chip_corner_radius">28dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index c39db94..bbf2048 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -49,7 +49,7 @@
<bool name="flag_charging_ripple">false</bool>
- <bool name="flag_ongoing_call_status_bar_chip">false</bool>
+ <bool name="flag_ongoing_call_status_bar_chip">true</bool>
<bool name="flag_smartspace">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index de14dbd..ba07829 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -623,6 +623,8 @@
<!-- Screenshots -->
<style name="LongScreenshotActivity" parent="@android:style/Theme.DeviceDefault.DayNight">
<item name="android:windowNoTitle">true</item>
+ <item name="android:windowLightStatusBar">true</item>
+ <item name="android:windowLightNavigationBar">true</item>
</style>
<!-- Privacy dialog -->
diff --git a/packages/SystemUI/src/com/android/systemui/Interpolators.java b/packages/SystemUI/src/com/android/systemui/Interpolators.java
index 2eba952..c23bcb8 100644
--- a/packages/SystemUI/src/com/android/systemui/Interpolators.java
+++ b/packages/SystemUI/src/com/android/systemui/Interpolators.java
@@ -40,6 +40,7 @@
new PathInterpolator(0.8f, 0f, 0.6f, 1f);
public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
+ public static final Interpolator SLOW_OUT_LINEAR_IN = new PathInterpolator(0.8f, 0f, 1f, 1f);
public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
public static final Interpolator LINEAR = new LinearInterpolator();
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 74f855b..994401d 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -25,24 +25,20 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.location.LocationManager;
import android.media.AudioManager;
import android.media.AudioRecordingConfiguration;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
-import android.provider.DeviceConfig;
+import android.permission.PermissionManager;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import androidx.annotation.WorkerThread;
-import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.Dumpable;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
@@ -83,19 +79,12 @@
private final Context mContext;
private final AppOpsManager mAppOps;
private final AudioManager mAudioManager;
- private final LocationManager mLocationManager;
private final IndividualSensorPrivacyController mSensorPrivacyController;
private final SystemClock mClock;
- // mLocationProviderPackages are cached and updated only occasionally
- private static final long LOCATION_PROVIDER_UPDATE_FREQUENCY_MS = 30000;
- private long mLastLocationProviderPackageUpdate;
- private List<String> mLocationProviderPackages;
-
private H mBGHandler;
private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>();
- private final PermissionFlagsCache mFlagsCache;
private boolean mListening;
private boolean mMicMuted;
private boolean mCameraDisabled;
@@ -124,7 +113,6 @@
Context context,
@Background Looper bgLooper,
DumpManager dumpManager,
- PermissionFlagsCache cache,
AudioManager audioManager,
IndividualSensorPrivacyController sensorPrivacyController,
BroadcastDispatcher dispatcher,
@@ -132,7 +120,6 @@
) {
mDispatcher = dispatcher;
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
- mFlagsCache = cache;
mBGHandler = new H(bgLooper);
final int numOps = OPS.length;
for (int i = 0; i < numOps; i++) {
@@ -143,7 +130,6 @@
mMicMuted = audioManager.isMicrophoneMute()
|| mSensorPrivacyController.isSensorBlocked(MICROPHONE);
mCameraDisabled = mSensorPrivacyController.isSensorBlocked(CAMERA);
- mLocationManager = context.getSystemService(LocationManager.class);
mContext = context;
mClock = clock;
dumpManager.registerDumpable(TAG, this);
@@ -308,102 +294,8 @@
return createdNew;
}
- /**
- * Does the app-op code refer to a user sensitive permission for the specified user id
- * and package. Only user sensitive permission should be shown to the user by default.
- *
- * @param appOpCode The code of the app-op.
- * @param uid The uid of the user.
- * @param packageName The name of the package.
- *
- * @return {@code true} iff the app-op item is user sensitive
- */
- private boolean isUserSensitive(int appOpCode, int uid, String packageName) {
- String permission = AppOpsManager.opToPermission(appOpCode);
- if (permission == null) {
- return false;
- }
- int permFlags = mFlagsCache.getPermissionFlags(permission,
- packageName, uid);
- return (permFlags & PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
- }
-
- /**
- * Does the app-op item refer to an operation that should be shown to the user.
- * Only specficic ops (like SYSTEM_ALERT_WINDOW) or ops that refer to user sensitive
- * permission should be shown to the user by default.
- *
- * @param item The item
- *
- * @return {@code true} iff the app-op item should be shown to the user
- */
- private boolean isUserVisible(AppOpItem item) {
- return isUserVisible(item.getCode(), item.getUid(), item.getPackageName());
- }
-
- /**
- * Checks if a package is the current location provider.
- *
- * <p>Data is cached to avoid too many calls into system server
- *
- * @param packageName The package that might be the location provider
- *
- * @return {@code true} iff the package is the location provider.
- */
- private boolean isLocationProvider(String packageName) {
- long now = System.currentTimeMillis();
-
- if (mLastLocationProviderPackageUpdate + LOCATION_PROVIDER_UPDATE_FREQUENCY_MS < now) {
- mLastLocationProviderPackageUpdate = now;
- mLocationProviderPackages = mLocationManager.getProviderPackages(
- LocationManager.FUSED_PROVIDER);
- }
-
- return mLocationProviderPackages.contains(packageName);
- }
-
- private boolean isSpeechRecognizerUsage(int opCode, String packageName) {
- if (AppOpsManager.OP_RECORD_AUDIO != opCode) {
- return false;
- }
-
- return packageName.equals(
- mContext.getString(R.string.config_systemSpeechRecognizer));
- }
-
- // TODO ntmyren: remove after teamfood is finished
- private boolean showSystemApps() {
- return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false);
- }
-
- /**
- * Does the app-op, uid and package name, refer to an operation that should be shown to the
- * user. Only specficic ops (like {@link AppOpsManager.OP_SYSTEM_ALERT_WINDOW}) or
- * ops that refer to user sensitive permission should be shown to the user by default.
- *
- * @param item The item
- *
- * @return {@code true} iff the app-op for should be shown to the user
- */
- private boolean isUserVisible(int appOpCode, int uid, String packageName) {
- // currently OP_SYSTEM_ALERT_WINDOW and OP_MONITOR_HIGH_POWER_LOCATION
- // does not correspond to a platform permission
- // which may be user sensitive, so for now always show it to the user.
- if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW
- || appOpCode == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION
- || appOpCode == AppOpsManager.OP_PHONE_CALL_CAMERA
- || appOpCode == AppOpsManager.OP_PHONE_CALL_MICROPHONE) {
- return true;
- }
- // TODO ntmyren: Replace this with more robust check if this moves beyond teamfood
- if (((showSystemApps() && !packageName.equals("android"))
- || appOpCode == AppOpsManager.OP_CAMERA && isLocationProvider(packageName))
- || isSpeechRecognizerUsage(appOpCode, packageName)) {
- return true;
- }
-
- return isUserSensitive(appOpCode, uid, packageName);
+ private boolean isUserVisible(String packageName) {
+ return PermissionManager.shouldShowPackageForIndicatorCached(mContext, packageName);
}
/**
@@ -438,7 +330,7 @@
AppOpItem item = mActiveItems.get(i);
if ((userId == UserHandle.USER_ALL
|| UserHandle.getUserId(item.getUid()) == userId)
- && isUserVisible(item) && !item.isDisabled()) {
+ && isUserVisible(item.getPackageName()) && !item.isDisabled()) {
list.add(item);
}
}
@@ -449,7 +341,7 @@
AppOpItem item = mNotedItems.get(i);
if ((userId == UserHandle.USER_ALL
|| UserHandle.getUserId(item.getUid()) == userId)
- && isUserVisible(item)) {
+ && isUserVisible(item.getPackageName())) {
list.add(item);
}
}
@@ -503,7 +395,7 @@
}
private void notifySuscribersWorker(int code, int uid, String packageName, boolean active) {
- if (mCallbacksByCode.contains(code) && isUserVisible(code, uid, packageName)) {
+ if (mCallbacksByCode.contains(code) && isUserVisible(packageName)) {
if (DEBUG) Log.d(TAG, "Notifying of change in package " + packageName);
for (Callback cb: mCallbacksByCode.get(code)) {
cb.onActiveStateChanged(code, uid, packageName, active);
diff --git a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt b/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt
deleted file mode 100644
index 3fd838b..0000000
--- a/packages/SystemUI/src/com/android/systemui/appops/PermissionFlagsCache.kt
+++ /dev/null
@@ -1,88 +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.systemui.appops
-
-import android.content.pm.PackageManager
-import android.os.UserHandle
-import androidx.annotation.WorkerThread
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.util.Assert
-import java.util.concurrent.Executor
-import javax.inject.Inject
-
-private data class PermissionFlagKey(
- val permission: String,
- val packageName: String,
- val uid: Int
-)
-
-/**
- * Cache for PackageManager's PermissionFlags.
- *
- * After a specific `{permission, package, uid}` has been requested, updates to it will be tracked,
- * and changes to the uid will trigger new requests (in the background).
- */
-@SysUISingleton
-class PermissionFlagsCache @Inject constructor(
- private val packageManager: PackageManager,
- @Background private val executor: Executor
-) : PackageManager.OnPermissionsChangedListener {
-
- private val permissionFlagsCache =
- mutableMapOf<Int, MutableMap<PermissionFlagKey, Int>>()
- private var listening = false
-
- override fun onPermissionsChanged(uid: Int) {
- executor.execute {
- // Only track those that we've seen before
- val keys = permissionFlagsCache.get(uid)
- if (keys != null) {
- keys.mapValuesTo(keys) {
- getFlags(it.key)
- }
- }
- }
- }
-
- /**
- * Retrieve permission flags from cache or PackageManager. There parameters will be passed
- * directly to [PackageManager].
- *
- * Calls to this method should be done from a background thread (though it will only be
- * enforced if the cache is not hit).
- */
- @WorkerThread
- fun getPermissionFlags(permission: String, packageName: String, uid: Int): Int {
- if (!listening) {
- listening = true
- packageManager.addOnPermissionsChangeListener(this)
- }
- val key = PermissionFlagKey(permission, packageName, uid)
- return permissionFlagsCache.getOrPut(uid, { mutableMapOf() }).get(key) ?: run {
- getFlags(key).also {
- Assert.isNotMainThread()
- permissionFlagsCache.get(uid)?.put(key, it)
- }
- }
- }
-
- private fun getFlags(key: PermissionFlagKey): Int {
- return packageManager.getPermissionFlags(key.permission, key.packageName,
- UserHandle.getUserHandleForUid(key.uid))
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 405151d..aa818bf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -37,6 +37,7 @@
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.Trace;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -243,6 +244,8 @@
}
// TODO: move isWithinSensorArea to UdfpsController.
if (udfpsView.isWithinSensorArea(event.getX(), event.getY())) {
+ Trace.beginAsyncSection(
+ "UdfpsController.mOnTouchListener#isWithinSensorArea", 1);
// The pointer that causes ACTION_DOWN is always at index 0.
// We need to persist its ID to track it during ACTION_MOVE that could include
// data for many other pointers because of multi-touch support.
@@ -269,6 +272,8 @@
minor, major, v);
final long sinceLastLog = SystemClock.elapsedRealtime() - mTouchLogTime;
if (!isFingerDown) {
+ Trace.endAsyncSection(
+ "UdfpsController.mOnTouchListener#isWithinSensorArea", 1);
onFingerDown((int) x, (int) y, minor, major);
Log.v(TAG, "onTouch | finger down: " + touchInfo);
mTouchLogTime = SystemClock.elapsedRealtime();
@@ -584,8 +589,11 @@
Log.w(TAG, "Null view in onFingerDown");
return;
}
- mView.startIllumination(() ->
- mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major));
+ Trace.beginAsyncSection("UdfpsController#startIllumination", 1);
+ mView.startIllumination(() -> {
+ Trace.endAsyncSection("UdfpsController#startIllumination", 1);
+ mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major);
+ });
}
// This method can be called from the UI thread.
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index 5bdc7a4..e2c62ed 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -218,18 +218,43 @@
}
@Override
- public boolean isFalseTap(boolean robustCheck, double falsePenalty) {
+ public boolean isSimpleTap() {
+ FalsingClassifier.Result result = mSingleTapClassifier.isTap(
+ mDataProvider.getRecentMotionEvents(), 0);
+ mPriorResults = Collections.singleton(result);
+
+ return !result.isFalse();
+ }
+
+ @Override
+ public boolean isFalseTap(@Penalty int penalty) {
if (skipFalsing()) {
return false;
}
+ double falsePenalty = 0;
+ switch(penalty) {
+ case NO_PENALTY:
+ falsePenalty = 0;
+ break;
+ case LOW_PENALTY:
+ falsePenalty = 0.1;
+ break;
+ case MODERATE_PENALTY:
+ falsePenalty = 0.3;
+ break;
+ case HIGH_PENALTY:
+ falsePenalty = 0.6;
+ break;
+ }
+
FalsingClassifier.Result singleTapResult =
mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents().isEmpty()
? mDataProvider.getPriorMotionEvents()
- : mDataProvider.getRecentMotionEvents());
+ : mDataProvider.getRecentMotionEvents(), falsePenalty);
mPriorResults = Collections.singleton(singleTapResult);
- if (!singleTapResult.isFalse() && robustCheck) {
+ if (!singleTapResult.isFalse()) {
if (mDataProvider.isJustUnlockedWithFace()) {
// Immediately pass if a face is detected.
mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(1));
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index 4dd8780..6a012eb 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -38,8 +38,10 @@
public static final int BOUNCER_UNLOCK = 8;
public static final int PULSE_EXPAND = 9;
public static final int BRIGHTNESS_SLIDER = 10;
- public static final int UDFPS_AUTHENTICATION = 11;
- public static final int DISABLED_UDFPS_AFFORDANCE = 12;
+ public static final int SHADE_DRAG = 11;
+ public static final int QS_COLLAPSE = 12;
+ public static final int UDFPS_AUTHENTICATION = 13;
+ public static final int DISABLED_UDFPS_AFFORDANCE = 14;
@IntDef({
QUICK_SETTINGS,
@@ -53,6 +55,9 @@
BOUNCER_UNLOCK,
PULSE_EXPAND,
BRIGHTNESS_SLIDER,
+ SHADE_DRAG,
+ QS_COLLAPSE,
+ BRIGHTNESS_SLIDER,
UDFPS_AUTHENTICATION,
DISABLED_UDFPS_AFFORDANCE
})
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
index 6a70622..a4e1637 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
@@ -22,6 +22,9 @@
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VELOCITY_TO_DISTANCE;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VERTICAL_FLING_THRESHOLD_IN;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VERTICAL_SWIPE_THRESHOLD_IN;
+import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
+import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
+import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
import android.provider.DeviceConfig;
import android.view.MotionEvent;
@@ -148,7 +151,9 @@
Result calculateFalsingResult(
@Classifier.InteractionType int interactionType,
double historyBelief, double historyConfidence) {
- if (interactionType == Classifier.BRIGHTNESS_SLIDER
+ if (interactionType == BRIGHTNESS_SLIDER
+ || interactionType == SHADE_DRAG
+ || interactionType == QS_COLLAPSE
|| interactionType == Classifier.UDFPS_AUTHENTICATION
|| interactionType == Classifier.DISABLED_UDFPS_AFFORDANCE) {
return Result.passed(0);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java
index e7c9d18..b6d529d 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java
@@ -66,13 +66,13 @@
public boolean isDoubleTap(List<MotionEvent> firstEvents, List<MotionEvent> secondEvents,
StringBuilder reason) {
- Result firstTap = mSingleTapClassifier.isTap(firstEvents);
+ Result firstTap = mSingleTapClassifier.isTap(firstEvents, 0.5);
if (firstTap.isFalse()) {
reason.append("First gesture is not a tap. ").append(firstTap.getReason());
return false;
}
- Result secondTap = mSingleTapClassifier.isTap(secondEvents);
+ Result secondTap = mSingleTapClassifier.isTap(secondEvents, 0.5);
if (secondTap.isFalse()) {
reason.append("Second gesture is not a tap. ").append(secondTap.getReason());
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
index d39f124..e557773 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
@@ -32,7 +32,7 @@
*/
public class FalsingManagerFake implements FalsingManager {
private boolean mIsFalseTouch;
- private boolean mIsFalseTap;
+ private boolean mIsSimpleTap;
private boolean mIsFalseDoubleTap;
private boolean mIsUnlockingDisabled;
private boolean mIsClassiferEnabled;
@@ -67,12 +67,12 @@
return mIsFalseTouch;
}
- public void setFalseRobustTap(boolean falseRobustTap) {
+ public void setFalseTap(boolean falseRobustTap) {
mIsFalseRobustTap = falseRobustTap;
}
- public void setFalseTap(boolean falseTap) {
- mIsFalseTap = falseTap;
+ public void setSimpleTap(boolean isSimpleTape) {
+ mIsSimpleTap = isSimpleTape;
}
public void setFalseDoubleTap(boolean falseDoubleTap) {
@@ -80,8 +80,13 @@
}
@Override
- public boolean isFalseTap(boolean robustCheck, double falsePenalty) {
- return robustCheck ? mIsFalseRobustTap : mIsFalseTap;
+ public boolean isSimpleTap() {
+ return mIsSimpleTap;
+ }
+
+ @Override
+ public boolean isFalseTap(@Penalty int penalty) {
+ return mIsFalseRobustTap;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 9c29f27..1723291 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -131,8 +131,13 @@
}
@Override
- public boolean isFalseTap(boolean robustCheck, double falsePenalty) {
- return mInternalFalsingManager.isFalseTap(robustCheck, falsePenalty);
+ public boolean isSimpleTap() {
+ return mInternalFalsingManager.isSimpleTap();
+ }
+
+ @Override
+ public boolean isFalseTap(@Penalty int penalty) {
+ return mInternalFalsingManager.isFalseTap(penalty);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
index 6f80010..3bc24c7 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
@@ -18,6 +18,7 @@
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD;
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
+import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import android.provider.DeviceConfig;
@@ -116,7 +117,8 @@
Result calculateFalsingResult(
@Classifier.InteractionType int interactionType,
double historyBelief, double historyConfidence) {
- if (interactionType == QUICK_SETTINGS || interactionType == BRIGHTNESS_SLIDER) {
+ if (interactionType == QUICK_SETTINGS || interactionType == BRIGHTNESS_SLIDER
+ || interactionType == QS_COLLAPSE) {
return Result.passed(0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java
index 68a9e5f..bd6fbfb 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java
@@ -42,11 +42,11 @@
Result calculateFalsingResult(
@Classifier.InteractionType int interactionType,
double historyBelief, double historyConfidence) {
- return isTap(getRecentMotionEvents());
+ return isTap(getRecentMotionEvents(), 0.5);
}
/** Given a list of {@link android.view.MotionEvent}'s, returns true if the look like a tap. */
- public Result isTap(List<MotionEvent> motionEvents) {
+ public Result isTap(List<MotionEvent> motionEvents, double falsePenalty) {
if (motionEvents.isEmpty()) {
return falsed(0, "no motion events");
}
@@ -60,13 +60,13 @@
+ Math.abs(event.getX() - downX)
+ "vs "
+ mTouchSlop;
- return falsed(0.5, reason);
+ return falsed(falsePenalty, reason);
} else if (Math.abs(event.getY() - downY) >= mTouchSlop) {
reason = "dY too big for a tap: "
+ Math.abs(event.getY() - downY)
+ " vs "
+ mTouchSlop;
- return falsed(0.5, reason);
+ return falsed(falsePenalty, reason);
}
}
return Result.passed(0);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
index 50e94b3..1042516 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
@@ -23,8 +23,10 @@
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
import static com.android.systemui.classifier.Classifier.PULSE_EXPAND;
+import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
+import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
import static com.android.systemui.classifier.Classifier.UNLOCK;
import javax.inject.Inject;
@@ -77,6 +79,12 @@
case RIGHT_AFFORDANCE: // Swiping from the bottom right corner for camera or similar.
wrongDirection = right || !up;
break;
+ case SHADE_DRAG:
+ wrongDirection = !vertical;
+ break;
+ case QS_COLLAPSE:
+ wrongDirection = !vertical || !up;
+ break;
default:
wrongDirection = true;
break;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
index d9197ef..e1349f2 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
@@ -21,6 +21,7 @@
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE;
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
+import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
import android.graphics.Point;
import android.provider.DeviceConfig;
@@ -88,7 +89,7 @@
Result calculateFalsingResult(
@Classifier.InteractionType int interactionType,
double historyBelief, double historyConfidence) {
- if (interactionType == BRIGHTNESS_SLIDER) {
+ if (interactionType == BRIGHTNESS_SLIDER || interactionType == SHADE_DRAG) {
return Result.passed(0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 5f6d95f..0fbe1f7 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -319,6 +319,7 @@
CharSequence content = (isMissedCall && !hasMessageText)
? context.getString(R.string.missed_call) : message.getText();
Uri dataUri = message != null ? message.getDataUri() : null;
+ if (DEBUG) Log.d(TAG, "Notification message has text: " + hasMessageText);
return tile
.toBuilder()
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 96fbe69..3bc91bc 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -391,6 +391,7 @@
statusText = getStatusTextByType(status.getActivity());
}
views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
+ views.setViewVisibility(R.id.messages_count, View.GONE);
setMaxLines(views);
// Secondary text color for statuses.
TypedValue typedValue = new TypedValue();
@@ -530,6 +531,7 @@
if (mLayoutSize == LAYOUT_SMALL) {
views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
views.setViewVisibility(R.id.name, View.GONE);
+ views.setViewVisibility(R.id.messages_count, View.GONE);
} else {
views.setViewVisibility(R.id.predefined_icon, View.GONE);
views.setViewVisibility(R.id.name, View.VISIBLE);
@@ -544,6 +546,7 @@
if (mLayoutSize == LAYOUT_SMALL) {
views.setViewVisibility(R.id.name, View.VISIBLE);
views.setViewVisibility(R.id.predefined_icon, View.GONE);
+ views.setViewVisibility(R.id.messages_count, View.GONE);
}
String status = PeopleSpaceUtils.getLastInteractionString(mContext,
mTile.getLastInteractionTimestamp());
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 5be2d4a..117be47 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -285,7 +285,7 @@
updateTilesByUri(key, sbn, action);
}
} catch (Exception e) {
- Log.e(TAG, "Exception: " + e);
+ Log.e(TAG, "Throwing exception: " + e);
}
}
@@ -498,7 +498,7 @@
if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) {
if (DEBUG) Log.i(TAG, "Adding notification to storage, appWidgetId: " + appWidgetId);
storedTile = augmentTileFromNotification(mContext, storedTile, sbn);
- } else if (storedTile.getNotificationKey().equals(sbn.getKey())) {
+ } else if (Objects.equals(storedTile.getNotificationKey(), sbn.getKey())) {
if (DEBUG) {
Log.i(TAG, "Removing notification from storage, appWidgetId: " + appWidgetId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index 52e05a4..3467838 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -34,6 +34,7 @@
import com.android.systemui.R;
import com.android.systemui.globalactions.GlobalActionsDialogLite;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.MultiUserSwitch;
@@ -62,6 +63,7 @@
private final QuickQSPanelController mQuickQSPanelController;
private final TunerService mTunerService;
private final MetricsLogger mMetricsLogger;
+ private final FalsingManager mFalsingManager;
private final SettingsButton mSettingsButton;
private final TextView mBuildText;
private final View mEdit;
@@ -83,8 +85,9 @@
private final View.OnClickListener mSettingsOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
- // Don't do anything until view are unhidden
- if (!mExpanded) {
+ // Don't do anything until views are unhidden. Don't do anything if the tap looks
+ // suspicious.
+ if (!mExpanded || mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
return;
}
@@ -132,7 +135,7 @@
DeviceProvisionedController deviceProvisionedController, UserTracker userTracker,
QSPanelController qsPanelController, QSDetailDisplayer qsDetailDisplayer,
QuickQSPanelController quickQSPanelController,
- TunerService tunerService, MetricsLogger metricsLogger,
+ TunerService tunerService, MetricsLogger metricsLogger, FalsingManager falsingManager,
@Named(PM_LITE_ENABLED) boolean showPMLiteButton,
GlobalActionsDialogLite globalActionsDialog) {
super(view);
@@ -146,6 +149,7 @@
mQuickQSPanelController = quickQSPanelController;
mTunerService = tunerService;
mMetricsLogger = metricsLogger;
+ mFalsingManager = falsingManager;
mSettingsButton = mView.findViewById(R.id.settings_button);
mBuildText = mView.findViewById(R.id.build);
@@ -184,9 +188,13 @@
return false;
});
- mEdit.setOnClickListener(view ->
- mActivityStarter.postQSRunnableDismissingKeyguard(() ->
- mQsPanelController.showEdit(view)));
+ mEdit.setOnClickListener(view -> {
+ if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ return;
+ }
+ mActivityStarter.postQSRunnableDismissingKeyguard(() ->
+ mQsPanelController.showEdit(view));
+ });
mMultiUserSwitch.setQSDetailDisplayer(mQsDetailDisplayer);
mQsPanelController.setFooterPageIndicator(mPageIndicator);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 1cb2683..375c79f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -279,7 +279,7 @@
mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_CLICK, 0, getMetricsSpec(),
getInstanceId());
mQSLogger.logTileClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
- if (!mFalsingManager.isFalseTap(true, 0.1)) {
+ if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
mHandler.sendEmptyMessage(H.CLICK);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
index 7a8b2c6..b953323 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
@@ -118,7 +118,11 @@
override fun setClickable(clickable: Boolean) {
super.setClickable(clickable)
background = if (clickable && mShowRippleEffect) {
- mTileBackground
+ mRipple?.also {
+ // In case that the colorBackgroundDrawable was used as the background, make sure
+ // it has the correct callback instead of null
+ colorBackgroundDrawable?.callback = it
+ }
} else {
colorBackgroundDrawable
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 7ef88bb..5cebf7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -142,9 +142,9 @@
}
return true;
case MotionEvent.ACTION_UP:
- if (!mFalsingManager.isUnlockingDisabled() && !isFalseTouch()
- && mDragDownCallback.onDraggedDown(mStartingChild,
- (int) (y - mInitialTouchY))) {
+ if (!mFalsingManager.isUnlockingDisabled() && mDragDownCallback.canDragDown()
+ && !isFalseTouch()) {
+ mDragDownCallback.onDraggedDown(mStartingChild, (int) (y - mInitialTouchY));
if (mStartingChild == null) {
cancelExpansion();
} else {
@@ -263,7 +263,10 @@
/**
* @return true if the interaction is accepted, false if it should be cancelled
*/
- boolean onDraggedDown(View startingChild, int dragLengthY);
+ boolean canDragDown();
+
+ /** Call when a view has been dragged. */
+ void onDraggedDown(View startingChild, int dragLengthY);
void onDragDownReset();
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 14c73b5..20383fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -63,6 +63,7 @@
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.time.SystemClock;
import com.android.wm.shell.bubbles.Bubbles;
import java.util.Optional;
@@ -234,9 +235,11 @@
@Provides
@SysUISingleton
static OngoingCallController provideOngoingCallController(
- CommonNotifCollection notifCollection, FeatureFlags featureFlags) {
+ CommonNotifCollection notifCollection,
+ FeatureFlags featureFlags,
+ SystemClock systemClock) {
OngoingCallController ongoingCallController =
- new OngoingCallController(notifCollection, featureFlags);
+ new OngoingCallController(notifCollection, featureFlags, systemClock);
ongoingCallController.init();
return ongoingCallController;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 18e5ead..17f70cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -720,6 +720,10 @@
mCurrentAppearInterpolator = mSlowOutFastInInterpolator;
mCurrentAlphaInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
targetValue = 1.0f;
+ if (!mIsHeadsUpAnimation && isChildInGroup()) {
+ // slower fade in of children to avoid visibly overlapping with other children
+ mCurrentAlphaInterpolator = Interpolators.SLOW_OUT_LINEAR_IN;
+ }
} else {
mCurrentAppearInterpolator = Interpolators.FAST_OUT_SLOW_IN;
mCurrentAlphaInterpolator = mSlowOutLinearInInterpolator;
@@ -819,6 +823,10 @@
if (mIsHeadsUpAnimation && !mIsAppearing) {
startWidthFraction = 0;
}
+ if (mIsAppearing && !mIsHeadsUpAnimation && isChildInGroup()) {
+ // Children in a group (when not heads up) should simply fade in.
+ startWidthFraction = 1;
+ }
float width = MathUtils.lerp(startWidthFraction, 1.0f, 1.0f - widthFraction)
* getWidth();
float left;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
index 3f7b8af..edd97af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
@@ -125,7 +125,7 @@
result = mNotificationTapHelper.onTouchEvent(ev, mView.getActualHeight());
} else if (ev.getAction() == MotionEvent.ACTION_UP) {
// If this is a false tap, capture the even so it doesn't result in a click.
- return mFalsingManager.isFalseTap(true, 0.1);
+ return mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY);
}
return result;
}
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 207a894..2ada281 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
@@ -881,7 +881,7 @@
// Other parts of the system may intercept and handle all the falsing.
// Otherwise, if we see motion and follow-on events, try to classify them as a tap.
if (ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
- mFalsingManager.isFalseTap(true, 0.3);
+ mFalsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY);
}
return super.onInterceptTouchEvent(ev);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index ad06e7d..b28cc14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -5499,9 +5499,16 @@
@ShadeViewRefactor(RefactorComponent.INPUT)
private final DragDownCallback mDragDownCallback = new DragDownCallback() {
+ @Override
+ public boolean canDragDown() {
+ return mStatusBarState == StatusBarState.KEYGUARD
+ && (mController.hasActiveNotifications() || mKeyguardMediaControllorVisible)
+ || mController.isInLockedDownShade();
+ }
+
/* Only ever called as a consequence of a lockscreen expansion gesture. */
@Override
- public boolean onDraggedDown(View startingChild, int dragLengthY) {
+ public void onDraggedDown(View startingChild, int dragLengthY) {
boolean canDragDown =
mController.hasActiveNotifications() || mKeyguardMediaControllorVisible;
if (mStatusBarState == StatusBarState.KEYGUARD && canDragDown) {
@@ -5519,16 +5526,10 @@
row.onExpandedByGesture(true /* drag down is always an open */);
}
}
-
- return true;
} else if (mController.isInLockedDownShade()) {
mStatusbarStateController.setLeaveOpenOnKeyguardHide(true);
mStatusBar.dismissKeyguardThenExecute(() -> false /* dismissAction */,
null /* cancelRunnable */, false /* afterKeyguardGone */);
- return true;
- } else {
- // abort gesture.
- return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index ce7b397..7776e69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -59,10 +59,12 @@
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
+import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.KeyguardMediaController;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
@@ -143,6 +145,7 @@
private final ZenModeController mZenModeController;
private final MetricsLogger mMetricsLogger;
private final FalsingCollector mFalsingCollector;
+ private final FalsingManager mFalsingManager;
private final Resources mResources;
private final NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
private final ScrimController mScrimController;
@@ -556,6 +559,7 @@
NotificationLockscreenUserManager lockscreenUserManager,
MetricsLogger metricsLogger,
FalsingCollector falsingCollector,
+ FalsingManager falsingManager,
@Main Resources resources,
NotificationSwipeHelper.Builder notificationSwipeHelperBuilder,
StatusBar statusBar,
@@ -589,6 +593,7 @@
mLockscreenUserManager = lockscreenUserManager;
mMetricsLogger = metricsLogger;
mFalsingCollector = falsingCollector;
+ mFalsingManager = falsingManager;
mResources = resources;
mNotificationSwipeHelperBuilder = notificationSwipeHelperBuilder;
mStatusBar = statusBar;
@@ -1614,6 +1619,9 @@
}
}
if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+ // Ensure the falsing manager records the touch. we don't do anything with it
+ // at the moment.
+ mFalsingManager.isFalseTouch(Classifier.SHADE_DRAG);
mView.setCheckForLeaveBehind(true);
}
traceJankOnTouchEvent(ev.getActionMasked(), scrollerWantsIt);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index f64a0e0..9d1c609 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -38,6 +38,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
import com.android.systemui.statusbar.policy.EncryptionHelper;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.NetworkController;
@@ -67,6 +68,7 @@
private NetworkController mNetworkController;
private LinearLayout mSystemIconArea;
private View mClockView;
+ private ViewGroup mOngoingCallChip;
private View mNotificationIconAreaInner;
private View mCenteredIconArea;
private int mDisabled1;
@@ -86,6 +88,20 @@
}
};
+ private final OngoingCallListener mOngoingCallListener = new OngoingCallListener() {
+ @Override
+ public void onOngoingCallStarted(boolean animate) {
+ disable(getContext().getDisplayId(), mDisabled1, mDisabled2, animate);
+ animateShow(mOngoingCallChip, animate);
+ }
+
+ @Override
+ public void onOngoingCallEnded(boolean animate) {
+ disable(getContext().getDisplayId(), mDisabled1, mDisabled2, animate);
+ animateHiddenState(mOngoingCallChip, View.GONE, animate);
+ }
+ };
+
@Inject
public CollapsedStatusBarFragment(OngoingCallController ongoingCallController) {
mOngoingCallController = ongoingCallController;
@@ -142,6 +158,7 @@
super.onResume();
mCommandQueue.addCallback(this);
mStatusBarStateController.addCallback(this);
+ mOngoingCallController.addCallback(mOngoingCallListener);
}
@Override
@@ -149,6 +166,7 @@
super.onPause();
mCommandQueue.removeCallback(this);
mStatusBarStateController.removeCallback(this);
+ mOngoingCallController.removeCallback(mOngoingCallListener);
}
@Override
@@ -179,6 +197,8 @@
}
statusBarCenteredIconArea.addView(mCenteredIconArea);
+ initOngoingCallChip();
+
// Default to showing until we know otherwise.
showNotificationIconArea(false);
}
@@ -228,6 +248,10 @@
state |= DISABLE_CLOCK;
}
+ if (mOngoingCallController.getHasOngoingCall()) {
+ state |= DISABLE_NOTIFICATION_ICONS;
+ }
+
if (!mKeyguardStateController.isLaunchTransitionFadingAway()
&& !mKeyguardStateController.isKeyguardFadingAway()
&& shouldHideNotificationIcons()
@@ -395,6 +419,11 @@
}
}
+ private void initOngoingCallChip() {
+ mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip);
+ mOngoingCallController.setChipView(mOngoingCallChip);
+ }
+
@Override
public void onStateChanged(int newState) {
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 364b532..610369b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -24,6 +24,7 @@
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
+import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
@@ -1450,11 +1451,7 @@
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
trackMovement(event);
- if (mQsTracking) {
- flingQsWithCurrentVelocity(y,
- event.getActionMasked() == MotionEvent.ACTION_CANCEL);
- mQsTracking = false;
- }
+ mQsTracking = false;
break;
}
return false;
@@ -1526,10 +1523,17 @@
private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) {
float vel = getCurrentQSVelocity();
- final boolean expandsQs = flingExpandsQs(vel);
+ boolean expandsQs = flingExpandsQs(vel);
if (expandsQs) {
- logQsSwipeDown(y);
+ if (mFalsingManager.isUnlockingDisabled() || isFalseTouch(QUICK_SETTINGS)) {
+ expandsQs = false;
+ } else {
+ logQsSwipeDown(y);
+ }
+ } else if (vel < 0) {
+ mFalsingManager.isFalseTouch(QS_COLLAPSE);
}
+
flingSettings(vel, expandsQs && !isCancelMotionEvent ? FLING_EXPAND : FLING_COLLAPSE);
}
@@ -1545,9 +1549,6 @@
}
private boolean flingExpandsQs(float vel) {
- if (mFalsingManager.isUnlockingDisabled() || isFalseTouch(QUICK_SETTINGS)) {
- return false;
- }
if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
return getQsExpansionFraction() > 0.5f;
} else {
@@ -1556,9 +1557,6 @@
}
private boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
- if (!mKeyguardAffordanceHelperCallback.needsAntiFalsing()) {
- return false;
- }
if (mFalsingManager.isClassifierEnabled()) {
return mFalsingManager.isFalseTouch(interactionType);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java
index 66df936..95ecd3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java
@@ -64,7 +64,7 @@
mTrackTouch = event.getY() <= maxTouchableHeight;
break;
case MotionEvent.ACTION_MOVE:
- if (mTrackTouch && mFalsingManager.isFalseTap(false, 0)) {
+ if (mTrackTouch && !mFalsingManager.isSimpleTap()) {
makeInactive();
mTrackTouch = false;
}
@@ -78,10 +78,10 @@
// 1) See if we have confidence that we can activate after a single tap.
// 2) Else, see if it looks like a tap at all and check for a double-tap.
- if (!mFalsingManager.isFalseTap(true, 0)) {
+ if (!mFalsingManager.isFalseTap(FalsingManager.NO_PENALTY)) {
makeInactive();
return mDoubleTapListener.onDoubleTap();
- } else if (!mFalsingManager.isFalseTap(false, 0)) {
+ } else if (mFalsingManager.isSimpleTap()) {
if (mSlideBackListener != null && mSlideBackListener.onSlideBack()) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 77abe79..936e289 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -18,6 +18,7 @@
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
+import static com.android.systemui.classifier.Classifier.GENERIC;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.UNLOCK;
@@ -430,9 +431,9 @@
mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp);
mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_UNLOCK);
}
- @Classifier.InteractionType int interactionType = vel > 0
- ? QUICK_SETTINGS : (
- mKeyguardStateController.canDismissLockScreen()
+ @Classifier.InteractionType int interactionType = vel == 0 ? GENERIC
+ : vel > 0 ? QUICK_SETTINGS
+ : (mKeyguardStateController.canDismissLockScreen()
? UNLOCK : BOUNCER_UNLOCK);
fling(vel, expand, isFalseTouch(x, y, interactionType));
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 f1f34dc..c37ddd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -681,6 +681,9 @@
new FalsingManager.FalsingBeliefListener() {
@Override
public void onFalse() {
+ // Hides quick settings.
+ mNotificationPanelViewController.resetViews(true);
+ // Hides bouncer and quick-quick settings.
mStatusBarKeyguardViewManager.reset(true);
}
};
@@ -1822,7 +1825,7 @@
}
public boolean isFalsingThresholdNeeded() {
- return mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
+ return true;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 60d3ea3..b55db6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -19,11 +19,16 @@
import android.app.Notification
import android.app.Notification.CallStyle.CALL_TYPE_ONGOING
import android.util.Log
+import android.view.ViewGroup
+import android.widget.Chronometer
+import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.FeatureFlags
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
/**
@@ -32,19 +37,39 @@
@SysUISingleton
class OngoingCallController @Inject constructor(
private val notifCollection: CommonNotifCollection,
- private val featureFlags: FeatureFlags
-) {
+ private val featureFlags: FeatureFlags,
+ private val systemClock: SystemClock
+) : CallbackController<OngoingCallListener> {
+
+ var hasOngoingCall = false
+ private set
+ private var chipView: ViewGroup? = null
+
+ private val mListeners: MutableList<OngoingCallListener> = mutableListOf()
private val notifListener = object : NotifCollectionListener {
override fun onEntryUpdated(entry: NotificationEntry) {
- if (isOngoingCallNotification(entry) && DEBUG) {
- Log.d(TAG, "Ongoing call notification updated")
+ if (isOngoingCallNotification(entry)) {
+ val timeView = chipView?.findViewById<Chronometer>(R.id.ongoing_call_chip_time)
+ if (timeView != null) {
+ hasOngoingCall = true
+ val callStartTime = entry.sbn.notification.`when`
+ timeView.base = callStartTime -
+ System.currentTimeMillis() +
+ systemClock.elapsedRealtime()
+ timeView.start()
+ mListeners.forEach { l -> l.onOngoingCallStarted(animate = true) }
+ } else if (DEBUG) {
+ Log.w(TAG, "Ongoing call chip view could not be found; " +
+ "Not displaying chip in status bar")
+ }
}
}
override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
- if (isOngoingCallNotification(entry) && DEBUG) {
- Log.d(TAG, "Ongoing call notification removed")
+ if (isOngoingCallNotification(entry)) {
+ hasOngoingCall = false
+ mListeners.forEach { l -> l.onOngoingCallEnded(animate = true) }
}
}
}
@@ -54,6 +79,24 @@
notifCollection.addCollectionListener(notifListener)
}
}
+
+ fun setChipView(chipView: ViewGroup?) {
+ this.chipView = chipView
+ }
+
+ override fun addCallback(listener: OngoingCallListener) {
+ synchronized(mListeners) {
+ if (!mListeners.contains(listener)) {
+ mListeners.add(listener)
+ }
+ }
+ }
+
+ override fun removeCallback(listener: OngoingCallListener) {
+ synchronized(mListeners) {
+ mListeners.remove(listener)
+ }
+ }
}
private fun isOngoingCallNotification(entry: NotificationEntry): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallListener.kt
new file mode 100644
index 0000000..7c583a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallListener.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.phone.ongoingcall
+
+/** A listener that's notified when an ongoing call is started or ended. */
+interface OngoingCallListener {
+ /** Called when an ongoing call is started. */
+ fun onOngoingCallStarted(animate: Boolean)
+
+ /** Called when an ongoing call is ended. */
+ fun onOngoingCallEnded(animate: Boolean)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 8252ff4..3f0831c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -24,6 +24,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -49,6 +50,7 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
@@ -71,6 +73,7 @@
public class AppOpsControllerTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test";
private static final String TEST_ATTRIBUTION_NAME = "attribution";
+ private static final String SYSTEM_PKG = "android";
private static final int TEST_UID = UserHandle.getUid(0, 0);
private static final int TEST_UID_OTHER = UserHandle.getUid(1, 0);
private static final int TEST_UID_NON_USER_SENSITIVE = UserHandle.getUid(2, 0);
@@ -84,8 +87,6 @@
@Mock
private DumpManager mDumpManager;
@Mock
- private PermissionFlagsCache mFlagsCache;
- @Mock
private PackageManager mPackageManager;
@Mock
private IndividualSensorPrivacyController mSensorPrivacyController;
@@ -101,22 +102,19 @@
private AppOpsControllerImpl mController;
private TestableLooper mTestableLooper;
+ private String mExemptedRolePkgName;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
+ mExemptedRolePkgName = getContext().getString(R.string.config_systemUiIntelligence);
getContext().addMockSystemService(AppOpsManager.class, mAppOpsManager);
// All permissions of TEST_UID and TEST_UID_OTHER are user sensitive. None of
// TEST_UID_NON_USER_SENSITIVE are user sensitive.
getContext().setMockPackageManager(mPackageManager);
- when(mFlagsCache.getPermissionFlags(anyString(), anyString(), eq(TEST_UID))).thenReturn(
- PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
- when(mFlagsCache.getPermissionFlags(anyString(), anyString(), eq(TEST_UID_OTHER)))
- .thenReturn(PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED);
- when(mFlagsCache.getPermissionFlags(anyString(), anyString(),
- eq(TEST_UID_NON_USER_SENSITIVE))).thenReturn(0);
doAnswer((invocation) -> mRecordingCallback = invocation.getArgument(0))
.when(mAudioManager).registerAudioRecordingCallback(any(), any());
@@ -135,7 +133,6 @@
mContext,
mTestableLooper.getLooper(),
mDumpManager,
- mFlagsCache,
mAudioManager,
mSensorPrivacyController,
mDispatcher,
@@ -242,18 +239,26 @@
}
@Test
- public void nonUserSensitiveOpsAreIgnored() {
+ public void systemAndExemptedRolesAreIgnored() {
+ assumeFalse(mExemptedRolePkgName == null || mExemptedRolePkgName.equals(""));
+
mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
- TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true);
+ TEST_UID_NON_USER_SENSITIVE, mExemptedRolePkgName, true);
+ assertEquals(0, mController.getActiveAppOpsForUser(
+ UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size());
+ mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
+ TEST_UID_NON_USER_SENSITIVE, SYSTEM_PKG, true);
assertEquals(0, mController.getActiveAppOpsForUser(
UserHandle.getUserId(TEST_UID_NON_USER_SENSITIVE)).size());
}
@Test
- public void nonUserSensitiveOpsNotNotified() {
+ public void exemptedRoleNotNotified() {
+ assumeFalse(mExemptedRolePkgName == null || mExemptedRolePkgName.equals(""));
+
mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO}, mCallback);
mController.onOpActiveChanged(AppOpsManager.OP_RECORD_AUDIO,
- TEST_UID_NON_USER_SENSITIVE, TEST_PACKAGE_NAME, true);
+ TEST_UID_NON_USER_SENSITIVE, mExemptedRolePkgName, true);
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt
deleted file mode 100644
index 0fb0ce0..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/PermissionFlagsCacheTest.kt
+++ /dev/null
@@ -1,145 +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.systemui.appops
-
-import android.content.pm.PackageManager
-import android.os.UserHandle
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.time.FakeSystemClock
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertNotEquals
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyString
-import org.mockito.Mock
-import org.mockito.Mockito.`when`
-import org.mockito.Mockito.never
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class PermissionFlagsCacheTest : SysuiTestCase() {
-
- companion object {
- const val TEST_PERMISSION = "test_permission"
- const val TEST_PACKAGE = "test_package"
- const val TEST_UID1 = 1000
- const val TEST_UID2 = UserHandle.PER_USER_RANGE + 1000
- }
-
- @Mock
- private lateinit var packageManager: PackageManager
-
- private lateinit var executor: FakeExecutor
- private lateinit var flagsCache: PermissionFlagsCache
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- executor = FakeExecutor(FakeSystemClock())
-
- flagsCache = PermissionFlagsCache(packageManager, executor)
- executor.runAllReady()
- }
-
- @Test
- fun testNotListeningByDefault() {
- verify(packageManager, never()).addOnPermissionsChangeListener(any())
- }
-
- @Test
- fun testGetCorrectFlags() {
- `when`(packageManager.getPermissionFlags(anyString(), anyString(), any())).thenReturn(0)
- `when`(packageManager.getPermissionFlags(
- TEST_PERMISSION,
- TEST_PACKAGE,
- UserHandle.getUserHandleForUid(TEST_UID1))
- ).thenReturn(1)
-
- assertEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
- assertNotEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID2))
- }
-
- @Test
- fun testFlagIsCached() {
- flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
-
- flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
-
- verify(packageManager, times(1)).getPermissionFlags(
- TEST_PERMISSION,
- TEST_PACKAGE,
- UserHandle.getUserHandleForUid(TEST_UID1)
- )
- }
-
- @Test
- fun testListeningAfterFirstRequest() {
- flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
-
- verify(packageManager).addOnPermissionsChangeListener(any())
- }
-
- @Test
- fun testListeningOnlyOnce() {
- flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
-
- flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID2)
-
- verify(packageManager, times(1)).addOnPermissionsChangeListener(any())
- }
-
- @Test
- fun testUpdateFlag() {
- assertEquals(0, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
-
- `when`(packageManager.getPermissionFlags(
- TEST_PERMISSION,
- TEST_PACKAGE,
- UserHandle.getUserHandleForUid(TEST_UID1))
- ).thenReturn(1)
-
- flagsCache.onPermissionsChanged(TEST_UID1)
-
- executor.runAllReady()
-
- assertEquals(1, flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1))
- }
-
- @Test
- fun testUpdateFlag_notUpdatedIfUidHasNotBeenRequestedBefore() {
- flagsCache.getPermissionFlags(TEST_PERMISSION, TEST_PACKAGE, TEST_UID1)
-
- flagsCache.onPermissionsChanged(TEST_UID2)
-
- executor.runAllReady()
-
- verify(packageManager, never()).getPermissionFlags(
- TEST_PERMISSION,
- TEST_PACKAGE,
- UserHandle.getUserHandleForUid(TEST_UID2)
- )
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
index 923cae8..a7f9fe4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -16,6 +16,9 @@
package com.android.systemui.classifier;
+import static com.android.systemui.plugins.FalsingManager.HIGH_PENALTY;
+import static com.android.systemui.plugins.FalsingManager.NO_PENALTY;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -55,8 +58,6 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class BrightLineClassifierTest extends SysuiTestCase {
- private static final long DOUBLE_TAP_TIMEOUT_MS = 1000;
-
private BrightLineFalsingManager mBrightLineFalsingManager;
@Mock
private FalsingDataProvider mFalsingDataProvider;
@@ -91,7 +92,7 @@
.thenReturn(mPassedResult);
when(mClassifierB.classifyGesture(anyInt(), anyDouble(), anyDouble()))
.thenReturn(mPassedResult);
- when(mSingleTapClassfier.isTap(any(List.class))).thenReturn(mPassedResult);
+ when(mSingleTapClassfier.isTap(any(List.class), anyDouble())).thenReturn(mPassedResult);
when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble()))
.thenReturn(mPassedResult);
mClassifiers.add(mClassifierA);
@@ -170,13 +171,13 @@
@Test
public void testIsFalseTap_BasicCheck() {
- when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mFalsedResult);
+ when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mFalsedResult);
- assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isTrue();
+ assertThat(mBrightLineFalsingManager.isSimpleTap()).isFalse();
- when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
+ when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mPassedResult);
- assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isFalse();
+ assertThat(mBrightLineFalsingManager.isSimpleTap()).isTrue();
}
@Test
@@ -185,26 +186,26 @@
when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(new ArrayList<>());
when(mFalsingDataProvider.getPriorMotionEvents()).thenReturn(mMotionEventList);
- mBrightLineFalsingManager.isFalseTap(false, 0);
- verify(mSingleTapClassfier).isTap(mMotionEventList);
+ mBrightLineFalsingManager.isFalseTap(0);
+ verify(mSingleTapClassfier).isTap(mMotionEventList, 0);
}
@Test
public void testIsFalseTap_RobustCheck_NoFaceAuth() {
- when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
+ when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mPassedResult);
when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble()))
.thenReturn(mFalsedResult);
when(mHistoryTracker.falseBelief()).thenReturn(1.0);
mFalsingDataProvider.setJustUnlockedWithFace(false);
- assertThat(mBrightLineFalsingManager.isFalseTap(true, 0)).isTrue();
+ assertThat(mBrightLineFalsingManager.isFalseTap(NO_PENALTY)).isTrue();
}
@Test
public void testIsFalseTap_RobustCheck_FaceAuth() {
- when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
+ when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mPassedResult);
when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true);
- assertThat(mBrightLineFalsingManager.isFalseTap(true, 0)).isFalse();
+ assertThat(mBrightLineFalsingManager.isFalseTap(NO_PENALTY)).isFalse();
}
@Test
@@ -230,7 +231,7 @@
@Test
public void testHistory_singleTap() {
// When trying to classify single taps, we don't immediately add results to history.
- mBrightLineFalsingManager.isFalseTap(false, 0);
+ mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY);
mGestureFinalizedListener.onGestureFinalized(1000);
verify(mHistoryTracker).addResults(anyCollection(), eq(1000L));
}
@@ -238,9 +239,9 @@
@Test
public void testHistory_multipleSingleTaps() {
// When trying to classify single taps, we don't immediately add results to history.
- mBrightLineFalsingManager.isFalseTap(false, 0);
+ mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY);
mGestureFinalizedListener.onGestureFinalized(1000);
- mBrightLineFalsingManager.isFalseTap(false, 0);
+ mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY);
mGestureFinalizedListener.onGestureFinalized(2000);
verify(mHistoryTracker).addResults(anyCollection(), eq(1000L));
verify(mHistoryTracker).addResults(anyCollection(), eq(2000L));
@@ -249,10 +250,10 @@
@Test
public void testHistory_doubleTap() {
// When trying to classify single taps, we don't immediately add results to history.
- mBrightLineFalsingManager.isFalseTap(false, 0);
+ mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY);
mGestureFinalizedListener.onGestureFinalized(1000);
// Before checking for double tap, we may check for single-tap on the second gesture.
- mBrightLineFalsingManager.isFalseTap(false, 0);
+ mBrightLineFalsingManager.isFalseTap(HIGH_PENALTY);
mBrightLineFalsingManager.isFalseDoubleTap();
mGestureFinalizedListener.onGestureFinalized(2000);
@@ -270,8 +271,8 @@
.thenReturn(mFalsedResult);
assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse();
- when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mFalsedResult);
- assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isFalse();
+ when(mSingleTapClassfier.isTap(mMotionEventList, 0)).thenReturn(mFalsedResult);
+ assertThat(mBrightLineFalsingManager.isSimpleTap()).isFalse();
when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble()))
.thenReturn(mFalsedResult);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
index f726cbe..2ceee6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyDouble;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.when;
@@ -78,7 +79,7 @@
@Test
public void testSingleTap() {
- when(mSingleTapClassifier.isTap(anyList())).thenReturn(mFalsedResult);
+ when(mSingleTapClassifier.isTap(anyList(), anyDouble())).thenReturn(mFalsedResult);
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, TOUCH_SLOP, 1);
@@ -88,7 +89,7 @@
@Test
public void testDoubleTap() {
- when(mSingleTapClassifier.isTap(anyList())).thenReturn(mPassedResult);
+ when(mSingleTapClassifier.isTap(anyList(), anyDouble())).thenReturn(mPassedResult);
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
@@ -104,7 +105,8 @@
@Test
public void testBadFirstTap() {
- when(mSingleTapClassifier.isTap(anyList())).thenReturn(mPassedResult, mFalsedResult);
+ when(mSingleTapClassifier.isTap(anyList(), anyDouble()))
+ .thenReturn(mPassedResult, mFalsedResult);
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
@@ -120,7 +122,8 @@
@Test
public void testBadSecondTap() {
- when(mSingleTapClassifier.isTap(anyList())).thenReturn(mFalsedResult, mPassedResult);
+ when(mSingleTapClassifier.isTap(anyList(), anyDouble()))
+ .thenReturn(mFalsedResult, mPassedResult);
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
@@ -136,7 +139,7 @@
@Test
public void testBadTouchSlop() {
- when(mSingleTapClassifier.isTap(anyList())).thenReturn(mFalsedResult);
+ when(mSingleTapClassifier.isTap(anyList(), anyDouble())).thenReturn(mFalsedResult);
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
@@ -152,7 +155,7 @@
@Test
public void testBadTouchSlow() {
- when(mSingleTapClassifier.isTap(anyList())).thenReturn(mFalsedResult);
+ when(mSingleTapClassifier.isTap(anyList(), anyDouble())).thenReturn(mFalsedResult);
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java
index 1524107..e3c800e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java
@@ -142,12 +142,12 @@
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, 1);
- assertThat(mClassifier.isTap(mMotionEvents).isFalse()).isFalse();
+ assertThat(mClassifier.isTap(mMotionEvents, 0.5).isFalse()).isFalse();
addMotionEvent(0, 0, MotionEvent.ACTION_DOWN, 1, 1);
addMotionEvent(0, 1, MotionEvent.ACTION_UP, 1, TOUCH_SLOP + 1);
- assertThat(mClassifier.isTap(mMotionEvents).isFalse()).isTrue();
+ assertThat(mClassifier.isTap(mMotionEvents, 0.5).isFalse()).isTrue();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
index 8db0f33..107ac83 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
@@ -162,6 +162,9 @@
assertEquals(View.GONE, smallResult.findViewById(R.id.predefined_icon).getVisibility());
// Shows person icon.
assertEquals(View.VISIBLE, smallResult.findViewById(R.id.person_icon).getVisibility());
+ // No messages count.
+ assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
+
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
@@ -220,6 +223,8 @@
// Has person icon.
assertEquals(View.VISIBLE,
smallResult.findViewById(R.id.person_icon).getVisibility());
+ // No messages count.
+ assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
@@ -281,6 +286,8 @@
// Has person icon.
assertEquals(View.VISIBLE,
smallResult.findViewById(R.id.person_icon).getVisibility());
+ // No messages count.
+ assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
@@ -342,6 +349,8 @@
// Has person icon.
assertEquals(View.VISIBLE,
smallResult.findViewById(R.id.person_icon).getVisibility());
+ // No messages count.
+ assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
@@ -404,6 +413,8 @@
smallResult.findViewById(R.id.predefined_icon).getVisibility());
// Has person icon.
assertEquals(View.VISIBLE, smallResult.findViewById(R.id.person_icon).getVisibility());
+ // No messages count.
+ assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
@@ -471,7 +482,7 @@
smallResult.findViewById(R.id.person_icon).getVisibility());
// Has a single message, no count shown.
- assertEquals(View.GONE, result.findViewById(R.id.messages_count).getVisibility());
+ assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
@@ -497,7 +508,7 @@
assertThat(statusContent.getMaxLines()).isEqualTo(3);
// Has a single message, no count shown.
- assertEquals(View.GONE, result.findViewById(R.id.messages_count).getVisibility());
+ assertEquals(View.GONE, largeResult.findViewById(R.id.messages_count).getVisibility());
}
@@ -544,7 +555,7 @@
smallResult.findViewById(R.id.person_icon).getVisibility());
// Has two messages, show count.
- assertEquals(View.VISIBLE, result.findViewById(R.id.messages_count).getVisibility());
+ assertEquals(View.VISIBLE, smallResult.findViewById(R.id.messages_count).getVisibility());
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
getSizeInDp(R.dimen.required_width_for_large));
@@ -570,7 +581,7 @@
assertThat(statusContent.getMaxLines()).isEqualTo(3);
// Has two messages, show count.
- assertEquals(View.VISIBLE, result.findViewById(R.id.messages_count).getVisibility());
+ assertEquals(View.VISIBLE, largeResult.findViewById(R.id.messages_count).getVisibility());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
index 650ee50..21fec91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
@@ -38,6 +38,7 @@
import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.globalactions.GlobalActionsDialogLite;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserTracker;
@@ -121,7 +122,7 @@
mController = new QSFooterViewController(mView, mUserManager, mUserInfoController,
mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController,
new QSDetailDisplayer(), mQuickQSPanelController, mFakeTunerService,
- mMetricsLogger, false, mGlobalActionsDialog);
+ mMetricsLogger, new FalsingManagerFake(), false, mGlobalActionsDialog);
mController.init();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 0f9ca7b..2d6ed7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -150,12 +150,12 @@
@Test
public void testClick_falsing() {
- mFalsingManager.setFalseRobustTap(true);
+ mFalsingManager.setFalseTap(true);
mTile.click();
mTestableLooper.processAllMessages();
assertThat(mTile.mClicked).isFalse();
- mFalsingManager.setFalseRobustTap(false);
+ mFalsingManager.setFalseTap(false);
mTile.click();
mTestableLooper.processAllMessages();
assertThat(mTile.mClicked).isTrue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
index 3c4fde8..895339f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
@@ -43,6 +43,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollectorFake;
+import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -160,6 +161,7 @@
mNotificationLockscreenUserManager,
mMetricsLogger,
new FalsingCollectorFake(),
+ new FalsingManagerFake(),
mResources,
mNotificationSwipeHelperBuilder,
mStatusBar,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 96cdaf9..67fd5eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -38,12 +38,16 @@
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
+import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
+import java.util.Objects;
+
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
@@ -53,6 +57,7 @@
private View mNotificationAreaInner;
private View mCenteredNotificationAreaView;
private StatusBarStateController mStatusBarStateController;
+ private OngoingCallController mOngoingCallController;
public CollapsedStatusBarFragmentTest() {
super(CollapsedStatusBarFragment.class);
@@ -162,8 +167,51 @@
Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
}
+ @Test
+ public void onOngoingCallStarted_notificationsHiddenAndOngoingCallChipDisplayed() {
+ mFragments.dispatchResume();
+ processAllMessages();
+
+ CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ fragment.initNotificationIconArea(mMockNotificiationAreaController);
+
+ ArgumentCaptor<OngoingCallListener> ongoingCallListenerCaptor = ArgumentCaptor.forClass(
+ OngoingCallListener.class);
+ Mockito.verify(mOngoingCallController).addCallback(ongoingCallListenerCaptor.capture());
+ OngoingCallListener listener = Objects.requireNonNull(ongoingCallListenerCaptor.getValue());
+
+ when(mOngoingCallController.getHasOngoingCall()).thenReturn(true);
+ listener.onOngoingCallStarted(/* animate= */ false);
+
+ assertEquals(View.VISIBLE,
+ mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+ Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
+ }
+
+ @Test
+ public void onOngoingCallEnded_notificationsDisplayedAndOngoingCallChipHidden() {
+ mFragments.dispatchResume();
+ processAllMessages();
+
+ CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ fragment.initNotificationIconArea(mMockNotificiationAreaController);
+
+ ArgumentCaptor<OngoingCallListener> ongoingCallListenerCaptor = ArgumentCaptor.forClass(
+ OngoingCallListener.class);
+ Mockito.verify(mOngoingCallController).addCallback(ongoingCallListenerCaptor.capture());
+ OngoingCallListener listener = Objects.requireNonNull(ongoingCallListenerCaptor.getValue());
+
+ when(mOngoingCallController.getHasOngoingCall()).thenReturn(false);
+ listener.onOngoingCallEnded(/* animate= */ false);
+
+ assertEquals(View.GONE,
+ mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+ Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
+ }
+
@Override
protected Fragment instantiate(Context context, String className, Bundle arguments) {
- return new CollapsedStatusBarFragment(mock(OngoingCallController.class));
+ mOngoingCallController = mock(OngoingCallController.class);
+ return new CollapsedStatusBarFragment(mOngoingCallController);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java
index 4ed2746..b70c6dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java
@@ -63,7 +63,8 @@
when(mResources.getDimension(R.dimen.double_tap_slop))
.thenReturn((float) ViewConfiguration.get(mContext).getScaledTouchSlop() - 1);
- mFalsingManager.setFalseRobustTap(true); // Test double tapping most of the time.
+ mFalsingManager.setSimpleTap(true);
+ mFalsingManager.setFalseTap(true); // Test double tapping most of the time.
mNotificationTapHelper = new NotificationTapHelper.Factory(mFalsingManager, mFakeExecutor)
.create(mActivationListener, mDoubleTapListener, mSlideBackListener);
@@ -158,7 +159,7 @@
1,
0);
- mFalsingManager.setFalseTap(true);
+ mFalsingManager.setSimpleTap(false);
mNotificationTapHelper.onTouchEvent(evDownA);
mNotificationTapHelper.onTouchEvent(evUpA);
verify(mActivationListener, never()).onActiveChanged(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
new file mode 100644
index 0000000..d87d1d1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -0,0 +1,153 @@
+/*
+ * 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.phone.ongoingcall
+
+import android.app.Notification
+import android.app.PendingIntent
+import android.app.Person
+import android.service.notification.NotificationListenerService.REASON_USER_STOPPED
+import androidx.test.filters.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.LayoutInflater
+import android.widget.LinearLayout
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.FeatureFlags
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.never
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class OngoingCallControllerTest : SysuiTestCase() {
+
+ private lateinit var controller: OngoingCallController
+ private lateinit var notifCollectionListener: NotifCollectionListener
+
+ @Mock private lateinit var mockOngoingCallListener: OngoingCallListener
+
+ private lateinit var chipView: LinearLayout
+
+ @Before
+ fun setUp() {
+ allowTestableLooperAsMainThread()
+ TestableLooper.get(this).runWithLooper {
+ chipView = LayoutInflater.from(mContext)
+ .inflate(R.layout.ongoing_call_chip, null) as LinearLayout
+ }
+
+ MockitoAnnotations.initMocks(this)
+ val featureFlags = mock(FeatureFlags::class.java)
+ `when`(featureFlags.isOngoingCallStatusBarChipEnabled).thenReturn(true)
+ val notificationCollection = mock(CommonNotifCollection::class.java)
+
+ controller = OngoingCallController(notificationCollection, featureFlags, FakeSystemClock())
+ controller.init()
+ controller.addCallback(mockOngoingCallListener)
+ controller.setChipView(chipView)
+
+ val collectionListenerCaptor = ArgumentCaptor.forClass(NotifCollectionListener::class.java)
+ verify(notificationCollection).addCollectionListener(collectionListenerCaptor.capture())
+ notifCollectionListener = collectionListenerCaptor.value!!
+ }
+
+ @Test
+ fun onEntryUpdated_isOngoingCallNotif_listenerNotifiedWithRightCallTime() {
+ notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+ verify(mockOngoingCallListener).onOngoingCallStarted(anyBoolean())
+ }
+
+ @Test
+ fun onEntryUpdated_notOngoingCallNotif_listenerNotNotified() {
+ notifCollectionListener.onEntryUpdated(createNotCallNotifEntry())
+
+ verify(mockOngoingCallListener, never()).onOngoingCallStarted(anyBoolean())
+ }
+
+ @Test
+ fun onEntryRemoved_ongoingCallNotif_listenerNotified() {
+ notifCollectionListener.onEntryRemoved(createOngoingCallNotifEntry(), REASON_USER_STOPPED)
+
+ verify(mockOngoingCallListener).onOngoingCallEnded(anyBoolean())
+ }
+
+ @Test
+ fun onEntryRemoved_notOngoingCallNotif_listenerNotNotified() {
+ notifCollectionListener.onEntryRemoved(createNotCallNotifEntry(), REASON_USER_STOPPED)
+
+ verify(mockOngoingCallListener, never()).onOngoingCallEnded(anyBoolean())
+ }
+
+ @Test
+ fun hasOngoingCall_noOngoingCallNotifSent_returnsFalse() {
+ assertThat(controller.hasOngoingCall).isFalse()
+ }
+
+ @Test
+ fun hasOngoingCall_ongoingCallNotifSentAndChipViewSet_returnsTrue() {
+ notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+ assertThat(controller.hasOngoingCall).isTrue()
+ }
+
+ @Test
+ fun hasOngoingCall_ongoingCallNotifSentButNoChipView_returnsFalse() {
+ controller.setChipView(null)
+ notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+ assertThat(controller.hasOngoingCall).isFalse()
+ }
+
+ @Test
+ fun hasOngoingCall_ongoingCallNotifSentThenRemoved_returnsFalse() {
+ val ongoingCallNotifEntry = createOngoingCallNotifEntry()
+
+ notifCollectionListener.onEntryUpdated(ongoingCallNotifEntry)
+ notifCollectionListener.onEntryRemoved(ongoingCallNotifEntry, REASON_USER_STOPPED)
+
+ assertThat(controller.hasOngoingCall).isFalse()
+ }
+
+ private fun createOngoingCallNotifEntry(): NotificationEntry {
+ val notificationEntryBuilder = NotificationEntryBuilder()
+ notificationEntryBuilder.modifyNotification(context).style = ongoingCallStyle
+ return notificationEntryBuilder.build()
+ }
+
+ private fun createNotCallNotifEntry() = NotificationEntryBuilder().build()
+}
+
+private val ongoingCallStyle = Notification.CallStyle.forOngoingCall(
+ Person.Builder().setName("name").build(),
+ /* hangUpIntent= */ mock(PendingIntent::class.java))
diff --git a/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java b/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
index ea3e650..5f9aaae 100644
--- a/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
+++ b/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
@@ -158,24 +158,29 @@
final Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR, SEND_NOTIFICATION_DELAY_HOURS);
mAlarmManager.set(RTC_WAKEUP, cal.getTimeInMillis(),
- createPendingIntent(mContext, userId, ACTION_SEND_NOTIFICATION,
- service.flattenToShortString()));
+ createPendingIntent(mContext, userId, ACTION_SEND_NOTIFICATION, service));
}
private void cancelAlarm(int userId, ComponentName service) {
mAlarmManager.cancel(
- createPendingIntent(mContext, userId, ACTION_SEND_NOTIFICATION,
- service.flattenToShortString()));
+ createPendingIntent(mContext, userId, ACTION_SEND_NOTIFICATION, service));
}
protected static PendingIntent createPendingIntent(Context context, int userId, String action,
- String serviceComponentName) {
+ ComponentName serviceComponentName) {
+ return PendingIntent.getBroadcast(context, 0,
+ createIntent(context, userId, action, serviceComponentName),
+ PendingIntent.FLAG_IMMUTABLE);
+ }
+
+ protected static Intent createIntent(Context context, int userId, String action,
+ ComponentName serviceComponentName) {
final Intent intent = new Intent(action);
intent.setPackage(context.getPackageName())
- .setIdentifier(serviceComponentName)
+ .setIdentifier(serviceComponentName.flattenToShortString())
+ .putExtra(Intent.EXTRA_COMPONENT_NAME, serviceComponentName)
.putExtra(Intent.EXTRA_USER_ID, userId);
- return PendingIntent.getBroadcast(context, 0, intent,
- PendingIntent.FLAG_IMMUTABLE);
+ return intent;
}
/** A sub class to handle notifications and settings on the main thread. */
@@ -204,10 +209,9 @@
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- final String service = intent.getIdentifier();
- final ComponentName componentName = ComponentName.unflattenFromString(service);
- if (TextUtils.isEmpty(action) || TextUtils.isEmpty(service)
- || componentName == null) {
+ final ComponentName componentName = intent.getParcelableExtra(
+ Intent.EXTRA_COMPONENT_NAME);
+ if (TextUtils.isEmpty(action) || componentName == null) {
return;
}
final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_SYSTEM);
@@ -215,7 +219,8 @@
trySendNotification(userId, componentName);
} else if (ACTION_A11Y_SETTINGS.equals(action)) {
launchSettings(userId, componentName);
- mNotificationManager.cancel(service, NOTE_A11Y_VIEW_AND_CONTROL_ACCESS);
+ mNotificationManager.cancel(componentName.flattenToShortString(),
+ NOTE_A11Y_VIEW_AND_CONTROL_ACCESS);
onNotificationCanceled(userId, componentName);
} else if (ACTION_DISMISS_NOTIFICATION.equals(action)) {
onNotificationCanceled(userId, componentName);
@@ -257,8 +262,7 @@
mContext.getPackageManager());
final int size = mContext.getResources().getDimensionPixelSize(
android.R.dimen.app_icon_size);
- sendNotification(userId, componentName.flattenToShortString(),
- displayName,
+ sendNotification(userId, componentName, displayName,
ImageUtils.buildScaledBitmap(drawable, size, size));
}
break;
@@ -289,7 +293,8 @@
}
}
- private void sendNotification(int userId, String serviceComponentName, CharSequence name,
+ private void sendNotification(int userId, ComponentName serviceComponentName,
+ CharSequence name,
Bitmap bitmap) {
final Notification.Builder notificationBuilder = new Notification.Builder(mContext,
SystemNotificationChannels.ACCESSIBILITY_SECURITY_POLICY);
@@ -315,7 +320,8 @@
if (bitmap != null) {
notificationBuilder.setLargeIcon(bitmap);
}
- mNotificationManager.notify(serviceComponentName, NOTE_A11Y_VIEW_AND_CONTROL_ACCESS,
+ mNotificationManager.notify(serviceComponentName.flattenToShortString(),
+ NOTE_A11Y_VIEW_AND_CONTROL_ACCESS,
notificationBuilder.build());
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index e9b2ed3..55490ce 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -154,6 +154,8 @@
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 long DEVICE_LISTENER_DIED_REBIND_TIMEOUT_MS = 10 * 1000;
+
private static final boolean DEBUG = false;
private static final String LOG_TAG = "CompanionDeviceManagerService";
@@ -1131,6 +1133,14 @@
// Service binding is managed manually based on corresponding device being nearby
return Long.MAX_VALUE;
}
+
+ @Override
+ public void binderDied() {
+ super.binderDied();
+
+ // Re-connect to the service if process gets killed
+ mMainHandler.postDelayed(this::connect, DEVICE_LISTENER_DIED_REBIND_TIMEOUT_MS);
+ }
};
}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index c983600..7057b840 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -19,6 +19,7 @@
per-file *Alarm* = file:/apex/jobscheduler/OWNERS
per-file *AppOp* = file:/core/java/android/permission/OWNERS
per-file *Battery* = file:/BATTERY_STATS_OWNERS
+per-file *Binder* = file:/core/java/com/android/internal/os/BINDER_OWNERS
per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS
per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
per-file *Location* = file:/services/core/java/com/android/server/location/OWNERS
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ac042a7..23dda78 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -234,6 +234,7 @@
import android.graphics.Rect;
import android.hardware.display.DisplayManagerInternal;
import android.media.audiofx.AudioEffect;
+import android.net.ConnectivityManager;
import android.net.Proxy;
import android.net.Uri;
import android.os.AppZygote;
@@ -3489,9 +3490,9 @@
// Clear its scheduled jobs
JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
- // Clearing data is akin to uninstalling. The app is force stopped before we
- // get to this point, so the reason won't be checked by the app.
- js.cancelJobsForUid(appInfo.uid, JobParameters.STOP_REASON_USER, "clear data");
+ // Clearing data is a user-initiated action.
+ js.cancelJobsForUid(appInfo.uid, JobParameters.STOP_REASON_USER,
+ JobParameters.DEBUG_REASON_DATA_CLEARED, "clear data");
// Clear its pending alarms
AlarmManagerInternal ami = LocalServices.getService(AlarmManagerInternal.class);
@@ -12944,7 +12945,7 @@
}
mBatteryStatsService.noteCurrentTimeChanged();
break;
- case Intent.ACTION_CLEAR_DNS_CACHE:
+ case ConnectivityManager.ACTION_CLEAR_DNS_CACHE:
mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);
break;
case Proxy.PROXY_CHANGE_ACTION:
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index c8721cc..960cc42 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1328,6 +1328,9 @@
|| uidRec.getSetProcState() == PROCESS_STATE_NONEXISTENT) {
uidChange |= isCached ? UidRecord.CHANGE_CACHED : UidRecord.CHANGE_UNCACHED;
}
+ if (uidRec.getSetCapability() != uidRec.getCurCapability()) {
+ uidChange |= UidRecord.CHANGE_CAPABILITY;
+ }
uidRec.setSetProcState(uidRec.getCurProcState());
uidRec.setSetCapability(uidRec.getCurCapability());
uidRec.setSetAllowListed(uidRec.isCurAllowListed());
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index df65ee6..c1bfe25 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -147,6 +147,9 @@
if ((currentChange & UidRecord.CHANGE_GONE) != 0) {
currentChange &= ~(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_CACHED);
}
+ if ((pendingChange & UidRecord.CHANGE_CAPABILITY) != 0) {
+ currentChange |= UidRecord.CHANGE_CAPABILITY;
+ }
return currentChange;
}
@@ -285,12 +288,9 @@
reg.mLastProcStates.delete(item.uid);
}
} else {
+ boolean doReport = false;
if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
- if (DEBUG_UID_OBSERVERS) {
- Slog.i(TAG_UID_OBSERVERS, "UID CHANGED uid=" + item.uid
- + ": " + item.procState + ": " + item.capability);
- }
- boolean doReport = true;
+ doReport = true;
if (reg.mCutpoint >= ActivityManager.MIN_PROCESS_STATE) {
final int lastState = reg.mLastProcStates.get(item.uid,
ActivityManager.PROCESS_STATE_UNKNOWN);
@@ -302,13 +302,20 @@
doReport = item.procState != PROCESS_STATE_NONEXISTENT;
}
}
- if (doReport) {
- if (reg.mLastProcStates != null) {
- reg.mLastProcStates.put(item.uid, item.procState);
- }
- observer.onUidStateChanged(item.uid, item.procState,
- item.procStateSeq, item.capability);
+ }
+ if ((reg.mWhich & ActivityManager.UID_OBSERVER_CAPABILITY) != 0) {
+ doReport |= (change & UidRecord.CHANGE_CAPABILITY) != 0;
+ }
+ if (doReport) {
+ if (DEBUG_UID_OBSERVERS) {
+ Slog.i(TAG_UID_OBSERVERS, "UID CHANGED uid=" + item.uid
+ + ": " + item.procState + ": " + item.capability);
}
+ if (reg.mLastProcStates != null) {
+ reg.mLastProcStates.put(item.uid, item.procState);
+ }
+ observer.onUidStateChanged(item.uid, item.procState,
+ item.procStateSeq, item.capability);
}
}
final int duration = (int) (SystemClock.uptimeMillis() - start);
@@ -428,12 +435,14 @@
ActivityManager.UID_OBSERVER_ACTIVE,
ActivityManager.UID_OBSERVER_GONE,
ActivityManager.UID_OBSERVER_PROCSTATE,
+ ActivityManager.UID_OBSERVER_CAPABILITY,
};
private static final int[] PROTO_ENUMS = new int[]{
ActivityManagerProto.UID_OBSERVER_FLAG_IDLE,
ActivityManagerProto.UID_OBSERVER_FLAG_ACTIVE,
ActivityManagerProto.UID_OBSERVER_FLAG_GONE,
ActivityManagerProto.UID_OBSERVER_FLAG_PROCSTATE,
+ ActivityManagerProto.UID_OBSERVER_FLAG_CAPABILITY,
};
UidObserverRegistration(int uid, @NonNull String pkg, int which, int cutpoint) {
@@ -462,6 +471,9 @@
if ((mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) {
pw.print(" GONE");
}
+ if ((mWhich & ActivityManager.UID_OBSERVER_CAPABILITY) != 0) {
+ pw.print(" CAP");
+ }
if ((mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
pw.print(" STATE");
pw.print(" (cut=");
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index 2fb662f..4ba59fa 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -122,6 +122,7 @@
static final int CHANGE_ACTIVE = 1<<2;
static final int CHANGE_CACHED = 1<<3;
static final int CHANGE_UNCACHED = 1<<4;
+ static final int CHANGE_CAPABILITY = 1<<5;
// Keep the enum lists in sync
private static int[] ORIG_ENUMS = new int[] {
@@ -130,6 +131,7 @@
CHANGE_ACTIVE,
CHANGE_CACHED,
CHANGE_UNCACHED,
+ CHANGE_CAPABILITY,
};
private static int[] PROTO_ENUMS = new int[] {
UidRecordProto.CHANGE_GONE,
@@ -137,6 +139,7 @@
UidRecordProto.CHANGE_ACTIVE,
UidRecordProto.CHANGE_CACHED,
UidRecordProto.CHANGE_UNCACHED,
+ UidRecordProto.CHANGE_CAPABILITY,
};
// UidObserverController is the only thing that should modify this.
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 2bc81cb..9c10814 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -31,6 +31,7 @@
import android.app.UriGrantsManager;
import android.content.ClipData;
import android.content.ClipDescription;
+import android.content.ClipboardManager;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -57,10 +58,6 @@
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-import android.system.VmSocketAddress;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
@@ -82,128 +79,9 @@
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.WindowManagerInternal;
-import java.io.FileDescriptor;
-import java.io.InterruptedIOException;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
-
-// The following class is Android Emulator specific. It is used to read and
-// write contents of the host system's clipboard.
-class HostClipboardMonitor implements Runnable {
- public interface HostClipboardCallback {
- void onHostClipboardUpdated(String contents);
- }
-
- private FileDescriptor mPipe = null;
- private HostClipboardCallback mHostClipboardCallback;
- private static final String PIPE_NAME = "pipe:clipboard";
- private static final int HOST_PORT = 5000;
-
- private static byte[] createOpenHandshake() {
- // String.getBytes doesn't include the null terminator,
- // but the QEMU pipe device requires the pipe service name
- // to be null-terminated.
-
- final byte[] bits = Arrays.copyOf(PIPE_NAME.getBytes(), PIPE_NAME.length() + 1);
- bits[PIPE_NAME.length()] = 0;
- return bits;
- }
-
- private boolean openPipe() {
- try {
- final FileDescriptor fd = Os.socket(OsConstants.AF_VSOCK, OsConstants.SOCK_STREAM, 0);
-
- try {
- Os.connect(fd, new VmSocketAddress(HOST_PORT, OsConstants.VMADDR_CID_HOST));
-
- final byte[] handshake = createOpenHandshake();
- Os.write(fd, handshake, 0, handshake.length);
- mPipe = fd;
- return true;
- } catch (ErrnoException | SocketException | InterruptedIOException e) {
- Os.close(fd);
- }
- } catch (ErrnoException e) {
- }
-
- return false;
- }
-
- private void closePipe() {
- try {
- final FileDescriptor fd = mPipe;
- mPipe = null;
- if (fd != null) {
- Os.close(fd);
- }
- } catch (ErrnoException ignore) {
- }
- }
-
- private byte[] receiveMessage() throws ErrnoException, InterruptedIOException {
- final byte[] lengthBits = new byte[4];
- Os.read(mPipe, lengthBits, 0, lengthBits.length);
-
- final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
- bb.order(ByteOrder.LITTLE_ENDIAN);
- final int msgLen = bb.getInt();
-
- final byte[] msg = new byte[msgLen];
- Os.read(mPipe, msg, 0, msg.length);
-
- return msg;
- }
-
- private void sendMessage(byte[] msg) throws ErrnoException, InterruptedIOException {
- final byte[] lengthBits = new byte[4];
- final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
- bb.order(ByteOrder.LITTLE_ENDIAN);
- bb.putInt(msg.length);
-
- Os.write(mPipe, lengthBits, 0, lengthBits.length);
- Os.write(mPipe, msg, 0, msg.length);
- }
-
- public HostClipboardMonitor(HostClipboardCallback cb) {
- mHostClipboardCallback = cb;
- }
-
- @Override
- public void run() {
- while (!Thread.interrupted()) {
- try {
- // There's no guarantee that QEMU pipes will be ready at the moment
- // this method is invoked. We simply try to get the pipe open and
- // retry on failure indefinitely.
- while ((mPipe == null) && !openPipe()) {
- Thread.sleep(100);
- }
-
- final byte[] receivedData = receiveMessage();
- mHostClipboardCallback.onHostClipboardUpdated(
- new String(receivedData));
- } catch (ErrnoException | InterruptedIOException e) {
- closePipe();
- } catch (InterruptedException e) {
- }
- }
- }
-
- public void setHostClipboard(String content) {
- try {
- if (mPipe != null) {
- sendMessage(content.getBytes());
- }
- } catch (ErrnoException | InterruptedIOException e) {
- Slog.e("HostClipboardMonitor",
- "Failed to set host clipboard " + e.getMessage());
- }
- }
-}
+import java.util.function.Consumer;
/**
* Implementation of the clipboard for copy and paste.
@@ -220,8 +98,6 @@
SystemProperties.getBoolean("ro.boot.qemu", false);
// DeviceConfig properties
- private static final String PROPERTY_SHOW_ACCESS_NOTIFICATIONS = "show_access_notifications";
- private static final boolean DEFAULT_SHOW_ACCESS_NOTIFICATIONS = true;
private static final String PROPERTY_MAX_CLASSIFICATION_LENGTH = "max_classification_length";
private static final int DEFAULT_MAX_CLASSIFICATION_LENGTH = 400;
@@ -235,14 +111,15 @@
private final ContentCaptureManagerInternal mContentCaptureInternal;
private final AutofillManagerInternal mAutofillInternal;
private final IBinder mPermissionOwner;
- private final HostClipboardMonitor mHostClipboardMonitor;
+ private final Consumer<ClipData> mEmulatorClipboardMonitor;
private final Handler mWorkerHandler;
@GuardedBy("mLock")
private final SparseArray<PerUserClipboard> mClipboards = new SparseArray<>();
@GuardedBy("mLock")
- private boolean mShowAccessNotifications = DEFAULT_SHOW_ACCESS_NOTIFICATIONS;
+ private boolean mShowAccessNotifications =
+ ClipboardManager.DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS;
@GuardedBy("mLock")
private int mMaxClassificationLength = DEFAULT_MAX_CLASSIFICATION_LENGTH;
@@ -267,24 +144,14 @@
final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard");
mPermissionOwner = permOwner;
if (IS_EMULATOR) {
- mHostClipboardMonitor = new HostClipboardMonitor(
- new HostClipboardMonitor.HostClipboardCallback() {
- @Override
- public void onHostClipboardUpdated(String contents){
- ClipData clip =
- new ClipData("host clipboard",
- new String[]{"text/plain"},
- new ClipData.Item(contents));
- synchronized (mLock) {
- setPrimaryClipInternalLocked(getClipboardLocked(0), clip,
- android.os.Process.SYSTEM_UID, null);
- }
- }
- });
- Thread hostMonitorThread = new Thread(mHostClipboardMonitor);
- hostMonitorThread.start();
+ mEmulatorClipboardMonitor = new EmulatorClipboardMonitor((clip) -> {
+ synchronized (mLock) {
+ setPrimaryClipInternalLocked(getClipboardLocked(0), clip,
+ android.os.Process.SYSTEM_UID, null);
+ }
+ });
} else {
- mHostClipboardMonitor = null;
+ mEmulatorClipboardMonitor = (clip) -> {};
}
updateConfig();
@@ -310,8 +177,10 @@
private void updateConfig() {
synchronized (mLock) {
- mShowAccessNotifications = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CLIPBOARD,
- PROPERTY_SHOW_ACCESS_NOTIFICATIONS, DEFAULT_SHOW_ACCESS_NOTIFICATIONS);
+ mShowAccessNotifications = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_CLIPBOARD,
+ ClipboardManager.DEVICE_CONFIG_SHOW_ACCESS_NOTIFICATIONS,
+ ClipboardManager.DEVICE_CONFIG_DEFAULT_SHOW_ACCESS_NOTIFICATIONS);
mMaxClassificationLength = DeviceConfig.getInt(DeviceConfig.NAMESPACE_CLIPBOARD,
PROPERTY_MAX_CLASSIFICATION_LENGTH, DEFAULT_MAX_CLASSIFICATION_LENGTH);
}
@@ -643,18 +512,7 @@
@GuardedBy("mLock")
private void setPrimaryClipInternalLocked(
@Nullable ClipData clip, int uid, @Nullable String sourcePackage) {
- // Push clipboard to host, if any
- if (mHostClipboardMonitor != null) {
- if (clip == null) {
- // Someone really wants the clipboard cleared, so push empty
- mHostClipboardMonitor.setHostClipboard("");
- } else if (clip.getItemCount() > 0) {
- final CharSequence text = clip.getItemAt(0).getText();
- if (text != null) {
- mHostClipboardMonitor.setHostClipboard(text.toString());
- }
- }
- }
+ mEmulatorClipboardMonitor.accept(clip);
final int userId = UserHandle.getUserId(uid);
if (clip != null) {
@@ -1056,11 +914,9 @@
if (clipboard.primaryClip == null) {
return;
}
- if (!mShowAccessNotifications) {
- return;
- }
if (Settings.Secure.getInt(getContext().getContentResolver(),
- Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, 1) == 0) {
+ Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS,
+ (mShowAccessNotifications ? 1 : 0)) == 0) {
return;
}
// Don't notify if the app accessing the clipboard is the same as the current owner.
diff --git a/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java
new file mode 100644
index 0000000..62b701a
--- /dev/null
+++ b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java
@@ -0,0 +1,168 @@
+/*
+ * 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.clipboard;
+
+import android.annotation.Nullable;
+import android.content.ClipData;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.VmSocketAddress;
+import android.util.Slog;
+
+import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+// The following class is Android Emulator specific. It is used to read and
+// write contents of the host system's clipboard.
+class EmulatorClipboardMonitor implements Consumer<ClipData> {
+ private static final String TAG = "EmulatorClipboardMonitor";
+ private static final String PIPE_NAME = "pipe:clipboard";
+ private static final int HOST_PORT = 5000;
+ private final Thread mHostMonitorThread;
+ private FileDescriptor mPipe = null;
+
+ private static byte[] createOpenHandshake() {
+ // String.getBytes doesn't include the null terminator,
+ // but the QEMU pipe device requires the pipe service name
+ // to be null-terminated.
+
+ final byte[] bits = Arrays.copyOf(PIPE_NAME.getBytes(), PIPE_NAME.length() + 1);
+ bits[PIPE_NAME.length()] = 0;
+ return bits;
+ }
+
+ private boolean isPipeOpened() {
+ return mPipe != null;
+ }
+
+ private synchronized boolean openPipe() {
+ if (mPipe != null) {
+ return true;
+ }
+
+ try {
+ final FileDescriptor fd = Os.socket(OsConstants.AF_VSOCK, OsConstants.SOCK_STREAM, 0);
+
+ try {
+ Os.connect(fd, new VmSocketAddress(HOST_PORT, OsConstants.VMADDR_CID_HOST));
+
+ final byte[] handshake = createOpenHandshake();
+ Os.write(fd, handshake, 0, handshake.length);
+ mPipe = fd;
+ return true;
+ } catch (ErrnoException | SocketException | InterruptedIOException e) {
+ Os.close(fd);
+ }
+ } catch (ErrnoException e) {
+ }
+
+ return false;
+ }
+
+ private synchronized void closePipe() {
+ try {
+ final FileDescriptor fd = mPipe;
+ mPipe = null;
+ if (fd != null) {
+ Os.close(fd);
+ }
+ } catch (ErrnoException ignore) {
+ }
+ }
+
+ private byte[] receiveMessage() throws ErrnoException, InterruptedIOException {
+ final byte[] lengthBits = new byte[4];
+ Os.read(mPipe, lengthBits, 0, lengthBits.length);
+
+ final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+ final int msgLen = bb.getInt();
+
+ final byte[] msg = new byte[msgLen];
+ Os.read(mPipe, msg, 0, msg.length);
+
+ return msg;
+ }
+
+ private void sendMessage(final byte[] msg) throws ErrnoException, InterruptedIOException {
+ final byte[] lengthBits = new byte[4];
+ final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+ bb.putInt(msg.length);
+
+ Os.write(mPipe, lengthBits, 0, lengthBits.length);
+ Os.write(mPipe, msg, 0, msg.length);
+ }
+
+ EmulatorClipboardMonitor(final Consumer<ClipData> setAndroidClipboard) {
+ this.mHostMonitorThread = new Thread(() -> {
+ while (!Thread.interrupted()) {
+ try {
+ // There's no guarantee that QEMU pipes will be ready at the moment
+ // this method is invoked. We simply try to get the pipe open and
+ // retry on failure indefinitely.
+ while (!openPipe()) {
+ Thread.sleep(100);
+ }
+
+ final byte[] receivedData = receiveMessage();
+
+ final String str = new String(receivedData);
+ final ClipData clip = new ClipData("host clipboard",
+ new String[]{"text/plain"},
+ new ClipData.Item(str));
+
+ setAndroidClipboard.accept(clip);
+ } catch (ErrnoException | InterruptedIOException e) {
+ closePipe();
+ } catch (InterruptedException | IllegalArgumentException e) {
+ }
+ }
+ });
+
+ this.mHostMonitorThread.start();
+ }
+
+ @Override
+ public void accept(final @Nullable ClipData clip) {
+ if (clip == null) {
+ setHostClipboardImpl("");
+ } else if (clip.getItemCount() > 0) {
+ final CharSequence text = clip.getItemAt(0).getText();
+ if (text != null) {
+ setHostClipboardImpl(text.toString());
+ }
+ }
+ }
+
+ private void setHostClipboardImpl(final String value) {
+ try {
+ if (isPipeOpened()) {
+ sendMessage(value.getBytes());
+ }
+ } catch (ErrnoException | InterruptedIOException e) {
+ Slog.e(TAG, "Failed to set host clipboard " + e.getMessage());
+ } catch (IllegalArgumentException e) {
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/clipboard/OWNERS b/services/core/java/com/android/server/clipboard/OWNERS
new file mode 100644
index 0000000..5449df9
--- /dev/null
+++ b/services/core/java/com/android/server/clipboard/OWNERS
@@ -0,0 +1 @@
+per-file EmulatorClipboardMonitor.java = bohu@google.com,lfy@google.com,rkir@google.com
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index ffeb77d..cf4fe1e 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -420,7 +420,7 @@
/*
* Tell the VMs to toss their DNS caches
*/
- final Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);
+ final Intent intent = new Intent(ConnectivityManager.ACTION_CLEAR_DNS_CACHE);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
/*
* Connectivity events can happen before boot has completed ...
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 1786a51..614e488 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -268,10 +268,6 @@
private final SyncLogger mLogger;
- // NOTE: this is a temporary allow-list for testing purposes; it will be removed before release.
- private final String[] mEjSyncAllowedPackages = new String[]{
- "com.google.android.google", "com.android.frameworks.servicestests"};
-
private boolean isJobIdInUseLockedH(int jobId, List<JobInfo> pendingJobs) {
for (JobInfo job: pendingJobs) {
if (job.getId() == jobId) {
@@ -991,14 +987,6 @@
}
}
- final boolean scheduleAsEj =
- extras.getBoolean(ContentResolver.SYNC_EXTRAS_SCHEDULE_AS_EXPEDITED_JOB, false);
- // NOTE: this is a temporary check for internal testing - to be removed before release.
- if (scheduleAsEj && !ArrayUtils.contains(mEjSyncAllowedPackages, callingPackage)) {
- throw new IllegalArgumentException(
- callingPackage + " is not allowed to schedule a sync as an EJ yet.");
- }
-
for (AccountAndUser account : accounts) {
// If userId is specified, do not sync accounts of other users
if (userId >= UserHandle.USER_SYSTEM && account.userId >= UserHandle.USER_SYSTEM
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 03fb3a4..acfeb6c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -713,7 +713,9 @@
assertRunOnServiceThread();
if (!mService.isPowerStandbyOrTransient()) {
addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress()));
- if (isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId())
+ if (!isDirectConnectAddress(avr.getPhysicalAddress())) {
+ startArcAction(false);
+ } else if (isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId())
&& !hasAction(SetArcTransmissionStateAction.class)) {
startArcAction(true);
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 71a5d1c..6b28fbc 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.READ_CONTACTS;
+import static android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.UserHandle.USER_ALL;
@@ -160,6 +161,7 @@
import java.util.Objects;
import java.util.Random;
import java.util.Set;
+import java.util.StringJoiner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -1074,6 +1076,10 @@
mContext.enforceCallingOrSelfPermission(BIOMETRIC_PERMISSION, "LockSettingsBiometric");
}
+ private boolean hasPermission(String permission) {
+ return mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
+ }
+
@Override
public boolean hasSecureLockScreen() {
return mHasSecureLockScreen;
@@ -1567,42 +1573,52 @@
throw new UnsupportedOperationException(
"This operation requires secure lock screen feature");
}
- checkWritePermission(userId);
- enforceFrpResolved();
+ if (!hasPermission(PERMISSION) && !hasPermission(SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS)) {
+ throw new SecurityException(
+ "setLockCredential requires SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS or "
+ + PERMISSION);
+ }
- // When changing credential for profiles with unified challenge, some callers
- // will pass in empty credential while others will pass in the credential of
- // the parent user. setLockCredentialInternal() handles the formal case (empty
- // credential) correctly but not the latter. As a stopgap fix, convert the latter
- // case to the formal. The long-term fix would be fixing LSS such that it should
- // accept only the parent user credential on its public API interfaces, swap it
- // with the profile's random credential at that API boundary (i.e. here) and make
- // sure LSS internally does not special case profile with unififed challenge: b/80170828.
- if (!savedCredential.isNone() && isManagedProfileWithUnifiedLock(userId)) {
- // Verify the parent credential again, to make sure we have a fresh enough
- // auth token such that getDecryptedPasswordForTiedProfile() inside
- // setLockCredentialInternal() can function correctly.
- verifyCredential(savedCredential, mUserManager.getProfileParent(userId).id,
- 0 /* flags */);
- savedCredential.zeroize();
- savedCredential = LockscreenCredential.createNone();
- }
- synchronized (mSeparateChallengeLock) {
- if (!setLockCredentialInternal(credential, savedCredential,
- userId, /* isLockTiedToParent= */ false)) {
- scheduleGc();
- return false;
+ long identity = Binder.clearCallingIdentity();
+ try {
+ enforceFrpResolved();
+ // When changing credential for profiles with unified challenge, some callers
+ // will pass in empty credential while others will pass in the credential of
+ // the parent user. setLockCredentialInternal() handles the formal case (empty
+ // credential) correctly but not the latter. As a stopgap fix, convert the latter
+ // case to the formal. The long-term fix would be fixing LSS such that it should
+ // accept only the parent user credential on its public API interfaces, swap it
+ // with the profile's random credential at that API boundary (i.e. here) and make
+ // sure LSS internally does not special case profile with unififed challenge: b/80170828
+ if (!savedCredential.isNone() && isManagedProfileWithUnifiedLock(userId)) {
+ // Verify the parent credential again, to make sure we have a fresh enough
+ // auth token such that getDecryptedPasswordForTiedProfile() inside
+ // setLockCredentialInternal() can function correctly.
+ verifyCredential(savedCredential, mUserManager.getProfileParent(userId).id,
+ 0 /* flags */);
+ savedCredential.zeroize();
+ savedCredential = LockscreenCredential.createNone();
}
- setSeparateProfileChallengeEnabledLocked(userId, true, /* unused */ null);
- notifyPasswordChanged(userId);
+ synchronized (mSeparateChallengeLock) {
+ if (!setLockCredentialInternal(credential, savedCredential,
+ userId, /* isLockTiedToParent= */ false)) {
+ scheduleGc();
+ return false;
+ }
+ setSeparateProfileChallengeEnabledLocked(userId, true, /* unused */ null);
+ notifyPasswordChanged(userId);
+ }
+ if (mUserManager.getUserInfo(userId).isManagedProfile()) {
+ // Make sure the profile doesn't get locked straight after setting work challenge.
+ setDeviceUnlockedForUser(userId);
+ }
+ notifySeparateProfileChallengeChanged(userId);
+ onPostPasswordChanged(credential, userId);
+ scheduleGc();
+ return true;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- if (mUserManager.getUserInfo(userId).isManagedProfile()) {
- // Make sure the profile doesn't get locked straight after setting work challenge.
- setDeviceUnlockedForUser(userId);
- }
- notifySeparateProfileChallengeChanged(userId);
- scheduleGc();
- return true;
}
/**
@@ -1703,10 +1719,157 @@
return true;
}
+ private void onPostPasswordChanged(LockscreenCredential newCredential, int userHandle) {
+ updateEncryptionPasswordIfNeeded(newCredential, userHandle);
+ if (newCredential.isPattern()) {
+ setBoolean(LockPatternUtils.PATTERN_EVER_CHOSEN_KEY, true, userHandle);
+ }
+ updatePasswordHistory(newCredential, userHandle);
+ mContext.getSystemService(TrustManager.class).reportEnabledTrustAgentsChanged(userHandle);
+ }
+
+ /**
+ * Update device encryption password if calling user is USER_SYSTEM and device supports
+ * encryption.
+ */
+ private void updateEncryptionPasswordIfNeeded(LockscreenCredential credential, int userHandle) {
+ // Update the device encryption password.
+ if (userHandle != UserHandle.USER_SYSTEM || !isDeviceEncryptionEnabled()) {
+ return;
+ }
+ if (!shouldEncryptWithCredentials()) {
+ updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
+ return;
+ }
+ if (credential.isNone()) {
+ // Set the encryption password to default.
+ setCredentialRequiredToDecrypt(false);
+ }
+ updateEncryptionPassword(credential.getStorageCryptType(), credential.getCredential());
+ }
+
+ /**
+ * Store the hash of the *current* password in the password history list, if device policy
+ * enforces password history requirement.
+ */
+ private void updatePasswordHistory(LockscreenCredential password, int userHandle) {
+ if (password.isNone()) {
+ return;
+ }
+ if (password.isPattern()) {
+ // Do not keep track of historical patterns
+ return;
+ }
+ // Add the password to the password history. We assume all
+ // password hashes have the same length for simplicity of implementation.
+ String passwordHistory = getString(
+ LockPatternUtils.PASSWORD_HISTORY_KEY, /* defaultValue= */ null, userHandle);
+ if (passwordHistory == null) {
+ passwordHistory = "";
+ }
+ int passwordHistoryLength = getRequestedPasswordHistoryLength(userHandle);
+ if (passwordHistoryLength == 0) {
+ passwordHistory = "";
+ } else {
+ final byte[] hashFactor = getHashFactor(password, userHandle);
+ final byte[] salt = getSalt(userHandle).getBytes();
+ String hash = password.passwordToHistoryHash(hashFactor, salt);
+ if (hash == null) {
+ Slog.e(TAG, "Compute new style password hash failed, fallback to legacy style");
+ hash = password.legacyPasswordToHash(salt);
+ }
+ if (TextUtils.isEmpty(passwordHistory)) {
+ passwordHistory = hash;
+ } else {
+ String[] history = passwordHistory.split(
+ LockPatternUtils.PASSWORD_HISTORY_DELIMITER);
+ StringJoiner joiner = new StringJoiner(LockPatternUtils.PASSWORD_HISTORY_DELIMITER);
+ joiner.add(hash);
+ for (int i = 0; i < passwordHistoryLength - 1 && i < history.length; i++) {
+ joiner.add(history[i]);
+ }
+ passwordHistory = joiner.toString();
+ }
+ }
+ setString(LockPatternUtils.PASSWORD_HISTORY_KEY, passwordHistory, userHandle);
+ }
+
+ private String getSalt(int userId) {
+ long salt = getLong(LockPatternUtils.LOCK_PASSWORD_SALT_KEY, 0, userId);
+ if (salt == 0) {
+ try {
+ salt = SecureRandom.getInstance("SHA1PRNG").nextLong();
+ setLong(LockPatternUtils.LOCK_PASSWORD_SALT_KEY, salt, userId);
+ Slog.v(TAG, "Initialized lock password salt for user: " + userId);
+ } catch (NoSuchAlgorithmException e) {
+ // Throw an exception rather than storing a password we'll never be able to recover
+ throw new IllegalStateException("Couldn't get SecureRandom number", e);
+ }
+ }
+ return Long.toHexString(salt);
+ }
+
+ private int getRequestedPasswordHistoryLength(int userId) {
+ return mInjector.getDevicePolicyManager().getPasswordHistoryLength(null, userId);
+ }
+
+ private static boolean isDeviceEncryptionEnabled() {
+ return StorageManager.isEncrypted();
+ }
+
+ private boolean shouldEncryptWithCredentials() {
+ return isCredentialRequiredToDecrypt() && !isDoNotAskCredentialsOnBootSet();
+ }
+
+ private boolean isDoNotAskCredentialsOnBootSet() {
+ return mInjector.getDevicePolicyManager().getDoNotAskCredentialsOnBoot();
+ }
+
+ private boolean isCredentialRequiredToDecrypt() {
+ final int value = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, -1);
+ return value != 0;
+ }
+
private VerifyCredentialResponse convertResponse(GateKeeperResponse gateKeeperResponse) {
return VerifyCredentialResponse.fromGateKeeperResponse(gateKeeperResponse);
}
+ private void setCredentialRequiredToDecrypt(boolean required) {
+ if (isDeviceEncryptionEnabled()) {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, required ? 1 : 0);
+ }
+ }
+
+ /** Update the encryption password if it is enabled **/
+ @Override
+ public void updateEncryptionPassword(final int type, final byte[] password) {
+ if (!hasSecureLockScreen() && password != null && password.length != 0) {
+ throw new UnsupportedOperationException(
+ "This operation requires the lock screen feature.");
+ }
+ if (!isDeviceEncryptionEnabled()) {
+ return;
+ }
+ final IBinder service = ServiceManager.getService("mount");
+ if (service == null) {
+ Slog.e(TAG, "Could not find the mount service to update the encryption password");
+ return;
+ }
+
+ // TODO(b/120484642): This is a location where we still use a String for vold
+ String passwordString = password != null ? new String(password) : null;
+ mHandler.post(() -> {
+ IStorageManager storageManager = mInjector.getStorageManager();
+ try {
+ storageManager.changeEncryptionPassword(type, passwordString);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error changing encryption password", e);
+ }
+ });
+ }
+
@VisibleForTesting /** Note: this method is overridden in unit tests */
protected void tieProfileLockToParent(int userId, LockscreenCredential password) {
if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId);
@@ -1952,10 +2115,16 @@
@Nullable
public VerifyCredentialResponse verifyCredential(LockscreenCredential credential,
int userId, int flags) {
- checkPasswordReadPermission();
+ if (!hasPermission(PERMISSION) && !hasPermission(SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS)) {
+ throw new SecurityException(
+ "verifyCredential requires SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS or "
+ + PERMISSION);
+ }
+ final long identity = Binder.clearCallingIdentity();
try {
return doVerifyCredential(credential, userId, null /* progressCallback */, flags);
} finally {
+ Binder.restoreCallingIdentity(identity);
scheduleGc();
}
}
@@ -3436,8 +3605,12 @@
throw new UnsupportedOperationException(
"This operation requires secure lock screen feature.");
}
- return LockSettingsService.this.setLockCredentialWithToken(
- credential, tokenHandle, token, userId);
+ if (!LockSettingsService.this.setLockCredentialWithToken(
+ credential, tokenHandle, token, userId)) {
+ return false;
+ }
+ onPostPasswordChanged(credential, userId);
+ return true;
}
@Override
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 5286bce..ba1e23c 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -16,7 +16,9 @@
package com.android.server.media;
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+import static android.content.Intent.ACTION_SCREEN_OFF;
+import static android.content.Intent.ACTION_SCREEN_ON;
import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR;
import static android.media.MediaRouter2Utils.getOriginalId;
import static android.media.MediaRouter2Utils.getProviderId;
@@ -26,7 +28,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.media.IMediaRouter2;
import android.media.IMediaRouter2Manager;
@@ -41,6 +46,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -62,6 +68,7 @@
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
/**
* Implements features related to {@link android.media.MediaRouter2} and
@@ -74,12 +81,13 @@
// TODO: (In Android S or later) if we add callback methods for generic failures
// in MediaRouter2, remove this constant and replace the usages with the real request IDs.
private static final long DUMMY_REQUEST_ID = -1;
- private static final int PACKAGE_IMPORTANCE_FOR_DISCOVERY = IMPORTANCE_FOREGROUND;
+ private static final int PACKAGE_IMPORTANCE_FOR_DISCOVERY = IMPORTANCE_FOREGROUND_SERVICE;
private final Context mContext;
private final Object mLock = new Object();
final AtomicInteger mNextRouterOrManagerId = new AtomicInteger(1);
final ActivityManager mActivityManager;
+ final PowerManager mPowerManager;
@GuardedBy("mLock")
private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
@@ -100,11 +108,32 @@
}
};
+ private final BroadcastReceiver mScreenOnOffReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mLock) {
+ final int count = mUserRecords.size();
+ for (int i = 0; i < count; i++) {
+ UserHandler userHandler = mUserRecords.valueAt(i).mHandler;
+ userHandler.sendMessage(PooledLambda.obtainMessage(
+ UserHandler::updateDiscoveryPreferenceOnHandler, userHandler));
+ }
+ }
+ }
+ };
+
MediaRouter2ServiceImpl(Context context) {
mContext = context;
mActivityManager = mContext.getSystemService(ActivityManager.class);
mActivityManager.addOnUidImportanceListener(mOnUidImportanceListener,
PACKAGE_IMPORTANCE_FOR_DISCOVERY);
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+
+ IntentFilter screenOnOffIntentFilter = new IntentFilter();
+ screenOnOffIntentFilter.addAction(ACTION_SCREEN_ON);
+ screenOnOffIntentFilter.addAction(ACTION_SCREEN_OFF);
+
+ mContext.registerReceiver(mScreenOnOffReceiver, screenOnOffIntentFilter);
}
////////////////////////////////////////////////////////////////
@@ -2121,19 +2150,26 @@
if (service == null) {
return;
}
- List<RouteDiscoveryPreference> discoveryPreferences = new ArrayList<>();
+ List<RouteDiscoveryPreference> discoveryPreferences = Collections.emptyList();
List<RouterRecord> routerRecords = getRouterRecords();
List<ManagerRecord> managerRecords = getManagerRecords();
- boolean isAnyManagerScanning =
- managerRecords.stream().anyMatch(manager -> manager.mIsScanning
- && service.mActivityManager.getPackageImportance(manager.mPackageName)
- <= PACKAGE_IMPORTANCE_FOR_DISCOVERY);
- for (RouterRecord routerRecord : routerRecords) {
- if (isAnyManagerScanning
- || service.mActivityManager.getPackageImportance(routerRecord.mPackageName)
- <= PACKAGE_IMPORTANCE_FOR_DISCOVERY) {
- discoveryPreferences.add(routerRecord.mDiscoveryPreference);
+ if (service.mPowerManager.isInteractive()) {
+ boolean isManagerScanning = managerRecords.stream().anyMatch(manager ->
+ manager.mIsScanning && service.mActivityManager
+ .getPackageImportance(manager.mPackageName)
+ <= PACKAGE_IMPORTANCE_FOR_DISCOVERY);
+
+ if (isManagerScanning) {
+ discoveryPreferences = routerRecords.stream()
+ .map(record -> record.mDiscoveryPreference)
+ .collect(Collectors.toList());
+ } else {
+ discoveryPreferences = routerRecords.stream().filter(record ->
+ service.mActivityManager.getPackageImportance(record.mPackageName)
+ <= PACKAGE_IMPORTANCE_FOR_DISCOVERY)
+ .map(record -> record.mDiscoveryPreference)
+ .collect(Collectors.toList());
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index bcd9007..05922b3 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -926,10 +926,11 @@
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
try {
- // TODO: There shouldn't be a need to receive callback for all changes.
- mActivityManager.registerUidObserver(mUidObserver,
- ActivityManager.UID_OBSERVER_PROCSTATE|ActivityManager.UID_OBSERVER_GONE,
- ActivityManager.PROCESS_STATE_UNKNOWN, "android");
+ final int changes = ActivityManager.UID_OBSERVER_PROCSTATE
+ | ActivityManager.UID_OBSERVER_GONE
+ | ActivityManager.UID_OBSERVER_CAPABILITY;
+ mActivityManager.registerUidObserver(mUidObserver, changes,
+ NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE, "android");
mNetworkManager.registerObserver(mAlertObserver);
} catch (RemoteException e) {
// ignored; both services live in system_server
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index f5d6489..1050835 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -27,6 +27,7 @@
import android.service.notification.IConditionProvider;
import android.service.notification.NotificationListenerService;
import android.service.notification.ZenModeConfig;
+import android.util.Log;
import android.util.Slog;
import java.io.PrintWriter;
@@ -122,8 +123,11 @@
public static void traceSetNotificationPolicy(String pkg, int targetSdk,
NotificationManager.Policy policy) {
- append(TYPE_SET_NOTIFICATION_POLICY, "pkg=" + pkg + " targetSdk=" + targetSdk
- + " NotificationPolicy=" + policy.toString());
+ String policyLog = "pkg=" + pkg + " targetSdk=" + targetSdk
+ + " NotificationPolicy=" + policy.toString();
+ append(TYPE_SET_NOTIFICATION_POLICY, policyLog);
+ // TODO(b/180205791): remove when we can better surface apps that are changing policy
+ Log.d(TAG, "Zen Policy Changed: " + policyLog);
}
public static void traceSubscribe(Uri uri, IConditionProvider provider, RemoteException e) {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index c236b4d..fa126a4 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -267,7 +267,7 @@
mArtStatsLogger,
sessionId,
compilerFilter,
- sharedGid,
+ pkg.getUid(),
packageStats.getCompileTime(path),
dexMetadataPath,
options.getCompilationReason(),
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index a6d4ed9..e532790 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -4157,7 +4157,7 @@
if (stageDir != null && !params.isStaged) {
try {
if (incrementalFileStorages != null) {
- incrementalFileStorages.cleanUp();
+ incrementalFileStorages.cleanUpAndMarkComplete();
}
mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
} catch (InstallerException ignored) {
@@ -4183,7 +4183,7 @@
}
try {
if (incrementalFileStorages != null) {
- incrementalFileStorages.cleanUp();
+ incrementalFileStorages.cleanUpAndMarkComplete();
}
mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
} catch (InstallerException ignored) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 30ce57f..9477464 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5966,7 +5966,8 @@
backgroundHandler,
SYSTEM_PARTITIONS,
(i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock),
- (i, pm) -> PermissionManagerService.create(context),
+ (i, pm) -> PermissionManagerService.create(context,
+ i.getSystemConfig().getAvailableFeatures()),
(i, pm) -> new UserManagerService(context, pm,
new UserDataPreparer(installer, installLock, context, onlyCore),
lock),
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index ef37201..091d820 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -127,18 +127,12 @@
private static final Map<String, Integer> ISA_MAP = new HashMap();
static {
- COMPILE_FILTER_MAP.put("arm", ArtStatsLog.
- ART_DATUM_REPORTED__ISA__ART_ISA_ARM);
- COMPILE_FILTER_MAP.put("arm64", ArtStatsLog.
- ART_DATUM_REPORTED__ISA__ART_ISA_ARM64);
- COMPILE_FILTER_MAP.put("x86", ArtStatsLog.
- ART_DATUM_REPORTED__ISA__ART_ISA_X86);
- COMPILE_FILTER_MAP.put("x86_64", ArtStatsLog.
- ART_DATUM_REPORTED__ISA__ART_ISA_X86_64);
- COMPILE_FILTER_MAP.put("mips", ArtStatsLog.
- ART_DATUM_REPORTED__ISA__ART_ISA_MIPS);
- COMPILE_FILTER_MAP.put("mips64", ArtStatsLog.
- ART_DATUM_REPORTED__ISA__ART_ISA_MIPS64);
+ ISA_MAP.put("arm", ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_ARM);
+ ISA_MAP.put("arm64", ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_ARM64);
+ ISA_MAP.put("x86", ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_X86);
+ ISA_MAP.put("x86_64", ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_X86_64);
+ ISA_MAP.put("mips", ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_MIPS);
+ ISA_MAP.put("mips64", ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_MIPS64);
}
public static void writeStatsLog(
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 310ef23..2d1178a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -69,8 +69,10 @@
import android.app.admin.DevicePolicyManagerInternal;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.PermissionGroupInfoFlags;
import android.content.pm.PackageManager.PermissionInfoFlags;
@@ -85,7 +87,6 @@
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.metrics.LogMaker;
import android.os.AsyncTask;
-import android.content.AttributionSource;
import android.os.Binder;
import android.os.Build;
import android.os.Debug;
@@ -177,6 +178,10 @@
private static final long BACKUP_TIMEOUT_MILLIS = SECONDS.toMillis(60);
+ // For automotive products, CarService enforces allow-listing of the privileged permissions
+ // com.android.car is the package name which declares auto specific permissions
+ private static final String CAR_PACKAGE_NAME = "com.android.car";
+
/** Cap the size of permission trees that 3rd party apps can define; in characters of text */
private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768;
/** Empty array to avoid allocations */
@@ -210,6 +215,10 @@
STORAGE_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION);
}
+ /** Set of source package names for Privileged Permission Allowlist */
+ private final ArraySet<String> mPrivilegedPermissionAllowlistSourcePackageNames =
+ new ArraySet<>();
+
/** Lock to protect internal data access */
private final Object mLock = new Object();
@@ -356,7 +365,8 @@
}
};
- PermissionManagerService(@NonNull Context context) {
+ PermissionManagerService(@NonNull Context context,
+ @NonNull ArrayMap<String, FeatureInfo> availableFeatures) {
// The package info cache is the cache for package and permission information.
// Disable the package info and package permission caches locally but leave the
// checkPermission cache active.
@@ -368,6 +378,13 @@
mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
mAppOpsManager = context.getSystemService(AppOpsManager.class);
+ mPrivilegedPermissionAllowlistSourcePackageNames.add(PLATFORM_PACKAGE_NAME);
+ // PackageManager.hasSystemFeature() is not used here because PackageManagerService
+ // isn't ready yet.
+ if (availableFeatures.containsKey(PackageManager.FEATURE_AUTOMOTIVE)) {
+ mPrivilegedPermissionAllowlistSourcePackageNames.add(CAR_PACKAGE_NAME);
+ }
+
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
@@ -422,7 +439,8 @@
* lock created by the permission manager itself.
*/
@NonNull
- public static PermissionManagerServiceInternal create(@NonNull Context context) {
+ public static PermissionManagerServiceInternal create(@NonNull Context context,
+ ArrayMap<String, FeatureInfo> availableFeatures) {
final PermissionManagerServiceInternal permMgrInt =
LocalServices.getService(PermissionManagerServiceInternal.class);
if (permMgrInt != null) {
@@ -431,7 +449,7 @@
PermissionManagerService permissionService =
(PermissionManagerService) ServiceManager.getService("permissionmgr");
if (permissionService == null) {
- permissionService = new PermissionManagerService(context);
+ permissionService = new PermissionManagerService(context, availableFeatures);
ServiceManager.addService("permissionmgr", permissionService);
}
return LocalServices.getService(PermissionManagerServiceInternal.class);
@@ -3318,7 +3336,8 @@
if (!pkg.isPrivileged()) {
return true;
}
- if (!Objects.equals(permission.getPackageName(), PLATFORM_PACKAGE_NAME)) {
+ if (!mPrivilegedPermissionAllowlistSourcePackageNames
+ .contains(permission.getPackageName())) {
return true;
}
final String permissionName = permission.getName();
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index fa27b4b4..25709d4 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -21,11 +21,22 @@
import android.app.AppOpsManager;
import android.app.AppOpsManagerInternal;
import android.app.SyncNotedAppOp;
+import android.app.role.RoleManager;
import android.content.AttributionSource;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.location.LocationManagerInternal;
+import android.net.Uri;
import android.os.IBinder;
+import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.function.HeptFunction;
@@ -35,6 +46,8 @@
import com.android.internal.util.function.TriFunction;
import com.android.server.LocalServices;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -42,9 +55,21 @@
* This class defines policy for special behaviors around app ops.
*/
public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegate {
+ private static final String LOG_TAG = AppOpsPolicy.class.getName();
+
+ private static final String ACTIVITY_RECOGNITION_TAGS =
+ "android:activity_recognition_allow_listed_tags";
+ private static final String ACTIVITY_RECOGNITION_TAGS_SEPARATOR = ";";
+
@NonNull
private final Object mLock = new Object();
+ @NonNull
+ private final Context mContext;
+
+ @NonNull
+ private final RoleManager mRoleManager;
+
/**
* The locking policy around the location tags is a bit special. Since we want to
* avoid grabbing the lock on every op note we are taking the approach where the
@@ -60,48 +85,57 @@
private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>> mLocationTags =
new ConcurrentHashMap<>();
- public AppOpsPolicy() {
+ @GuardedBy("mLock - writes only - see above")
+ @NonNull
+ private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>>
+ mActivityRecognitionTags = new ConcurrentHashMap<>();
+
+ public AppOpsPolicy(@NonNull Context context) {
+ mContext = context;
+ mRoleManager = mContext.getSystemService(RoleManager.class);
+
final LocationManagerInternal locationManagerInternal = LocalServices.getService(
LocationManagerInternal.class);
locationManagerInternal.setOnProviderLocationTagsChangeListener((providerTagInfo) -> {
synchronized (mLock) {
- final int uid = providerTagInfo.getUid();
- // We make a copy of the per UID state to limit our mutation to one
- // operation in the underlying concurrent data structure.
- ArrayMap<String, ArraySet<String>> uidTags = mLocationTags.get(uid);
- if (uidTags != null) {
- uidTags = new ArrayMap<>(uidTags);
- }
-
- final String packageName = providerTagInfo.getPackageName();
- ArraySet<String> packageTags = (uidTags != null) ? uidTags.get(packageName) : null;
- if (packageTags != null) {
- packageTags = new ArraySet<>(packageTags);
- }
-
- final Set<String> providerTags = providerTagInfo.getTags();
- if (providerTags != null && !providerTags.isEmpty()) {
- if (packageTags != null) {
- packageTags.clear();
- packageTags.addAll(providerTags);
- } else {
- packageTags = new ArraySet<>(providerTags);
- }
- if (uidTags == null) {
- uidTags = new ArrayMap<>();
- }
- uidTags.put(packageName, packageTags);
- mLocationTags.put(uid, uidTags);
- } else if (uidTags != null) {
- uidTags.remove(packageName);
- if (!uidTags.isEmpty()) {
- mLocationTags.put(uid, uidTags);
- } else {
- mLocationTags.remove(uid);
- }
- }
+ updateAllowListedTagsForPackageLocked(providerTagInfo.getUid(),
+ providerTagInfo.getPackageName(), providerTagInfo.getTags(),
+ mLocationTags);
}
});
+
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ intentFilter.addDataScheme("package");
+
+ context.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final Uri uri = intent.getData();
+ if (uri == null) {
+ return;
+ }
+ final String packageName = uri.getSchemeSpecificPart();
+ if (TextUtils.isEmpty(packageName)) {
+ return;
+ }
+ final List<String> activityRecognizers = mRoleManager.getRoleHolders(
+ RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER);
+ if (activityRecognizers.contains(packageName)) {
+ updateActivityRecognizerTags(packageName);
+ }
+ }
+ }, UserHandle.SYSTEM, intentFilter, null, null);
+
+ mRoleManager.addOnRoleHoldersChangedListenerAsUser(context.getMainExecutor(),
+ (String roleName, UserHandle user) -> {
+ if (RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER.equals(roleName)) {
+ initializeActivityRecognizersTags();
+ }
+ }, UserHandle.SYSTEM);
+
+ initializeActivityRecognizersTags();
}
@Override
@@ -121,7 +155,7 @@
@Nullable String attributionTag, boolean shouldCollectAsyncNotedOp, @Nullable
String message, boolean shouldCollectMessage, @NonNull HeptFunction<Integer, Integer,
String, String, Boolean, String, Boolean, SyncNotedAppOp> superImpl) {
- return superImpl.apply(resolveOpCode(code, uid, packageName, attributionTag), uid,
+ return superImpl.apply(resolveDatasourceOp(code, uid, packageName, attributionTag), uid,
packageName, attributionTag, shouldCollectAsyncNotedOp,
message, shouldCollectMessage);
}
@@ -132,7 +166,7 @@
boolean shouldCollectMessage, boolean skipProxyOperation, @NonNull HexFunction<Integer,
AttributionSource, Boolean, String, Boolean, Boolean,
SyncNotedAppOp> superImpl) {
- return superImpl.apply(resolveOpCode(code, attributionSource.getUid(),
+ return superImpl.apply(resolveDatasourceOp(code, attributionSource.getUid(),
attributionSource.getPackageName(), attributionSource.getAttributionTag()),
attributionSource, shouldCollectAsyncNotedOp, message, shouldCollectMessage,
skipProxyOperation);
@@ -144,7 +178,7 @@
boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage,
boolean skipProxyOperation, @NonNull OctFunction<IBinder, Integer, AttributionSource,
Boolean, Boolean, String, Boolean, Boolean, SyncNotedAppOp> superImpl) {
- return superImpl.apply(token, resolveOpCode(code, attributionSource.getUid(),
+ return superImpl.apply(token, resolveDatasourceOp(code, attributionSource.getUid(),
attributionSource.getPackageName(), attributionSource.getAttributionTag()),
attributionSource, startIfModeDefault, shouldCollectAsyncNotedOp, message,
shouldCollectMessage, skipProxyOperation);
@@ -154,36 +188,129 @@
public void finishProxyOperation(IBinder clientId, int code,
@NonNull AttributionSource attributionSource,
@NonNull TriFunction<IBinder, Integer, AttributionSource, Void> superImpl) {
- superImpl.apply(clientId, resolveOpCode(code, attributionSource.getUid(),
+ superImpl.apply(clientId, resolveDatasourceOp(code, attributionSource.getUid(),
attributionSource.getPackageName(), attributionSource.getAttributionTag()),
attributionSource);
}
- private int resolveOpCode(int code, int uid, @NonNull String packageName,
+ private int resolveDatasourceOp(int code, int uid, @NonNull String packageName,
@Nullable String attributionTag) {
- if (isHandledOp(code) && attributionTag != null) {
- // Only a single lookup from the underlying concurrent data structure
- final ArrayMap<String, ArraySet<String>> uidTags = mLocationTags.get(uid);
- if (uidTags != null) {
- final ArraySet<String> packageTags = uidTags.get(packageName);
- if (packageTags != null && packageTags.contains(attributionTag)) {
- return resolveHandledOp(code);
+ if (attributionTag == null) {
+ return code;
+ }
+ int resolvedCode = resolveLocationOp(code);
+ if (resolvedCode != code) {
+ if (isDatasourceAttributionTag(uid, packageName, attributionTag,
+ mLocationTags)) {
+ return resolvedCode;
+ }
+ } else {
+ resolvedCode = resolveArOp(code);
+ if (resolvedCode != code) {
+ if (isDatasourceAttributionTag(uid, packageName, attributionTag,
+ mActivityRecognitionTags)) {
+ return resolvedCode;
}
}
}
return code;
}
- private static boolean isHandledOp(int code) {
- switch (code) {
- case AppOpsManager.OP_FINE_LOCATION:
- case AppOpsManager.OP_COARSE_LOCATION:
+ private void initializeActivityRecognizersTags() {
+ final List<String> activityRecognizers = mRoleManager.getRoleHolders(
+ RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER);
+ final int recognizerCount = activityRecognizers.size();
+ if (recognizerCount > 0) {
+ for (int i = 0; i < recognizerCount; i++) {
+ final String activityRecognizer = activityRecognizers.get(i);
+ updateActivityRecognizerTags(activityRecognizer);
+ }
+ } else {
+ clearActivityRecognitionTags();
+ }
+ }
+
+ private void clearActivityRecognitionTags() {
+ synchronized (mLock) {
+ mActivityRecognitionTags.clear();
+ }
+ }
+
+ private void updateActivityRecognizerTags(@NonNull String activityRecognizer) {
+ try {
+ final ApplicationInfo recognizerAppInfo = mContext.getPackageManager()
+ .getApplicationInfoAsUser(activityRecognizer, PackageManager.GET_META_DATA,
+ UserHandle.USER_SYSTEM);
+ if (recognizerAppInfo.metaData == null) {
+ return;
+ }
+ final String tagsList = recognizerAppInfo.metaData.getString(ACTIVITY_RECOGNITION_TAGS);
+ if (tagsList != null) {
+ final String[] tags = tagsList.split(ACTIVITY_RECOGNITION_TAGS_SEPARATOR);
+ synchronized (mLock) {
+ updateAllowListedTagsForPackageLocked(recognizerAppInfo.uid,
+ recognizerAppInfo.packageName, new ArraySet<>(tags),
+ mActivityRecognitionTags);
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.wtf(LOG_TAG, "Missing " + RoleManager.ROLE_SYSTEM_ACTIVITY_RECOGNIZER
+ + " role holder package " + activityRecognizer);
+ }
+ }
+
+ private static void updateAllowListedTagsForPackageLocked(int uid, String packageName,
+ Set<String> allowListedTags, ConcurrentHashMap<Integer, ArrayMap<String,
+ ArraySet<String>>> datastore) {
+ // We make a copy of the per UID state to limit our mutation to one
+ // operation in the underlying concurrent data structure.
+ ArrayMap<String, ArraySet<String>> uidTags = datastore.get(uid);
+ if (uidTags != null) {
+ uidTags = new ArrayMap<>(uidTags);
+ }
+
+ ArraySet<String> packageTags = (uidTags != null) ? uidTags.get(packageName) : null;
+ if (packageTags != null) {
+ packageTags = new ArraySet<>(packageTags);
+ }
+
+ if (allowListedTags != null && !allowListedTags.isEmpty()) {
+ if (packageTags != null) {
+ packageTags.clear();
+ packageTags.addAll(allowListedTags);
+ } else {
+ packageTags = new ArraySet<>(allowListedTags);
+ }
+ if (uidTags == null) {
+ uidTags = new ArrayMap<>();
+ }
+ uidTags.put(packageName, packageTags);
+ datastore.put(uid, uidTags);
+ } else if (uidTags != null) {
+ uidTags.remove(packageName);
+ if (!uidTags.isEmpty()) {
+ datastore.put(uid, uidTags);
+ } else {
+ datastore.remove(uid);
+ }
+ }
+ }
+
+ private static boolean isDatasourceAttributionTag(int uid, @NonNull String packageName,
+ @NonNull String attributionTag, @NonNull Map<Integer, ArrayMap<String,
+ ArraySet<String>>> mappedOps) {
+ // Only a single lookup from the underlying concurrent data structure
+ final ArrayMap<String, ArraySet<String>> uidTags = mappedOps.get(uid);
+ if (uidTags != null) {
+ final ArraySet<String> packageTags = uidTags.get(packageName);
+ if (packageTags != null && packageTags.contains(attributionTag)) {
return true;
+ }
}
return false;
}
- private static int resolveHandledOp(int code) {
+ private static int resolveLocationOp(int code) {
switch (code) {
case AppOpsManager.OP_FINE_LOCATION:
return AppOpsManager.OP_FINE_LOCATION_SOURCE;
@@ -192,4 +319,11 @@
}
return code;
}
+
+ private static int resolveArOp(int code) {
+ if (code == AppOpsManager.OP_ACTIVITY_RECOGNITION) {
+ return AppOpsManager.OP_ACTIVITY_RECOGNITION_SOURCE;
+ }
+ return code;
+ }
}
diff --git a/services/core/java/com/android/server/timedetector/ServerFlags.java b/services/core/java/com/android/server/timedetector/ServerFlags.java
index 52ff48b..d91e9c2 100644
--- a/services/core/java/com/android/server/timedetector/ServerFlags.java
+++ b/services/core/java/com/android/server/timedetector/ServerFlags.java
@@ -28,6 +28,8 @@
import com.android.server.timezonedetector.ConfigurationChangeListener;
import com.android.server.timezonedetector.ServiceConfigAccessor;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.time.Duration;
import java.util.Map;
import java.util.Objects;
@@ -53,14 +55,15 @@
*/
@StringDef(prefix = "KEY_", value = {
KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
- KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
- KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
+ KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
+ KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
})
+ @Retention(RetentionPolicy.SOURCE)
@interface DeviceConfigKey {}
/**
@@ -75,19 +78,19 @@
/**
* The key for the server flag that can override the device config for whether the primary
- * location time zone provider is enabled or disabled.
+ * location time zone provider is enabled, disabled, or (for testing) in simulation mode.
*/
@DeviceConfigKey
- public static final String KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE =
- "primary_location_time_zone_provider_enabled_override";
+ public static final String KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE =
+ "primary_location_time_zone_provider_mode_override";
/**
* The key for the server flag that can override the device config for whether the secondary
- * location time zone provider is enabled or disabled.
+ * location time zone provider is enabled or disabled, or (for testing) in simulation mode.
*/
@DeviceConfigKey
- public static final String KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE =
- "secondary_location_time_zone_provider_enabled_override";
+ public static final String KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE =
+ "secondary_location_time_zone_provider_mode_override";
/**
* The key for the minimum delay after location time zone detection has been enabled before the
@@ -196,6 +199,16 @@
}
/**
+ * Returns an optional string value from {@link DeviceConfig} from the system_time
+ * namespace, returns {@link Optional#empty()} if there is no explicit value set.
+ */
+ @NonNull
+ public Optional<String> getOptionalString(@DeviceConfigKey String key) {
+ String value = DeviceConfig.getProperty(NAMESPACE_SYSTEM_TIME, key);
+ return Optional.ofNullable(value);
+ }
+
+ /**
* Returns an optional boolean value from {@link DeviceConfig} from the system_time
* namespace, returns {@link Optional#empty()} if there is no explicit value set.
*/
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
index 222e852..50d37f4 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringDef;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -27,6 +28,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.server.timedetector.ServerFlags;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.time.Duration;
import java.util.Collections;
import java.util.Objects;
@@ -40,13 +45,38 @@
*/
public final class ServiceConfigAccessor {
+ @StringDef(prefix = "PROVIDER_MODE_",
+ value = { PROVIDER_MODE_SIMULATED, PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED})
+ @Retention(RetentionPolicy.SOURCE)
+ @Target(ElementType.TYPE_USE)
+ @interface ProviderMode {}
+
+ /**
+ * The "simulated" provider mode.
+ * For use with {@link #getPrimaryLocationTimeZoneProviderMode()} and {@link
+ * #getSecondaryLocationTimeZoneProviderMode()}.
+ */
+ public static final @ProviderMode String PROVIDER_MODE_SIMULATED = "simulated";
+
+ /**
+ * The "disabled" provider mode. For use with {@link #getPrimaryLocationTimeZoneProviderMode()}
+ * and {@link #getSecondaryLocationTimeZoneProviderMode()}.
+ */
+ public static final @ProviderMode String PROVIDER_MODE_DISABLED = "disabled";
+
+ /**
+ * The "enabled" provider mode. For use with {@link #getPrimaryLocationTimeZoneProviderMode()}
+ * and {@link #getSecondaryLocationTimeZoneProviderMode()}.
+ */
+ public static final @ProviderMode String PROVIDER_MODE_ENABLED = "enabled";
+
private static final Set<String> SERVER_FLAGS_KEYS_TO_WATCH = Collections.unmodifiableSet(
new ArraySet<>(new String[] {
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
- ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
- ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
+ ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
+ ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS
@@ -139,14 +169,28 @@
/**
* Returns {@code true} if the location-based time zone detection feature is supported on the
- * device. This can be used during feature testing on builds that are capable of location time
- * zone detection to enable / disable the feature for some users.
+ * device.
*/
public boolean isGeoTimeZoneDetectionFeatureSupported() {
+ // For the feature to be enabled it must:
+ // 1) Be turned on in config.
+ // 2) Not be turned off via a server flag.
+ // 3) There must be at least one location time zone provider enabled / configured.
return mGeoDetectionFeatureSupportedInConfig
- && isGeoTimeZoneDetectionFeatureSupportedInternal();
+ && isGeoTimeZoneDetectionFeatureSupportedInternal()
+ && atLeastOneProviderIsEnabled();
}
+ private boolean atLeastOneProviderIsEnabled() {
+ return !(Objects.equals(getPrimaryLocationTimeZoneProviderMode(), PROVIDER_MODE_DISABLED)
+ && Objects.equals(getSecondaryLocationTimeZoneProviderMode(),
+ PROVIDER_MODE_DISABLED));
+ }
+
+ /**
+ * Returns {@code true} if the location-based time zone detection feature is not explicitly
+ * disabled by a server flag.
+ */
private boolean isGeoTimeZoneDetectionFeatureSupportedInternal() {
final boolean defaultEnabled = true;
return mServerFlags.getBoolean(
@@ -154,42 +198,49 @@
defaultEnabled);
}
+ @NonNull
+ public String getPrimaryLocationTimeZoneProviderPackageName() {
+ return mContext.getResources().getString(
+ R.string.config_primaryLocationTimeZoneProviderPackageName);
+ }
+
+ @NonNull
+ public String getSecondaryLocationTimeZoneProviderPackageName() {
+ return mContext.getResources().getString(
+ R.string.config_secondaryLocationTimeZoneProviderPackageName);
+ }
+
/**
* Returns {@code true} if the primary location time zone provider can be used.
*/
- public boolean isPrimaryLocationTimeZoneProviderEnabled() {
- return getPrimaryLocationTimeZoneProviderEnabledOverride()
- .orElse(isPrimaryLocationTimeZoneProviderEnabledInConfig());
- }
-
- private boolean isPrimaryLocationTimeZoneProviderEnabledInConfig() {
- int providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider;
- return getConfigBoolean(providerEnabledConfigId);
+ @NonNull
+ public @ProviderMode String getPrimaryLocationTimeZoneProviderMode() {
+ return mServerFlags.getOptionalString(
+ ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE)
+ .orElse(getPrimaryLocationTimeZoneProviderModeFromConfig());
}
@NonNull
- private Optional<Boolean> getPrimaryLocationTimeZoneProviderEnabledOverride() {
- return mServerFlags.getOptionalBoolean(
- ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE);
+ private @ProviderMode String getPrimaryLocationTimeZoneProviderModeFromConfig() {
+ int providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider;
+ return getConfigBoolean(providerEnabledConfigId)
+ ? PROVIDER_MODE_ENABLED : PROVIDER_MODE_DISABLED;
}
/**
- * Returns {@code true} if the secondary location time zone provider can be used.
+ * Returns the mode for the secondary location time zone provider can be used.
*/
- public boolean isSecondaryLocationTimeZoneProviderEnabled() {
- return getSecondaryLocationTimeZoneProviderEnabledOverride()
- .orElse(isSecondaryLocationTimeZoneProviderEnabledInConfig());
- }
-
- private boolean isSecondaryLocationTimeZoneProviderEnabledInConfig() {
- int providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider;
- return getConfigBoolean(providerEnabledConfigId);
+ public @ProviderMode String getSecondaryLocationTimeZoneProviderMode() {
+ return mServerFlags.getOptionalString(
+ ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE)
+ .orElse(getSecondaryLocationTimeZoneProviderModeFromConfig());
}
@NonNull
- private Optional<Boolean> getSecondaryLocationTimeZoneProviderEnabledOverride() {
- return mServerFlags.getOptionalBoolean(
- ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE);
+ private @ProviderMode String getSecondaryLocationTimeZoneProviderModeFromConfig() {
+ int providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider;
+ return getConfigBoolean(providerEnabledConfigId)
+ ? PROVIDER_MODE_ENABLED : PROVIDER_MODE_DISABLED;
}
/**
diff --git a/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
index fb2a184..d2190fd 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
@@ -30,6 +30,7 @@
import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN;
import android.annotation.DurationMillisLong;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.RemoteCallback;
@@ -604,14 +605,14 @@
* known provider, then the command is logged and discarded.
*/
void handleProviderTestCommand(
- @NonNull String providerName, @NonNull TestCommand testCommand,
+ @IntRange(from = 0, to = 1) int providerIndex, @NonNull TestCommand testCommand,
@Nullable RemoteCallback callback) {
mThreadingDomain.assertCurrentThread();
- LocationTimeZoneProvider targetProvider = getLocationTimeZoneProvider(providerName);
+ LocationTimeZoneProvider targetProvider = getLocationTimeZoneProvider(providerIndex);
if (targetProvider == null) {
warnLog("Unable to process test command:"
- + " providerName=" + providerName + ", testCommand=" + testCommand);
+ + " providerIndex=" + providerIndex + ", testCommand=" + testCommand);
return;
}
@@ -620,7 +621,7 @@
targetProvider.handleTestCommand(testCommand, callback);
} catch (Exception e) {
warnLog("Unable to process test command:"
- + " providerName=" + providerName + ", testCommand=" + testCommand, e);
+ + " providerIndex=" + providerIndex + ", testCommand=" + testCommand, e);
}
}
}
@@ -658,14 +659,15 @@
}
@Nullable
- private LocationTimeZoneProvider getLocationTimeZoneProvider(@NonNull String providerName) {
+ private LocationTimeZoneProvider getLocationTimeZoneProvider(
+ @IntRange(from = 0, to = 1) int providerIndex) {
LocationTimeZoneProvider targetProvider;
- if (Objects.equals(mPrimaryProvider.getName(), providerName)) {
+ if (providerIndex == 0) {
targetProvider = mPrimaryProvider;
- } else if (Objects.equals(mSecondaryProvider.getName(), providerName)) {
+ } else if (providerIndex == 1) {
targetProvider = mSecondaryProvider;
} else {
- warnLog("Bad providerName=" + providerName);
+ warnLog("Bad providerIndex=" + providerIndex);
targetProvider = null;
}
return targetProvider;
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index ca4a640..d8d44d4 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -16,13 +16,12 @@
package com.android.server.timezonedetector.location;
-import static android.app.time.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME;
-import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DISABLED;
-import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE;
-import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED;
-import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME;
import static android.app.time.LocationTimeZoneManager.SERVICE_NAME;
+import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_DISABLED;
+import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_SIMULATED;
+
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -37,12 +36,12 @@
import android.util.Log;
import android.util.Slog;
-import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.SystemService;
+import com.android.server.timezonedetector.Dumpable;
import com.android.server.timezonedetector.ServiceConfigAccessor;
import com.android.server.timezonedetector.TimeZoneDetectorInternal;
import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderMetricsLogger;
@@ -77,15 +76,7 @@
* bound (ensuring no real location events will be received) and simulated events / behaviors
* can be injected via the command line.
*
- * <p>To enter simulation mode for a provider, use {@code adb shell cmd location_time_zone_manager
- * set_provider_mode_override <provider name> simulated} and restart the service with {@code
- * adb shell cmd location_time_zone_manager stop} and {@code adb shell cmd
- * location_time_zone_manager start}.
- *
- * <p>e.g. {@code adb shell cmd location_time_zone_manager set_provider_mode_override primary
- * simulated}.
- *
- * <p>See {@code adb shell cmd location_time_zone_manager help}" for more options.
+ * <p>See {@code adb shell cmd location_time_zone_manager help}" for details and more options.
*/
public class LocationTimeZoneManagerService extends Binder {
@@ -139,11 +130,15 @@
private static final String ATTRIBUTION_TAG = "LocationTimeZoneService";
- private static final String PRIMARY_LOCATION_TIME_ZONE_SERVICE_ACTION =
- TimeZoneProviderService.PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE;
- private static final String SECONDARY_LOCATION_TIME_ZONE_SERVICE_ACTION =
- TimeZoneProviderService.SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE;
+ @GuardedBy("mSharedLock")
+ private final ProviderConfig mPrimaryProviderConfig = new ProviderConfig(
+ 0 /* index */, "primary",
+ TimeZoneProviderService.PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE);
+ @GuardedBy("mSharedLock")
+ private final ProviderConfig mSecondaryProviderConfig = new ProviderConfig(
+ 1 /* index */, "secondary",
+ TimeZoneProviderService.SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE);
@NonNull private final Context mContext;
@@ -173,14 +168,6 @@
@GuardedBy("mSharedLock")
private ControllerEnvironmentImpl mEnvironment;
- @GuardedBy("mSharedLock")
- @NonNull
- private String mPrimaryProviderModeOverride = PROVIDER_MODE_OVERRIDE_NONE;
-
- @GuardedBy("mSharedLock")
- @NonNull
- private String mSecondaryProviderModeOverride = PROVIDER_MODE_OVERRIDE_NONE;
-
LocationTimeZoneManagerService(Context context) {
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
mHandler = FgThread.getHandler();
@@ -208,9 +195,14 @@
mThreadingDomain.assertCurrentThread();
synchronized (mSharedLock) {
- // Stop and start the service, waiting until completion.
- stopOnDomainThread();
- startOnDomainThread();
+ // Avoid starting the service if it is currently stopped. This is required because
+ // server flags are used by tests to set behavior with the service stopped, and we don't
+ // want the service being restarted after each flag is set.
+ if (mLocationTimeZoneDetectorController != null) {
+ // Stop and start the service, waiting until completion.
+ stopOnDomainThread();
+ startOnDomainThread();
+ }
}
}
@@ -265,8 +257,8 @@
}
if (mLocationTimeZoneDetectorController == null) {
- LocationTimeZoneProvider primary = createPrimaryProvider();
- LocationTimeZoneProvider secondary = createSecondaryProvider();
+ LocationTimeZoneProvider primary = mPrimaryProviderConfig.createProvider();
+ LocationTimeZoneProvider secondary = mSecondaryProviderConfig.createProvider();
ControllerImpl controller =
new ControllerImpl(mThreadingDomain, primary, secondary);
@@ -281,88 +273,6 @@
}
}
- @NonNull
- private LocationTimeZoneProvider createPrimaryProvider() {
- LocationTimeZoneProviderProxy proxy;
- if (isProviderInSimulationMode(PRIMARY_PROVIDER_NAME)) {
- proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
- } else if (!isProviderEnabled(PRIMARY_PROVIDER_NAME)) {
- proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
- } else {
- proxy = new RealLocationTimeZoneProviderProxy(
- mContext,
- mHandler,
- mThreadingDomain,
- PRIMARY_LOCATION_TIME_ZONE_SERVICE_ACTION,
- R.bool.config_enablePrimaryLocationTimeZoneOverlay,
- R.string.config_primaryLocationTimeZoneProviderPackageName
- );
- }
- ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(0);
- return new BinderLocationTimeZoneProvider(
- providerMetricsLogger, mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy);
- }
-
- @NonNull
- private LocationTimeZoneProvider createSecondaryProvider() {
- LocationTimeZoneProviderProxy proxy;
- if (isProviderInSimulationMode(SECONDARY_PROVIDER_NAME)) {
- proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
- } else if (!isProviderEnabled(SECONDARY_PROVIDER_NAME)) {
- proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
- } else {
- proxy = new RealLocationTimeZoneProviderProxy(
- mContext,
- mHandler,
- mThreadingDomain,
- SECONDARY_LOCATION_TIME_ZONE_SERVICE_ACTION,
- R.bool.config_enableSecondaryLocationTimeZoneOverlay,
- R.string.config_secondaryLocationTimeZoneProviderPackageName
- );
- }
- ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(1);
- return new BinderLocationTimeZoneProvider(
- providerMetricsLogger, mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy);
- }
-
- /** Used for bug triage and in tests to simulate provider events. */
- private boolean isProviderInSimulationMode(@NonNull String providerName) {
- return isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_SIMULATED);
- }
-
- /** Used for bug triage, and by tests and experiments to remove a provider. */
- private boolean isProviderEnabled(@NonNull String providerName) {
- if (isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_DISABLED)) {
- return false;
- }
-
- switch (providerName) {
- case PRIMARY_PROVIDER_NAME: {
- return mServiceConfigAccessor.isPrimaryLocationTimeZoneProviderEnabled();
- }
- case SECONDARY_PROVIDER_NAME: {
- return mServiceConfigAccessor.isSecondaryLocationTimeZoneProviderEnabled();
- }
- default: {
- throw new IllegalArgumentException(providerName);
- }
- }
- }
-
- private boolean isProviderModeOverrideSet(@NonNull String providerName, @NonNull String mode) {
- switch (providerName) {
- case PRIMARY_PROVIDER_NAME: {
- return Objects.equals(mPrimaryProviderModeOverride, mode);
- }
- case SECONDARY_PROVIDER_NAME: {
- return Objects.equals(mSecondaryProviderModeOverride, mode);
- }
- default: {
- throw new IllegalArgumentException(providerName);
- }
- }
- }
-
/**
* Stops the service for tests and other rare cases. To avoid tests needing to sleep, this
* method will not return until all the system server components have stopped.
@@ -398,33 +308,6 @@
}
/** Sets this service into provider state recording mode for tests. */
- void setProviderModeOverride(@NonNull String providerName, @NonNull String mode) {
- enforceManageTimeZoneDetectorPermission();
-
- Preconditions.checkArgument(
- PRIMARY_PROVIDER_NAME.equals(providerName)
- || SECONDARY_PROVIDER_NAME.equals(providerName));
- Preconditions.checkArgument(PROVIDER_MODE_OVERRIDE_DISABLED.equals(mode)
- || PROVIDER_MODE_OVERRIDE_SIMULATED.equals(mode)
- || PROVIDER_MODE_OVERRIDE_NONE.equals(mode));
-
- mThreadingDomain.postAndWait(() -> {
- synchronized (mSharedLock) {
- switch (providerName) {
- case PRIMARY_PROVIDER_NAME: {
- mPrimaryProviderModeOverride = mode;
- break;
- }
- case SECONDARY_PROVIDER_NAME: {
- mSecondaryProviderModeOverride = mode;
- break;
- }
- }
- }
- }, BLOCKING_OP_WAIT_DURATION_MILLIS);
- }
-
- /** Sets this service into provider state recording mode for tests. */
void setProviderStateRecordingEnabled(boolean enabled) {
enforceManageTimeZoneDetectorPermission();
@@ -437,8 +320,11 @@
}, BLOCKING_OP_WAIT_DURATION_MILLIS);
}
- /** Returns a snapshot of the current controller state for tests. */
- @NonNull
+ /**
+ * Returns a snapshot of the current controller state for tests. Returns {@code null} if the
+ * service is stopped.
+ */
+ @Nullable
LocationTimeZoneManagerServiceState getStateForTests() {
enforceManageTimeZoneDetectorPermission();
@@ -462,8 +348,8 @@
* Passes a {@link TestCommand} to the specified provider and waits for the response.
*/
@NonNull
- Bundle handleProviderTestCommand(
- @NonNull String providerName, @NonNull TestCommand testCommand) {
+ Bundle handleProviderTestCommand(@IntRange(from = 0, to = 1) int providerIndex,
+ @NonNull TestCommand testCommand) {
enforceManageTimeZoneDetectorPermission();
// Because this method blocks and posts work to the threading domain thread, it would cause
@@ -484,7 +370,7 @@
return;
}
mLocationTimeZoneDetectorController.handleProviderTestCommand(
- providerName, testCommand, remoteCallback);
+ providerIndex, testCommand, remoteCallback);
}
});
@@ -510,6 +396,17 @@
synchronized (mSharedLock) {
ipw.println("LocationTimeZoneManagerService:");
ipw.increaseIndent();
+
+ ipw.println("Primary provider config:");
+ ipw.increaseIndent();
+ mPrimaryProviderConfig.dump(ipw, args);
+ ipw.decreaseIndent();
+
+ ipw.println("Secondary provider config:");
+ ipw.increaseIndent();
+ mSecondaryProviderConfig.dump(ipw, args);
+ ipw.decreaseIndent();
+
if (mLocationTimeZoneDetectorController == null) {
ipw.println("{Stopped}");
} else {
@@ -546,4 +443,75 @@
android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION,
"manage time and time zone detection");
}
+
+ /** An inner class for managing a provider's config. */
+ private final class ProviderConfig implements Dumpable {
+ @IntRange(from = 0, to = 1) private final int mIndex;
+ @NonNull private final String mName;
+ @NonNull private final String mServiceAction;
+
+ ProviderConfig(@IntRange(from = 0, to = 1) int index, @NonNull String name,
+ @NonNull String serviceAction) {
+ Preconditions.checkArgument(index >= 0 && index <= 1);
+ mIndex = index;
+ mName = Objects.requireNonNull(name);
+ mServiceAction = Objects.requireNonNull(serviceAction);
+ }
+
+ @NonNull
+ LocationTimeZoneProvider createProvider() {
+ LocationTimeZoneProviderProxy proxy = createProxy();
+ ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(mIndex);
+ return new BinderLocationTimeZoneProvider(
+ providerMetricsLogger, mThreadingDomain, mName, proxy);
+ }
+
+ @GuardedBy("mSharedLock")
+ @Override
+ public void dump(IndentingPrintWriter ipw, String[] args) {
+ ipw.printf("getMode()=%s\n", getMode());
+ ipw.printf("getPackageName()=%s\n", getPackageName());
+ }
+
+ @NonNull
+ private LocationTimeZoneProviderProxy createProxy() {
+ String mode = getMode();
+ if (Objects.equals(mode, PROVIDER_MODE_SIMULATED)) {
+ return new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
+ } else if (Objects.equals(mode, PROVIDER_MODE_DISABLED)) {
+ return new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
+ } else {
+ // mode == PROVIDER_MODE_OVERRIDE_ENABLED (or unknown).
+ return createRealProxy();
+ }
+ }
+
+ /** Returns the mode of the provider. */
+ @NonNull
+ private String getMode() {
+ if (mIndex == 0) {
+ return mServiceConfigAccessor.getPrimaryLocationTimeZoneProviderMode();
+ } else {
+ return mServiceConfigAccessor.getSecondaryLocationTimeZoneProviderMode();
+ }
+ }
+
+ @NonNull
+ private RealLocationTimeZoneProviderProxy createRealProxy() {
+ String providerServiceAction = mServiceAction;
+ String providerPackageName = getPackageName();
+ return new RealLocationTimeZoneProviderProxy(
+ mContext, mHandler, mThreadingDomain, providerServiceAction,
+ providerPackageName);
+ }
+
+ @NonNull
+ private String getPackageName() {
+ if (mIndex == 0) {
+ return mServiceConfigAccessor.getPrimaryLocationTimeZoneProviderPackageName();
+ } else {
+ return mServiceConfigAccessor.getSecondaryLocationTimeZoneProviderPackageName();
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
index bdf4a70..c6df624 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
@@ -16,19 +16,25 @@
package com.android.server.timezonedetector.location;
import static android.app.time.LocationTimeZoneManager.DUMP_STATE_OPTION_PROTO;
-import static android.app.time.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME;
-import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DISABLED;
-import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE;
-import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED;
-import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME;
import static android.app.time.LocationTimeZoneManager.SERVICE_NAME;
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_DUMP_STATE;
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_RECORD_PROVIDER_STATES;
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND;
-import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE;
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_START;
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_STOP;
+import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;
+import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED;
+import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT;
+import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE;
+import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS;
+import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS;
+import static com.android.server.timedetector.ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS;
+import static com.android.server.timedetector.ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE;
+import static com.android.server.timedetector.ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE;
+import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_DISABLED;
+import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_ENABLED;
+import static com.android.server.timezonedetector.ServiceConfigAccessor.PROVIDER_MODE_SIMULATED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
@@ -53,16 +59,12 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/** Implements the shell command interface for {@link LocationTimeZoneManagerService}. */
class LocationTimeZoneManagerShellCommand extends ShellCommand {
- private static final List<String> VALID_PROVIDER_NAMES =
- Arrays.asList(PRIMARY_PROVIDER_NAME, SECONDARY_PROVIDER_NAME);
-
private final LocationTimeZoneManagerService mService;
LocationTimeZoneManagerShellCommand(LocationTimeZoneManagerService service) {
@@ -82,9 +84,6 @@
case SHELL_COMMAND_STOP: {
return runStop();
}
- case SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE: {
- return runSetProviderModeOverride();
- }
case SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND: {
return runSendProviderTestCommand();
}
@@ -110,10 +109,6 @@
pw.println(" Starts the location_time_zone_manager, creating time zone providers.");
pw.printf(" %s\n", SHELL_COMMAND_STOP);
pw.println(" Stops the location_time_zone_manager, destroying time zone providers.");
- pw.printf(" %s <provider name> <mode>\n", SHELL_COMMAND_SET_PROVIDER_MODE_OVERRIDE);
- pw.println(" Sets a provider into a test mode next time the service started.");
- pw.printf(" Values: %s|%s|%s\n", PROVIDER_MODE_OVERRIDE_NONE,
- PROVIDER_MODE_OVERRIDE_DISABLED, PROVIDER_MODE_OVERRIDE_SIMULATED);
pw.printf(" %s (true|false)\n", SHELL_COMMAND_RECORD_PROVIDER_STATES);
pw.printf(" Enables / disables provider state recording mode. See also %s. The default"
+ " state is always \"false\".\n", SHELL_COMMAND_DUMP_STATE);
@@ -126,11 +121,11 @@
pw.println(" Dumps Location Time Zone Manager state for tests as text or binary proto"
+ " form.");
pw.println(" See the LocationTimeZoneManagerServiceStateProto definition for details.");
- pw.printf(" %s <provider name> <test command>\n",
+ pw.printf(" %s <provider index> <test command>\n",
SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND);
pw.println(" Passes a test command to the named provider.");
pw.println();
- pw.printf("<provider name> = One of %s\n", VALID_PROVIDER_NAMES);
+ pw.println("<provider index> = 0 (primary), 1 (secondary)");
pw.println();
pw.printf("%s details:\n", SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND);
pw.println();
@@ -146,6 +141,47 @@
pw.println();
pw.println("Test commands cannot currently be passed to real provider implementations.");
pw.println();
+ pw.printf("This service is also affected by the following device_config flags in the"
+ + " %s namespace:\n", NAMESPACE_SYSTEM_TIME);
+ pw.printf(" %s - [default=true], only observed if the feature is enabled in config,"
+ + "set this to false to disable the feature\n",
+ KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED);
+ pw.printf(" %s - [default=false]. Only used if the device does not have an explicit"
+ + " 'location time zone detection enabled' setting configured [*].\n",
+ KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT);
+ pw.printf(" %s - [default=<unset>]. Used to override the device's 'location time zone"
+ + " detection enabled' setting [*]\n",
+ KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE);
+ pw.printf(" %s - Overrides the mode of the primary provider. Values=%s|%s|%s\n",
+ KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
+ PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED, PROVIDER_MODE_SIMULATED);
+ pw.printf(" %s - Overrides the mode of the secondary provider. Values=%s|%s|%s\n",
+ KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE,
+ PROVIDER_MODE_DISABLED, PROVIDER_MODE_ENABLED, PROVIDER_MODE_SIMULATED);
+ pw.printf(" %s - \n",
+ KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_MODE_OVERRIDE);
+ pw.printf(" %s - Sets the amount of time the service waits when uncertain before making"
+ + " an 'uncertain' suggestion to the time zone detector.\n",
+ KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS);
+ pw.printf(" %s - Sets the initialization time passed to the location time zone providers"
+ + "\n",
+ KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS);
+ pw.printf(" %s - Sets the amount of extra time added to the location time zone providers"
+ + " initialization time\n",
+ KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS);
+ pw.println();
+ pw.println("[*] The user must still have location = on / auto time zone detection = on");
+ pw.println();
+ pw.printf("Typically, use '%s' to stop the service before setting individual"
+ + " flags and '%s' after to restart it.\n",
+ SHELL_COMMAND_STOP, SHELL_COMMAND_START);
+ pw.println();
+ pw.println("Example:");
+ pw.printf(" $ adb shell cmd device_config put %s %s %s\n",
+ NAMESPACE_SYSTEM_TIME, KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
+ "true");
+ pw.println("See adb shell cmd device_config for more information.");
+ pw.println();
}
private int runStart() {
@@ -172,21 +208,6 @@
return 0;
}
- private int runSetProviderModeOverride() {
- PrintWriter outPrintWriter = getOutPrintWriter();
- try {
- String providerName = getNextArgRequired();
- String modeOverride = getNextArgRequired();
- outPrintWriter.println("Setting provider mode override for " + providerName
- + " to " + modeOverride);
- mService.setProviderModeOverride(providerName, modeOverride);
- } catch (RuntimeException e) {
- reportError(e);
- return 1;
- }
- return 0;
- }
-
private int runRecordProviderStates() {
PrintWriter outPrintWriter = getOutPrintWriter();
boolean enabled;
@@ -217,6 +238,11 @@
return 1;
}
+ if (state == null) {
+ // Controller is stopped.
+ return 0;
+ }
+
DualDumpOutputStream outputStream;
boolean useProto = Objects.equals(DUMP_STATE_OPTION_PROTO, getNextOption());
if (useProto) {
@@ -288,10 +314,10 @@
private int runSendProviderTestCommand() {
PrintWriter outPrintWriter = getOutPrintWriter();
- String providerName;
+ int providerIndex;
TestCommand testCommand;
try {
- providerName = validateProviderName(getNextArgRequired());
+ providerIndex = parseProviderIndex(getNextArgRequired());
testCommand = createTestCommandFromNextShellArg();
} catch (RuntimeException e) {
reportError(e);
@@ -299,9 +325,9 @@
}
outPrintWriter.println("Injecting testCommand=" + testCommand
- + " to providerName=" + providerName);
+ + " to providerIndex=" + providerIndex);
try {
- Bundle result = mService.handleProviderTestCommand(providerName, testCommand);
+ Bundle result = mService.handleProviderTestCommand(providerIndex, testCommand);
outPrintWriter.println(result);
} catch (RuntimeException e) {
reportError(e);
@@ -321,11 +347,11 @@
e.printStackTrace(errPrintWriter);
}
- @NonNull
- static String validateProviderName(@NonNull String value) {
- if (!VALID_PROVIDER_NAMES.contains(value)) {
- throw new IllegalArgumentException("Unknown provider name=" + value);
+ private static int parseProviderIndex(@NonNull String providerIndexString) {
+ int providerIndex = Integer.parseInt(providerIndexString);
+ if (providerIndex < 0 || providerIndex > 1) {
+ throw new IllegalArgumentException(providerIndexString);
}
- return value;
+ return providerIndex;
}
}
diff --git a/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
index 6c3f016..b5ac712 100644
--- a/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
@@ -62,15 +62,17 @@
RealLocationTimeZoneProviderProxy(
@NonNull Context context, @NonNull Handler handler,
@NonNull ThreadingDomain threadingDomain, @NonNull String action,
- int enableOverlayResId, int nonOverlayPackageResId) {
+ @NonNull String providerPackageName) {
super(context, threadingDomain);
mManagerProxy = null;
mRequest = TimeZoneProviderRequest.createStopUpdatesRequest();
+
+ Objects.requireNonNull(providerPackageName);
mServiceWatcher = ServiceWatcher.create(context,
handler,
"RealLocationTimeZoneProviderProxy",
- new CurrentUserServiceSupplier(context, action, enableOverlayResId,
- nonOverlayPackageResId, BIND_TIME_ZONE_PROVIDER_SERVICE,
+ new CurrentUserServiceSupplier(context, action,
+ providerPackageName, BIND_TIME_ZONE_PROVIDER_SERVICE,
INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE),
this);
}
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index fe728ab..fb664ab 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -60,7 +60,7 @@
const std::string instance = std::string() + IStats::descriptor + "/default";
const binder_exception_t err =
AServiceManager_addService(statsService->asBinder().get(), instance.c_str());
- LOG_ALWAYS_FATAL_IF(err != EX_NONE, "Cannot register %s: %d", instance.c_str(), err);
+ LOG_ALWAYS_FATAL_IF(err != EX_NONE, "Cannot register AIDL %s: %d", instance.c_str(), err);
}
static void startStatsHidlService() {
@@ -69,13 +69,18 @@
android::sp<IStats> statsHal = new StatsHal();
const android::status_t err = statsHal->registerAsService();
- LOG_ALWAYS_FATAL_IF(err != android::OK, "Cannot register %s: %d", IStats::descriptor, err);
+ LOG_ALWAYS_FATAL_IF(err != android::OK, "Cannot register HIDL %s: %d", IStats::descriptor, err);
}
} // namespace
namespace android {
+static void android_server_SystemServer_startIStatsService(JNIEnv* /* env */, jobject /* clazz */) {
+ startStatsHidlService();
+ startStatsAidlService();
+}
+
static void android_server_SystemServer_startSensorService(JNIEnv* /* env */, jobject /* clazz */) {
char propBuf[PROPERTY_VALUE_MAX];
property_get("system_init.startsensorservice", propBuf, "1");
@@ -129,9 +134,6 @@
} else {
ALOGW("%s is deprecated. Skipping registration.", ISchedulingPolicyService::descriptor);
}
-
- startStatsAidlService();
- startStatsHidlService();
}
static void android_server_SystemServer_initZygoteChildHeapProfiling(JNIEnv* /* env */,
@@ -160,6 +162,7 @@
*/
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
+ {"startIStatsService", "()V", (void*)android_server_SystemServer_startIStatsService},
{"startSensorService", "()V", (void*)android_server_SystemServer_startSensorService},
{"startMemtrackProxyService", "()V",
(void*)android_server_SystemServer_startMemtrackProxyService},
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 63488f9..f843ea4 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -150,6 +150,11 @@
return ok();
}
+binder::Status BinderIncrementalService::onInstallationComplete(int32_t storageId) {
+ mImpl.onInstallationComplete(storageId);
+ return ok();
+}
+
binder::Status BinderIncrementalService::makeBindMount(int32_t storageId,
const std::string& sourcePath,
const std::string& targetFullPath,
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index ebb23dc..5c8741b 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -52,6 +52,8 @@
const ::android::sp<IStorageHealthListener>& healthListener,
const ::std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts,
bool* _aidl_return) final;
+ binder::Status onInstallationComplete(int32_t storageId) final;
+
binder::Status makeBindMount(int32_t storageId, const std::string& sourcePath,
const std::string& targetFullPath, int32_t bindType,
int32_t* _aidl_return) final;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 6695ba8..388f932 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -100,6 +100,12 @@
return (s & (Constants::blockSize - 1)) == 0;
}
+static bool getAlwaysEnableReadTimeoutsForSystemDataLoaders() {
+ return android::base::
+ GetBoolProperty("debug.incremental.always_enable_read_timeouts_for_system_dataloaders",
+ true);
+}
+
static bool getEnforceReadLogsMaxIntervalForSystemDataLoaders() {
return android::base::GetBoolProperty("debug.incremental.enforce_readlogs_max_interval_for_"
"system_dataloaders",
@@ -315,19 +321,11 @@
::rmdir(path::c_str(root));
}
-void IncrementalService::IncFsMount::setReadLogsEnabled(bool value) {
+void IncrementalService::IncFsMount::setFlag(StorageFlags flag, bool value) {
if (value) {
- flags |= StorageFlags::ReadLogsEnabled;
+ flags |= flag;
} else {
- flags &= ~StorageFlags::ReadLogsEnabled;
- }
-}
-
-void IncrementalService::IncFsMount::setReadLogsRequested(bool value) {
- if (value) {
- flags |= StorageFlags::ReadLogsRequested;
- } else {
- flags &= ~StorageFlags::ReadLogsRequested;
+ flags &= ~flag;
}
}
@@ -728,10 +726,17 @@
LOG(INFO) << "Skipped data loader stub creation because it already exists";
return false;
}
+
prepareDataLoaderLocked(*ifs, std::move(dataLoaderParams), std::move(statusListener),
healthCheckParams, std::move(healthListener));
CHECK(ifs->dataLoaderStub);
dataLoaderStub = ifs->dataLoaderStub;
+
+ // Disable long read timeouts for non-system dataloaders.
+ // To be re-enabled after installation is complete.
+ ifs->setReadTimeoutsRequested(dataLoaderStub->isSystemDataLoader() &&
+ getAlwaysEnableReadTimeoutsForSystemDataLoaders());
+ applyStorageParamsLocked(*ifs);
}
if (dataLoaderStub->isSystemDataLoader() &&
@@ -765,6 +770,18 @@
return dataLoaderStub->requestStart();
}
+void IncrementalService::onInstallationComplete(StorageId storage) {
+ IfsMountPtr ifs = getIfs(storage);
+ if (!ifs) {
+ return;
+ }
+
+ // Always enable long read timeouts after installation is complete.
+ std::unique_lock l(ifs->lock);
+ ifs->setReadTimeoutsRequested(true);
+ applyStorageParamsLocked(*ifs);
+}
+
IncrementalService::BindPathMap::const_iterator IncrementalService::findStorageLocked(
std::string_view path) const {
return findParentPath(mBindsByPath, path);
@@ -868,7 +885,7 @@
if (!ifs->readLogsRequested()) {
return 0;
}
- if (auto status = applyStorageParamsLocked(*ifs, /*enableReadLogs=*/true); status != 0) {
+ if (auto status = applyStorageParamsLocked(*ifs); status != 0) {
return status;
}
}
@@ -880,10 +897,10 @@
int IncrementalService::disableReadLogsLocked(IncFsMount& ifs) {
ifs.setReadLogsRequested(false);
- return applyStorageParamsLocked(ifs, /*enableReadLogs=*/false);
+ return applyStorageParamsLocked(ifs);
}
-int IncrementalService::applyStorageParamsLocked(IncFsMount& ifs, bool enableReadLogs) {
+int IncrementalService::applyStorageParamsLocked(IncFsMount& ifs) {
os::incremental::IncrementalFileSystemControlParcel control;
control.cmd.reset(dup(ifs.control.cmd()));
control.pendingReads.reset(dup(ifs.control.pendingReads()));
@@ -892,11 +909,15 @@
control.log.reset(dup(logsFd));
}
+ bool enableReadLogs = ifs.readLogsRequested();
+ bool enableReadTimeouts = ifs.readTimeoutsRequested();
+
std::lock_guard l(mMountOperationLock);
- auto status = mVold->setIncFsMountOptions(control, enableReadLogs);
+ auto status = mVold->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts);
if (status.isOk()) {
- // Store enabled state.
+ // Store states.
ifs.setReadLogsEnabled(enableReadLogs);
+ ifs.setReadTimeoutsEnabled(enableReadTimeouts);
} else {
LOG(ERROR) << "applyStorageParams failed: " << status.toString8();
}
@@ -1271,7 +1292,7 @@
maxPendingTimeUs = std::max(maxPendingTimeUs, microseconds(timeouts.maxPendingTimeUs));
}
if (maxPendingTimeUs < Constants::minPerUidTimeout) {
- LOG(ERROR) << "Skip setting read timeouts (maxPendingTime < Constants::minPerUidTimeout): "
+ LOG(ERROR) << "Skip setting read timeouts (maxPendingTime < Constants::minPerUidTimeout): "
<< duration_cast<milliseconds>(maxPendingTimeUs).count() << "ms < "
<< Constants::minPerUidTimeout.count() << "ms";
return;
@@ -2946,8 +2967,10 @@
return result;
}
- LOG(DEBUG) << id() << ": pendingReads: " << control.pendingReads() << ", "
- << mLastPendingReads.size() << ": " << mLastPendingReads.front().bootClockTsUs;
+ LOG(DEBUG) << id() << ": pendingReads: fd(" << control.pendingReads() << "), count("
+ << mLastPendingReads.size() << "), block: " << mLastPendingReads.front().block
+ << ", time: " << mLastPendingReads.front().bootClockTsUs
+ << ", uid: " << mLastPendingReads.front().uid;
return getOldestTsFromLastPendingReads();
}
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index fb6f56c..e3b1e6f 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -118,6 +118,9 @@
ReadLogsAllowed = 1 << 0,
ReadLogsEnabled = 1 << 1,
ReadLogsRequested = 1 << 2,
+
+ ReadTimeoutsEnabled = 1 << 3,
+ ReadTimeoutsRequested = 1 << 4,
};
struct LoadingProgress {
@@ -160,6 +163,7 @@
const StorageHealthCheckParams& healthCheckParams,
StorageHealthListener healthListener,
std::vector<PerUidReadTimeouts> perUidReadTimeouts);
+ void onInstallationComplete(StorageId storage);
int bind(StorageId storage, std::string_view source, std::string_view target, BindKind kind);
int unbind(StorageId storage, std::string_view target);
@@ -316,7 +320,7 @@
} mHealthBase = {TimePoint::max(), kMaxBootClockTsUs};
StorageHealthCheckParams mHealthCheckParams;
int mStreamStatus = content::pm::IDataLoaderStatusListener::STREAM_HEALTHY;
- std::vector<incfs::ReadInfo> mLastPendingReads;
+ std::vector<incfs::ReadInfoWithUid> mLastPendingReads;
};
using DataLoaderStubPtr = sp<DataLoaderStub>;
@@ -364,13 +368,32 @@
void disallowReadLogs() { flags &= ~StorageFlags::ReadLogsAllowed; }
int32_t readLogsAllowed() const { return (flags & StorageFlags::ReadLogsAllowed); }
- void setReadLogsEnabled(bool value);
+ void setReadLogsEnabled(bool value) {
+ return setFlag(StorageFlags::ReadLogsEnabled, value);
+ }
int32_t readLogsEnabled() const { return (flags & StorageFlags::ReadLogsEnabled); }
- void setReadLogsRequested(bool value);
+ void setReadLogsRequested(bool value) {
+ return setFlag(StorageFlags::ReadLogsRequested, value);
+ }
int32_t readLogsRequested() const { return (flags & StorageFlags::ReadLogsRequested); }
+ void setReadTimeoutsEnabled(bool value) {
+ return setFlag(StorageFlags::ReadTimeoutsEnabled, value);
+ }
+ int32_t readTimeoutsEnabled() const { return (flags & StorageFlags::ReadTimeoutsEnabled); }
+
+ void setReadTimeoutsRequested(bool value) {
+ return setFlag(StorageFlags::ReadTimeoutsRequested, value);
+ }
+ int32_t readTimeoutsRequested() const {
+ return (flags & StorageFlags::ReadTimeoutsRequested);
+ }
+
static void cleanupFilesystem(std::string_view root);
+
+ private:
+ void setFlag(StorageFlags flag, bool value);
};
using IfsMountPtr = std::shared_ptr<IncFsMount>;
@@ -422,7 +445,7 @@
int makeDirs(const IncFsMount& ifs, StorageId storageId, std::string_view path, int mode);
int disableReadLogsLocked(IncFsMount& ifs);
- int applyStorageParamsLocked(IncFsMount& ifs, bool enableReadLogs);
+ int applyStorageParamsLocked(IncFsMount& ifs);
LoadingProgress getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const;
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 8e416f3..0755a22 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -55,8 +55,8 @@
}
binder::Status setIncFsMountOptions(
const ::android::os::incremental::IncrementalFileSystemControlParcel& control,
- bool enableReadLogs) const final {
- return mInterface->setIncFsMountOptions(control, enableReadLogs);
+ bool enableReadLogs, bool enableReadTimeouts) const final {
+ return mInterface->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts);
}
private:
@@ -233,8 +233,9 @@
ErrorCode reserveSpace(const Control& control, FileId id, IncFsSize size) const final {
return incfs::reserveSpace(control, id, size);
}
- WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout,
- std::vector<incfs::ReadInfo>* pendingReadsBuffer) const final {
+ WaitResult waitForPendingReads(
+ const Control& control, std::chrono::milliseconds timeout,
+ std::vector<incfs::ReadInfoWithUid>* pendingReadsBuffer) const final {
return incfs::waitForPendingReads(control, timeout, pendingReadsBuffer);
}
ErrorCode setUidReadTimeouts(const Control& control,
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index d4cdcbe..78e9589 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -56,8 +56,8 @@
virtual binder::Status bindMount(const std::string& sourceDir,
const std::string& targetDir) const = 0;
virtual binder::Status setIncFsMountOptions(
- const os::incremental::IncrementalFileSystemControlParcel& control,
- bool enableReadLogs) const = 0;
+ const os::incremental::IncrementalFileSystemControlParcel& control, bool enableReadLogs,
+ bool enableReadTimeouts) const = 0;
};
class DataLoaderManagerWrapper {
@@ -117,7 +117,7 @@
virtual ErrorCode reserveSpace(const Control& control, FileId id, IncFsSize size) const = 0;
virtual WaitResult waitForPendingReads(
const Control& control, std::chrono::milliseconds timeout,
- std::vector<incfs::ReadInfo>* pendingReadsBuffer) const = 0;
+ std::vector<incfs::ReadInfoWithUid>* pendingReadsBuffer) const = 0;
virtual ErrorCode setUidReadTimeouts(
const Control& control,
const std::vector<::android::os::incremental::PerUidReadTimeouts>& perUidReadTimeouts)
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 1ec446d..14bcd4e 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -56,10 +56,10 @@
MOCK_CONST_METHOD1(unmountIncFs, binder::Status(const std::string& dir));
MOCK_CONST_METHOD2(bindMount,
binder::Status(const std::string& sourceDir, const std::string& argetDir));
- MOCK_CONST_METHOD2(
+ MOCK_CONST_METHOD3(
setIncFsMountOptions,
binder::Status(const ::android::os::incremental::IncrementalFileSystemControlParcel&,
- bool));
+ bool, bool));
void mountIncFsFails() {
ON_CALL(*this, mountIncFs(_, _, _, _))
@@ -83,12 +83,13 @@
ON_CALL(*this, bindMount(_, _)).WillByDefault(Return(binder::Status::ok()));
}
void setIncFsMountOptionsFails() const {
- ON_CALL(*this, setIncFsMountOptions(_, _))
+ ON_CALL(*this, setIncFsMountOptions(_, _, _))
.WillByDefault(Return(
binder::Status::fromExceptionCode(1, String8("failed to set options"))));
}
void setIncFsMountOptionsSuccess() {
- ON_CALL(*this, setIncFsMountOptions(_, _)).WillByDefault(Return(binder::Status::ok()));
+ ON_CALL(*this, setIncFsMountOptions(_, _, _))
+ .WillByDefault(Invoke(this, &MockVoldService::setIncFsMountOptionsOk));
}
binder::Status getInvalidControlParcel(const std::string& imagePath,
const std::string& targetDir, int32_t flags,
@@ -103,10 +104,23 @@
_aidl_return->log.reset(base::unique_fd(dup(STDIN_FILENO)));
return binder::Status::ok();
}
+ binder::Status setIncFsMountOptionsOk(
+ const ::android::os::incremental::IncrementalFileSystemControlParcel& control,
+ bool enableReadLogs, bool enableReadTimeouts) {
+ mReadLogsEnabled = enableReadLogs;
+ mReadTimeoutsEnabled = enableReadTimeouts;
+ return binder::Status::ok();
+ }
+
+ bool readLogsEnabled() const { return mReadLogsEnabled; }
+ bool readTimeoutsEnabled() const { return mReadTimeoutsEnabled; }
private:
TemporaryFile cmdFile;
TemporaryFile logFile;
+
+ bool mReadLogsEnabled = false;
+ bool mReadTimeoutsEnabled = true;
};
class MockDataLoader : public IDataLoader {
@@ -395,7 +409,7 @@
MOCK_CONST_METHOD3(reserveSpace, ErrorCode(const Control& control, FileId id, IncFsSize size));
MOCK_CONST_METHOD3(waitForPendingReads,
WaitResult(const Control& control, std::chrono::milliseconds timeout,
- std::vector<incfs::ReadInfo>* pendingReadsBuffer));
+ std::vector<incfs::ReadInfoWithUid>* pendingReadsBuffer));
MOCK_CONST_METHOD2(setUidReadTimeouts,
ErrorCode(const Control& control,
const std::vector<PerUidReadTimeouts>& perUidReadTimeouts));
@@ -435,7 +449,7 @@
ON_CALL(*this, waitForPendingReads(_, _, _))
.WillByDefault(
Invoke([ts](const Control& control, std::chrono::milliseconds timeout,
- std::vector<incfs::ReadInfo>* pendingReadsBuffer) {
+ std::vector<incfs::ReadInfoWithUid>* pendingReadsBuffer) {
pendingReadsBuffer->push_back({.bootClockTsUs = ts});
return android::incfs::WaitResult::HaveData;
}));
@@ -1302,8 +1316,10 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+ // on startLoading
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
// We are calling setIncFsMountOptions(true).
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
// After setIncFsMountOptions succeeded expecting to start watching.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// Not expecting callback removal.
@@ -1325,8 +1341,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// Enabling and then disabling readlogs.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1);
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(2);
// After setIncFsMountOptions succeeded expecting to start watching.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// Not expecting callback removal.
@@ -1353,8 +1369,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// Enabling and then disabling readlogs.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(2);
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(2);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(2);
// After setIncFsMountOptions succeeded expecting to start watching.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// Not expecting callback removal.
@@ -1394,8 +1410,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// Enabling and then disabling readlogs.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(3);
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(0);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(3);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
// After setIncFsMountOptions succeeded expecting to start watching.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// Not expecting callback removal.
@@ -1435,8 +1451,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(2);
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// Enabling and then disabling readlogs.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(3);
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(5);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(3);
// After setIncFsMountOptions succeeded expecting to start watching.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// Not expecting callback removal.
@@ -1448,9 +1464,14 @@
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
+ // Before install - long timeouts.
+ ASSERT_TRUE(mVold->readTimeoutsEnabled());
+
auto dataLoaderParcel = mDataLoaderParcel;
ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(dataLoaderParcel), {}, {},
{}, {}));
+ // During install - short timeouts.
+ ASSERT_FALSE(mVold->readTimeoutsEnabled());
// Disable readlogs callback present.
ASSERT_EQ(storageId, mTimedQueue->mId);
@@ -1463,9 +1484,15 @@
mClock->advance(90min);
ASSERT_GE(mDataLoader->setStorageParams(true), 0);
+ mIncrementalService->onInstallationComplete(storageId);
+ // After install - long timeouts.
+ ASSERT_TRUE(mVold->readTimeoutsEnabled());
+
// New installation.
ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
{}, {}));
+ // New installation - short timeouts.
+ ASSERT_FALSE(mVold->readTimeoutsEnabled());
// New callback present.
ASSERT_EQ(storageId, mTimedQueue->mId);
@@ -1485,6 +1512,10 @@
// And timeout.
mClock->advance(90min);
ASSERT_EQ(mDataLoader->setStorageParams(true), -EPERM);
+
+ mIncrementalService->onInstallationComplete(storageId);
+ // After install - long timeouts.
+ ASSERT_TRUE(mVold->readTimeoutsEnabled());
}
TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChanged) {
@@ -1495,9 +1526,9 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// We are calling setIncFsMountOptions(true).
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
// setIncFsMountOptions(false) is called on the callback.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, false)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(2);
// After setIncFsMountOptions succeeded expecting to start watching.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(1);
// After callback is called, disable read logs and remove callback.
@@ -1520,7 +1551,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// checkPermission fails, no calls to set opitions, start or stop WatchingMode.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(0);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(0);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
TemporaryDir tempDir;
@@ -1539,7 +1571,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// checkPermission fails, no calls to set opitions, start or stop WatchingMode.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(0);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(0);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
TemporaryDir tempDir;
@@ -1559,7 +1592,8 @@
EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
// We are calling setIncFsMountOptions.
- EXPECT_CALL(*mVold, setIncFsMountOptions(_, true)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, true, _)).Times(1);
+ EXPECT_CALL(*mVold, setIncFsMountOptions(_, false, _)).Times(1);
// setIncFsMountOptions fails, no calls to start or stop WatchingMode.
EXPECT_CALL(*mAppOpsManager, startWatchingMode(_, _, _)).Times(0);
EXPECT_CALL(*mAppOpsManager, stopWatchingMode(_)).Times(0);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9b2a1e7..1426579 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -442,15 +442,15 @@
private final SystemServerDumper mDumper = new SystemServerDumper();
-
/**
* The pending WTF to be logged into dropbox.
*/
private static LinkedList<Pair<String, ApplicationErrorReport.CrashInfo>> sPendingWtfs;
- /**
- * Start the sensor service. This is a blocking call and can take time.
- */
+ /** Start the IStats services. This is a blocking call and can take time. */
+ private static native void startIStatsService();
+
+ /** Start the sensor service. This is a blocking call and can take time. */
private static native void startSensorService();
/**
@@ -1029,6 +1029,10 @@
mSystemServiceManager.startService(PowerStatsService.class);
t.traceEnd();
+ t.traceBegin("StartIStatsService");
+ startIStatsService();
+ t.traceEnd();
+
// Start MemtrackProxyService before ActivityManager, so that early calls
// to Memtrack::getMemory() don't fail.
t.traceBegin("MemtrackProxyService");
@@ -2678,7 +2682,7 @@
t.traceBegin("RegisterAppOpsPolicy");
try {
- mActivityManagerService.setAppOpsPolicy(new AppOpsPolicy());
+ mActivityManagerService.setAppOpsPolicy(new AppOpsPolicy(mSystemContext));
} catch (Throwable e) {
reportWtf("registering app ops policy", e);
}
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 75614d6..9f24d9a 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -63,6 +63,7 @@
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -105,6 +106,7 @@
public class DataManager {
private static final String TAG = "DataManager";
+ private static final boolean DEBUG = false;
private static final long RECENT_NOTIFICATIONS_MAX_AGE_MS = 10 * DateUtils.DAY_IN_MILLIS;
private static final long QUERY_EVENTS_MAX_AGE_MS = 5L * DateUtils.MINUTE_IN_MILLIS;
@@ -217,6 +219,7 @@
List<ShortcutInfo> shortcuts = getShortcuts(packageName, userId,
Collections.singletonList(shortcutId));
if (shortcuts != null && !shortcuts.isEmpty()) {
+ if (DEBUG) Log.d(TAG, "Found shortcut for " + shortcuts.get(0).getLabel());
return shortcuts.get(0);
}
return null;
@@ -258,6 +261,7 @@
}
ShortcutInfo shortcutInfo = getShortcut(packageName, userId, shortcutId);
if (shortcutInfo == null) {
+ Slog.e(TAG, " Shortcut no longer found: " + shortcutId);
return null;
}
int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
@@ -705,6 +709,7 @@
}
});
for (String packageName : packagesToDelete) {
+ if (DEBUG) Log.d(TAG, "Deleting packages data for: " + packageName);
userData.deletePackageData(packageName);
}
}
@@ -716,6 +721,7 @@
@ShortcutQuery.QueryFlags int queryFlags = ShortcutQuery.FLAG_MATCH_DYNAMIC
| ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER
| ShortcutQuery.FLAG_MATCH_CACHED | ShortcutQuery.FLAG_GET_PERSONS_DATA;
+ if (DEBUG) Log.d(TAG, " Get shortcuts with IDs: " + shortcutIds);
return mShortcutServiceInternal.getShortcuts(
UserHandle.USER_SYSTEM, mContext.getPackageName(),
/*changedSince=*/ 0, packageName, shortcutIds, /*locusIds=*/ null,
@@ -742,7 +748,7 @@
TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
String defaultDialer = telecomManager != null
? telecomManager.getDefaultDialerPackage(
- new UserHandle(userData.getUserId())) : null;
+ new UserHandle(userData.getUserId())) : null;
userData.setDefaultDialer(defaultDialer);
}
@@ -848,6 +854,9 @@
ConversationStore conversationStore = packageData.getConversationStore();
ConversationInfo oldConversationInfo =
conversationStore.getConversation(shortcutInfo.getId());
+ if (oldConversationInfo == null) {
+ if (DEBUG) Log.d(TAG, "Nothing previously stored about conversation.");
+ }
ConversationInfo.Builder builder = oldConversationInfo != null
? new ConversationInfo.Builder(oldConversationInfo)
: new ConversationInfo.Builder();
@@ -1083,6 +1092,7 @@
Set<String> shortcutIds = new HashSet<>();
for (ShortcutInfo shortcutInfo : shortcuts) {
if (packageData != null) {
+ if (DEBUG) Log.d(TAG, "Deleting shortcut: " + shortcutInfo.getId());
packageData.deleteDataForConversation(shortcutInfo.getId());
}
shortcutIds.add(shortcutInfo.getId());
@@ -1309,6 +1319,7 @@
int userId = getChangingUserId();
UserData userData = getUnlockedUserData(userId);
if (userData != null) {
+ if (DEBUG) Log.d(TAG, "Delete package data for: " + packageName);
userData.deletePackageData(packageName);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java
index 01a641f..b8535c2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/PolicyWarningUIControllerTest.java
@@ -119,36 +119,36 @@
when(mAccessibilitySecurityPolicy.isA11yCategoryService(
mMockA11yServiceInfo)).thenReturn(false);
- mFakeNotificationController.onReceive(mContext, createIntent(TEST_USER_ID,
- PolicyWarningUIController.ACTION_SEND_NOTIFICATION,
- TEST_COMPONENT_NAME.flattenToShortString()));
+ mFakeNotificationController.onReceive(mContext,
+ PolicyWarningUIController.createIntent(mContext, TEST_USER_ID,
+ PolicyWarningUIController.ACTION_SEND_NOTIFICATION,
+ TEST_COMPONENT_NAME));
verify(mNotificationManager).notify(eq(TEST_COMPONENT_NAME.flattenToShortString()),
- eq(NOTE_A11Y_VIEW_AND_CONTROL_ACCESS), any(
- Notification.class));
+ eq(NOTE_A11Y_VIEW_AND_CONTROL_ACCESS), any(Notification.class));
}
@Test
public void receiveActionA11ySettings_launchA11ySettingsAndDismissNotification() {
mFakeNotificationController.onReceive(mContext,
- createIntent(TEST_USER_ID, PolicyWarningUIController.ACTION_A11Y_SETTINGS,
- TEST_COMPONENT_NAME.flattenToShortString()));
+ PolicyWarningUIController.createIntent(mContext, TEST_USER_ID,
+ PolicyWarningUIController.ACTION_A11Y_SETTINGS,
+ TEST_COMPONENT_NAME));
verifyLaunchA11ySettings();
verify(mNotificationManager).cancel(TEST_COMPONENT_NAME.flattenToShortString(),
NOTE_A11Y_VIEW_AND_CONTROL_ACCESS);
- assertNotifiedSettingsEqual(TEST_USER_ID,
- TEST_COMPONENT_NAME.flattenToShortString());
+ assertNotifiedSettingsEqual(TEST_USER_ID, TEST_COMPONENT_NAME.flattenToShortString());
}
@Test
public void receiveActionDismissNotification_addToNotifiedSettings() {
- mFakeNotificationController.onReceive(mContext, createIntent(TEST_USER_ID,
- PolicyWarningUIController.ACTION_DISMISS_NOTIFICATION,
- TEST_COMPONENT_NAME.flattenToShortString()));
+ mFakeNotificationController.onReceive(mContext,
+ PolicyWarningUIController.createIntent(mContext, TEST_USER_ID,
+ PolicyWarningUIController.ACTION_DISMISS_NOTIFICATION,
+ TEST_COMPONENT_NAME));
- assertNotifiedSettingsEqual(TEST_USER_ID,
- TEST_COMPONENT_NAME.flattenToShortString());
+ assertNotifiedSettingsEqual(TEST_USER_ID, TEST_COMPONENT_NAME.flattenToShortString());
}
@Test
@@ -172,8 +172,7 @@
verify(mAlarmManager).set(eq(RTC_WAKEUP), anyLong(),
eq(PolicyWarningUIController.createPendingIntent(mContext, TEST_USER_ID,
- PolicyWarningUIController.ACTION_SEND_NOTIFICATION,
- TEST_COMPONENT_NAME.flattenToShortString())));
+ PolicyWarningUIController.ACTION_SEND_NOTIFICATION, TEST_COMPONENT_NAME)));
}
@Test
@@ -184,8 +183,7 @@
verify(mAlarmManager).cancel(
eq(PolicyWarningUIController.createPendingIntent(mContext, TEST_USER_ID,
- PolicyWarningUIController.ACTION_SEND_NOTIFICATION,
- TEST_COMPONENT_NAME.flattenToShortString())));
+ PolicyWarningUIController.ACTION_SEND_NOTIFICATION, TEST_COMPONENT_NAME)));
}
private void assertNotifiedSettingsEqual(int userId, String settingString) {
@@ -196,14 +194,6 @@
assertEquals(settingString, notifiedServicesSetting);
}
- private Intent createIntent(int userId, String action, String serviceComponentName) {
- final Intent intent = new Intent(action);
- intent.setPackage(mContext.getPackageName())
- .setIdentifier(serviceComponentName)
- .putExtra(Intent.EXTRA_USER_ID, userId);
- return intent;
- }
-
private void verifyLaunchA11ySettings() {
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
final ArgumentCaptor<UserHandle> userHandleCaptor = ArgumentCaptor.forClass(
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index fc4804b..41237c8 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -72,6 +72,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.util.IntArray;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
@@ -121,7 +122,8 @@
UidRecord.CHANGE_GONE,
UidRecord.CHANGE_GONE | UidRecord.CHANGE_IDLE,
UidRecord.CHANGE_IDLE,
- UidRecord.CHANGE_ACTIVE
+ UidRecord.CHANGE_ACTIVE,
+ UidRecord.CHANGE_CAPABILITY,
};
private static PackageManagerInternal sPackageManagerInternal;
@@ -528,8 +530,10 @@
ActivityManager.UID_OBSERVER_GONE,
ActivityManager.UID_OBSERVER_IDLE,
ActivityManager.UID_OBSERVER_ACTIVE,
+ ActivityManager.UID_OBSERVER_CAPABILITY,
ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
| ActivityManager.UID_OBSERVER_ACTIVE | ActivityManager.UID_OBSERVER_IDLE
+ | ActivityManager.UID_OBSERVER_CAPABILITY
};
final IUidObserver[] observers = new IUidObserver.Stub[changesToObserve.length];
for (int i = 0; i < observers.length; ++i) {
@@ -553,7 +557,16 @@
ActivityManager.PROCESS_STATE_NONEXISTENT,
ActivityManager.PROCESS_STATE_CACHED_EMPTY,
ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
- ActivityManager.PROCESS_STATE_TOP
+ ActivityManager.PROCESS_STATE_TOP,
+ ActivityManager.PROCESS_STATE_TOP,
+ };
+ final int[] capabilitiesForPendingUidRecords = {
+ ActivityManager.PROCESS_CAPABILITY_ALL,
+ ActivityManager.PROCESS_CAPABILITY_NONE,
+ ActivityManager.PROCESS_CAPABILITY_NONE,
+ ActivityManager.PROCESS_CAPABILITY_NONE,
+ ActivityManager.PROCESS_CAPABILITY_NONE,
+ ActivityManager.PROCESS_CAPABILITY_NETWORK,
};
final Map<Integer, ChangeRecord> changeItems = new HashMap<>();
for (int i = 0; i < changesForPendingUidRecords.length; ++i) {
@@ -562,6 +575,7 @@
pendingChange.uid = i;
pendingChange.procState = procStatesForPendingUidRecords[i];
pendingChange.procStateSeq = i;
+ pendingChange.capability = capabilitiesForPendingUidRecords[i];
changeItems.put(changesForPendingUidRecords[i], pendingChange);
addPendingUidChange(pendingChange);
}
@@ -606,20 +620,26 @@
verify(observer).onUidGone(changeItem.uid, changeItem.ephemeral);
});
}
- if ((changeToObserve & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
+ if ((changeToObserve & ActivityManager.UID_OBSERVER_PROCSTATE) != 0
+ || (changeToObserve & ActivityManager.UID_OBSERVER_CAPABILITY) != 0) {
// Observer listens to uid procState changes, so change items corresponding to
// UidRecord.CHANGE_PROCSTATE or UidRecord.CHANGE_IDLE or UidRecord.CHANGE_ACTIVE
// needs to be delivered to this observer.
- final int[] changesToVerify = {
- UidRecord.CHANGE_PROCSTATE,
- UidRecord.CHANGE_ACTIVE,
- UidRecord.CHANGE_IDLE
- };
- verifyObserverReceivedChanges(observerToTest, changesToVerify, changeItems,
+ final IntArray changesToVerify = new IntArray();
+ if ((changeToObserve & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) {
+ changesToVerify.add(UidRecord.CHANGE_CAPABILITY);
+ } else {
+ changesToVerify.add(UidRecord.CHANGE_PROCSTATE);
+ changesToVerify.add(UidRecord.CHANGE_ACTIVE);
+ changesToVerify.add(UidRecord.CHANGE_IDLE);
+ changesToVerify.add(UidRecord.CHANGE_CAPABILITY);
+ }
+ verifyObserverReceivedChanges(observerToTest, changesToVerify.toArray(),
+ changeItems,
(observer, changeItem) -> {
verify(observer).onUidStateChanged(changeItem.uid,
changeItem.procState, changeItem.procStateSeq,
- ActivityManager.PROCESS_CAPABILITY_NONE);
+ changeItem.capability);
});
}
// Verify there are no other callbacks for this observer.
@@ -725,11 +745,11 @@
ActivityManager.PROCESS_STATE_CACHED_EMPTY,
ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
ActivityManager.PROCESS_STATE_SERVICE,
- ActivityManager.PROCESS_STATE_RECEIVER
+ ActivityManager.PROCESS_STATE_RECEIVER,
};
final ArrayList<ChangeRecord> pendingItemsForUids =
- new ArrayList<>(changesForPendingItems.length);
- for (int i = 0; i < changesForPendingItems.length; ++i) {
+ new ArrayList<>(procStatesForPendingItems.length);
+ for (int i = 0; i < procStatesForPendingItems.length; ++i) {
final ChangeRecord item = new ChangeRecord();
item.uid = i;
item.change = changesForPendingItems[i];
diff --git a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
index 1de5f6f..0db118d 100644
--- a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
@@ -18,6 +18,7 @@
import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_NETWORK;
import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_RECENT;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
@@ -109,6 +110,7 @@
@Test
public void testMergeWithPendingChange() {
+ // Map of expectedChange -> {(currentChange, pendingChange)}
final SparseArray<Pair<Integer, Integer>> changesToVerify = new SparseArray<>();
changesToVerify.put(UidRecord.CHANGE_ACTIVE,
@@ -127,6 +129,8 @@
Pair.create(UidRecord.CHANGE_GONE, UidRecord.CHANGE_ACTIVE));
changesToVerify.put(UidRecord.CHANGE_GONE,
Pair.create(UidRecord.CHANGE_GONE, UidRecord.CHANGE_CACHED));
+ changesToVerify.put(UidRecord.CHANGE_PROCSTATE | UidRecord.CHANGE_CAPABILITY,
+ Pair.create(UidRecord.CHANGE_PROCSTATE, UidRecord.CHANGE_CAPABILITY));
for (int i = 0; i < changesToVerify.size(); ++i) {
final int expectedChange = changesToVerify.keyAt(i);
@@ -149,7 +153,8 @@
ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_ACTIVE,
PROCESS_STATE_IMPORTANT_FOREGROUND, TEST_PKG2, TEST_UID2);
final IUidObserver observer2 = mock(IUidObserver.Stub.class);
- registerObserver(observer2, ActivityManager.UID_OBSERVER_PROCSTATE,
+ registerObserver(observer2,
+ ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_CAPABILITY,
PROCESS_STATE_SERVICE, TEST_PKG3, TEST_UID3);
mUidObserverController.dispatchUidsChanged();
@@ -177,6 +182,14 @@
verifyNoMoreInteractions(observer1);
verifyNoMoreInteractions(observer2);
+ addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE | UidRecord.CHANGE_CAPABILITY,
+ PROCESS_STATE_RECEIVER, 111, PROCESS_CAPABILITY_NETWORK, false);
+ mUidObserverController.dispatchUidsChanged();
+ verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_RECEIVER,
+ 111, PROCESS_CAPABILITY_NETWORK);
+ verifyNoMoreInteractions(observer1);
+ verifyNoMoreInteractions(observer2);
+
unregisterObserver(observer1);
addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_TOP,
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 24a8b61..edc0d46 100644
--- a/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -235,4 +235,37 @@
assertEquals(GameManager.GAME_MODE_STANDARD,
gameManagerService.getGameMode(mPackageName, USER_ID_1));
}
+
+ /**
+ * Test game modes are user-specific.
+ */
+ @Test
+ public void testGameModeMultipleUsers() {
+ GameManagerService gameManagerService = new GameManagerService(mMockContext);
+ gameManagerService.onUserStarting(USER_ID_1);
+ gameManagerService.onUserStarting(USER_ID_2);
+
+ mockModifyGameModeGranted();
+
+ // Set User 1 to Standard
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_STANDARD, USER_ID_1);
+ assertEquals(GameManager.GAME_MODE_STANDARD,
+ gameManagerService.getGameMode(mPackageName, USER_ID_1));
+
+ // Set User 2 to Performance and verify User 1 is still Standard
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_PERFORMANCE,
+ USER_ID_2);
+ assertEquals(GameManager.GAME_MODE_PERFORMANCE,
+ gameManagerService.getGameMode(mPackageName, USER_ID_2));
+ assertEquals(GameManager.GAME_MODE_STANDARD,
+ gameManagerService.getGameMode(mPackageName, USER_ID_1));
+
+ // Set User 1 to Battery and verify User 2 is still Performance
+ gameManagerService.setGameMode(mPackageName, GameManager.GAME_MODE_BATTERY,
+ USER_ID_1);
+ assertEquals(GameManager.GAME_MODE_BATTERY,
+ gameManagerService.getGameMode(mPackageName, USER_ID_1));
+ assertEquals(GameManager.GAME_MODE_PERFORMANCE,
+ gameManagerService.getGameMode(mPackageName, USER_ID_2));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
index 557c14a..9937ec1 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
@@ -19,7 +19,6 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -98,7 +97,7 @@
when(nextClient.getTargetUserId()).thenReturn(nextUserId);
mScheduler.scheduleClientMonitor(nextClient);
- verify(nextClient, never()).start(any());
+
assertEquals(0, mUserStoppedCallback.numInvocations);
assertEquals(1, mUserStartedCallback.numInvocations);
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 5e50bea..d250297 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -571,7 +571,7 @@
*
* @throws SecurityException if the caller does not have the required permission/privileges
*/
- public static void enforeceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
+ public static void enforceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
Context context, int subId, String message) {
if (context.checkCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE)
== PERMISSION_GRANTED) {
@@ -591,7 +591,7 @@
*
* @throws SecurityException if the caller does not have the required permission/privileges
*/
- public static void enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+ public static void enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
Context context, int subId, String message) {
if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
== PERMISSION_GRANTED) {
@@ -613,7 +613,7 @@
*
* @throws SecurityException if the caller does not have the required permission/privileges
*/
- public static void enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ public static void enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
Context context, int subId, String message) {
if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
== PERMISSION_GRANTED) {
diff --git a/tools/hiddenapi/checksorted_sha.sh b/tools/hiddenapi/checksorted_sha.sh
index ceb705f..451fed6 100755
--- a/tools/hiddenapi/checksorted_sha.sh
+++ b/tools/hiddenapi/checksorted_sha.sh
@@ -1,7 +1,7 @@
#!/bin/bash
set -e
LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
-git show --name-only --pretty=format: $1 | grep "config/hiddenapi-.*txt" | while read file; do
+git show --name-only --pretty=format: $1 | grep "boot/hiddenapi/hiddenapi-.*txt" | while read file; do
diff <(git show $1:$file) <(git show $1:$file | $LOCAL_DIR/sort_api.sh ) || {
echo -e "\e[1m\e[31m$file $1 is not sorted or contains duplicates. To sort it correctly:\e[0m"
echo -e "\e[33m${LOCAL_DIR}/sort_api.sh $2/frameworks/base/$file\e[0m"
diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh
index 2924e01..822aba4 100755
--- a/tools/hiddenapi/exclude.sh
+++ b/tools/hiddenapi/exclude.sh
@@ -48,7 +48,7 @@
PACKAGES=$(for t in $TEAMS; do echo $(eval echo \${${t}_PACKAGES}); done)
RE=$(echo ${PACKAGES} | sed "s/ /|/g")
EXIT_CODE=0
-for file in $(git show --name-only --pretty=format: $SHA | grep "config/hiddenapi-.*txt"); do
+for file in $(git show --name-only --pretty=format: $SHA | grep "boot/hiddenapi/hiddenapi-.*txt"); do
ENTRIES=$(grep -E "^\+L(${RE})/" <(git diff ${SHA}~1 ${SHA} $file) | sed "s|^\+||" || echo)
if [[ -n "${ENTRIES}" ]]; then
echo -e "\e[1m\e[31m$file $SHA contains the following entries\e[0m"