Merge "Send AIDL face enrollment frame info to clients" into sc-dev
diff --git a/StubLibraries.bp b/StubLibraries.bp
index e6f50b7..720bfc0 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -358,7 +358,7 @@
visibility: ["//visibility:private"],
}
-java_library_static {
+java_library {
name: "android-non-updatable.stubs",
defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":api-stubs-docs-non-updatable"],
@@ -368,7 +368,7 @@
},
}
-java_library_static {
+java_library {
name: "android-non-updatable.stubs.system",
defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":system-api-stubs-docs-non-updatable"],
@@ -378,7 +378,7 @@
},
}
-java_library_static {
+java_library {
name: "android-non-updatable.stubs.module_lib",
defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":module-lib-api-stubs-docs-non-updatable"],
@@ -392,7 +392,7 @@
},
}
-java_library_static {
+java_library {
name: "android-non-updatable.stubs.test",
defaults: ["android-non-updatable_defaults_stubs_current"],
srcs: [":test-api-stubs-docs-non-updatable"],
@@ -415,7 +415,7 @@
defaults_visibility: ["//frameworks/base/services"],
}
-java_library_static {
+java_library {
name: "android_stubs_current",
static_libs: modules_public_stubs + [
"android-non-updatable.stubs",
@@ -424,7 +424,7 @@
defaults: ["android.jar_defaults"],
}
-java_library_static {
+java_library {
name: "android_system_stubs_current",
static_libs: modules_system_stubs + [
"android-non-updatable.stubs.system",
@@ -450,7 +450,7 @@
],
}
-java_library_static {
+java_library {
name: "android_test_stubs_current",
// Modules do not have test APIs, but we want to include their SystemApis, like we include
// the SystemApi of framework-non-updatable-sources.
@@ -467,7 +467,7 @@
},
}
-java_library_static {
+java_library {
name: "android_module_lib_stubs_current",
defaults: [
"android.jar_defaults",
@@ -482,6 +482,22 @@
},
}
+java_library {
+ name: "android_system_server_stubs_current",
+ defaults: ["android_stubs_dists_default"],
+ srcs: [":services-non-updatable-stubs"],
+ installable: false,
+ static_libs: [
+ "android_module_lib_stubs_current",
+ ],
+ sdk_version: "none",
+ system_modules: "none",
+ java_version: "1.8",
+ dist: {
+ dir: "apistubs/android/system-server",
+ },
+}
+
/////////////////////////////////////////////////////////////////////
// hwbinder.stubs provides APIs required for building HIDL Java
// libraries.
@@ -515,7 +531,7 @@
visibility: ["//visibility:private"],
}
-java_library_static {
+java_library {
name: "hwbinder.stubs",
sdk_version: "core_current",
libs: ["framework-annotations-lib"],
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
index 8a162d4..b7bd387 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java
@@ -96,8 +96,13 @@
}
/**
- * Returns a {@link Set} of schema type that were deleted by the {@link
- * AppSearchSession#setSchema} call.
+ * Returns a {@link Set} of deleted schema types.
+ *
+ * <p>A "deleted" type is a schema type that was previously a part of the database schema but
+ * was not present in the {@link SetSchemaRequest} object provided in the
+ * {@link AppSearchSession#setSchema) call.
+ *
+ * <p>Documents for a deleted type are removed from the database.
*/
@NonNull
public Set<String> getDeletedTypes() {
@@ -113,6 +118,15 @@
/**
* Returns a {@link Set} of schema type that were migrated by the {@link
* AppSearchSession#setSchema} call.
+ *
+ * <p>A "migrated" type is a schema type that has triggered a {@link Migrator} instance to
+ * migrate documents of the schema type to another schema type, or to another version of the
+ * schema type.
+ *
+ * <p>If a document fails to be migrated, a {@link MigrationFailure} will be generated for that
+ * document.
+ *
+ * @see Migrator
*/
@NonNull
public Set<String> getMigratedTypes() {
@@ -132,6 +146,7 @@
* <p>If a {@link Migrator} is provided for this type and the migration is success triggered.
* The type will also appear in {@link #getMigratedTypes()}.
*
+ * @see SetSchemaRequest
* @see AppSearchSession#setSchema
* @see SetSchemaRequest.Builder#setForceOverride
*/
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java
index ca4ea2b..62593ae8 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java
@@ -42,6 +42,8 @@
* Initializes an {@link AppSearchException} with a result code and message.
*
* @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}.
+ * @param message The detail message (which is saved for later retrieval by the {@link
+ * #getMessage()} method).
*/
public AppSearchException(
@AppSearchResult.ResultCode int resultCode, @Nullable String message) {
@@ -52,6 +54,11 @@
* Initializes an {@link AppSearchException} with a result code, message and cause.
*
* @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}.
+ * @param message The detail message (which is saved for later retrieval by the {@link
+ * #getMessage()} method).
+ * @param cause The cause (which is saved for later retrieval by the {@link #getCause()}
+ * method). (A null value is permitted, and indicates that the cause is nonexistent or
+ * unknown.)
*/
public AppSearchException(
@AppSearchResult.ResultCode int resultCode,
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 3f6e8a5..a4188a2 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -501,7 +501,8 @@
packageName,
databaseName,
queryExpression,
- new SearchSpec(searchSpecBundle));
+ new SearchSpec(searchSpecBundle),
+ /*logger=*/ null);
invokeCallbackOnResult(
callback,
AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
@@ -535,7 +536,8 @@
queryExpression,
new SearchSpec(searchSpecBundle),
packageName,
- callingUid);
+ callingUid,
+ /*logger=*/ null);
invokeCallbackOnResult(
callback,
AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
@@ -609,7 +611,8 @@
packageName,
databaseName,
queryExpression,
- new SearchSpec(searchSpecBundle));
+ new SearchSpec(searchSpecBundle),
+ /*logger=*/ null);
while (!searchResultPage.getResults().isEmpty()) {
for (int i = 0; i < searchResultPage.getResults().size(); i++) {
AppSearchMigrationHelper.writeBundleToOutputStream(
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
index 45023f9..94ee830 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -149,7 +149,8 @@
private AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
throws AppSearchException {
File appSearchDir = getAppSearchDir(userId);
- return AppSearchImpl.create(appSearchDir, context, userId, mGlobalQuerierPackage);
+ return AppSearchImpl.create(
+ appSearchDir, context, userId, mGlobalQuerierPackage, /*logger=*/ null);
}
/**
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index 98fcb13..e5e20e7 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -54,7 +54,9 @@
import com.android.server.appsearch.external.localstorage.converter.SearchSpecToProtoConverter;
import com.android.server.appsearch.external.localstorage.converter.SetSchemaResponseToProtoConverter;
import com.android.server.appsearch.external.localstorage.converter.TypePropertyPathToProtoConverter;
+import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
+import com.android.server.appsearch.external.localstorage.stats.SearchStats;
import com.google.android.icing.IcingSearchEngine;
import com.google.android.icing.proto.DeleteByQueryResultProto;
@@ -178,28 +180,63 @@
/**
* Creates and initializes an instance of {@link AppSearchImpl} which writes data to the given
* folder.
+ *
+ * <p>Clients can pass a {@link AppSearchLogger} here through their AppSearchSession, but it
+ * can't be saved inside {@link AppSearchImpl}, because the impl will be shared by all the
+ * sessions for the same package in JetPack.
+ *
+ * <p>Instead, logger instance needs to be passed to each individual method, like create, query
+ * and putDocument.
+ *
+ * @param logger collects stats for initialization if provided.
*/
@NonNull
public static AppSearchImpl create(
@NonNull File icingDir,
@NonNull Context context,
int userId,
- @NonNull String globalQuerierPackage)
+ @NonNull String globalQuerierPackage,
+ @Nullable AppSearchLogger logger)
throws AppSearchException {
Objects.requireNonNull(icingDir);
Objects.requireNonNull(context);
Objects.requireNonNull(globalQuerierPackage);
+
+ long totalLatencyStartMillis = SystemClock.elapsedRealtime();
+ InitializeStats.Builder initStatsBuilder = null;
+ if (logger != null) {
+ initStatsBuilder = new InitializeStats.Builder();
+ }
+
AppSearchImpl appSearchImpl =
- new AppSearchImpl(icingDir, context, userId, globalQuerierPackage);
+ new AppSearchImpl(
+ icingDir, context, userId, globalQuerierPackage, initStatsBuilder);
+
+ long prepareVisibilityStoreLatencyStartMillis = SystemClock.elapsedRealtime();
appSearchImpl.initializeVisibilityStore();
+ long prepareVisibilityStoreLatencyEndMillis = SystemClock.elapsedRealtime();
+
+ if (logger != null && initStatsBuilder != null) {
+ initStatsBuilder
+ .setTotalLatencyMillis(
+ (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis))
+ .setPrepareVisibilityStoreLatencyMillis(
+ (int)
+ (prepareVisibilityStoreLatencyEndMillis
+ - prepareVisibilityStoreLatencyStartMillis));
+ logger.logStats(initStatsBuilder.build());
+ }
+
return appSearchImpl;
}
+ /** @param initStatsBuilder collects stats for initialization if provided. */
private AppSearchImpl(
@NonNull File icingDir,
@NonNull Context context,
int userId,
- @NonNull String globalQuerierPackage)
+ @NonNull String globalQuerierPackage,
+ @Nullable InitializeStats.Builder initStatsBuilder)
throws AppSearchException {
mReadWriteLock.writeLock().lock();
@@ -211,13 +248,24 @@
.setBaseDir(icingDir.getAbsolutePath())
.build();
mIcingSearchEngineLocked = new IcingSearchEngine(options);
-
mVisibilityStoreLocked =
new VisibilityStore(this, context, userId, globalQuerierPackage);
-
InitializeResultProto initializeResultProto = mIcingSearchEngineLocked.initialize();
+
+ if (initStatsBuilder != null) {
+ initStatsBuilder
+ .setStatusCode(
+ statusProtoToAppSearchException(initializeResultProto.getStatus())
+ .getResultCode())
+ // TODO(b/173532925) how to get DeSyncs value
+ .setHasDeSync(false);
+ AppSearchLoggerHelper.copyNativeStats(
+ initializeResultProto.getInitializeStats(), initStatsBuilder);
+ }
+
+ long prepareSchemaAndNamespacesLatencyStartMillis = SystemClock.elapsedRealtime();
SchemaProto schemaProto;
- GetAllNamespacesResultProto getAllNamespacesResultProto;
+ GetAllNamespacesResultProto getAllNamespacesResultProto = null;
try {
checkSuccess(initializeResultProto.getStatus());
schemaProto = getSchemaProtoLocked();
@@ -225,6 +273,17 @@
checkSuccess(getAllNamespacesResultProto.getStatus());
} catch (AppSearchException e) {
Log.w(TAG, "Error initializing, resetting IcingSearchEngine.", e);
+ if (initStatsBuilder != null && getAllNamespacesResultProto != null) {
+ initStatsBuilder
+ .setStatusCode(
+ statusProtoToAppSearchException(
+ getAllNamespacesResultProto.getStatus())
+ .getResultCode())
+ .setPrepareSchemaAndNamespacesLatencyMillis(
+ (int)
+ (SystemClock.elapsedRealtime()
+ - prepareSchemaAndNamespacesLatencyStartMillis));
+ }
// Some error. Reset and see if it fixes it.
resetLocked();
return;
@@ -240,6 +299,14 @@
for (String prefixedNamespace : getAllNamespacesResultProto.getNamespacesList()) {
addToMap(mNamespaceMapLocked, getPrefix(prefixedNamespace), prefixedNamespace);
}
+
+ // logging prepare_schema_and_namespaces latency
+ if (initStatsBuilder != null) {
+ initStatsBuilder.setPrepareSchemaAndNamespacesLatencyMillis(
+ (int)
+ (SystemClock.elapsedRealtime()
+ - prepareSchemaAndNamespacesLatencyStartMillis));
+ }
} finally {
mReadWriteLock.writeLock().unlock();
}
@@ -539,7 +606,7 @@
addToMap(mNamespaceMapLocked, prefix, documentBuilder.getNamespace());
// Logging stats
- if (logger != null) {
+ if (logger != null && pStatsBuilder != null) {
pStatsBuilder
.getGeneralStatsBuilder()
.setStatusCode(
@@ -562,7 +629,7 @@
} finally {
mReadWriteLock.writeLock().unlock();
- if (logger != null) {
+ if (logger != null && pStatsBuilder != null) {
long totalEndTimeMillis = SystemClock.elapsedRealtime();
pStatsBuilder
.getGeneralStatsBuilder()
@@ -644,6 +711,7 @@
* @param databaseName The databaseName this query for.
* @param queryExpression Query String to search.
* @param searchSpec Spec for setting filters, raw query etc.
+ * @param logger logger to collect query stats
* @return The results of performing this search. It may contain an empty list of results if no
* documents matched the query.
* @throws AppSearchException on IcingSearchEngine error.
@@ -653,8 +721,17 @@
@NonNull String packageName,
@NonNull String databaseName,
@NonNull String queryExpression,
- @NonNull SearchSpec searchSpec)
+ @NonNull SearchSpec searchSpec,
+ @Nullable AppSearchLogger logger)
throws AppSearchException {
+ long totalLatencyStartMillis = SystemClock.elapsedRealtime();
+ SearchStats.Builder sStatsBuilder = null;
+ if (logger != null) {
+ sStatsBuilder =
+ new SearchStats.Builder(SearchStats.VISIBILITY_SCOPE_LOCAL, packageName)
+ .setDatabase(databaseName);
+ }
+
mReadWriteLock.readLock().lock();
try {
throwIfClosedLocked();
@@ -673,9 +750,15 @@
Collections.singleton(createPrefix(packageName, databaseName)),
allowedPrefixedSchemas,
queryExpression,
- searchSpec);
+ searchSpec,
+ sStatsBuilder);
} finally {
mReadWriteLock.readLock().unlock();
+ if (logger != null && sStatsBuilder != null) {
+ sStatsBuilder.setTotalLatencyMillis(
+ (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis));
+ logger.logStats(sStatsBuilder.build());
+ }
}
}
@@ -689,6 +772,7 @@
* @param searchSpec Spec for setting filters, raw query etc.
* @param callerPackageName Package name of the caller, should belong to the {@code callerUid}.
* @param callerUid UID of the client making the globalQuery call.
+ * @param logger logger to collect globalQuery stats
* @return The results of performing this search. It may contain an empty list of results if no
* documents matched the query.
* @throws AppSearchException on IcingSearchEngine error.
@@ -698,8 +782,16 @@
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec,
@NonNull String callerPackageName,
- int callerUid)
+ int callerUid,
+ @Nullable AppSearchLogger logger)
throws AppSearchException {
+ long totalLatencyStartMillis = SystemClock.elapsedRealtime();
+ SearchStats.Builder sStatsBuilder = null;
+ if (logger != null) {
+ sStatsBuilder =
+ new SearchStats.Builder(SearchStats.VISIBILITY_SCOPE_GLOBAL, callerPackageName);
+ }
+
mReadWriteLock.readLock().lock();
try {
throwIfClosedLocked();
@@ -754,9 +846,19 @@
}
return doQueryLocked(
- prefixFilters, allowedPrefixedSchemas, queryExpression, searchSpec);
+ prefixFilters,
+ allowedPrefixedSchemas,
+ queryExpression,
+ searchSpec,
+ sStatsBuilder);
} finally {
mReadWriteLock.readLock().unlock();
+
+ if (logger != null && sStatsBuilder != null) {
+ sStatsBuilder.setTotalLatencyMillis(
+ (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis));
+ logger.logStats(sStatsBuilder.build());
+ }
}
}
@@ -794,8 +896,11 @@
@NonNull Set<String> prefixes,
@NonNull Set<String> allowedPrefixedSchemas,
@NonNull String queryExpression,
- @NonNull SearchSpec searchSpec)
+ @NonNull SearchSpec searchSpec,
+ @Nullable SearchStats.Builder sStatsBuilder)
throws AppSearchException {
+ long rewriteSearchSpecLatencyStartMillis = SystemClock.elapsedRealtime();
+
SearchSpecProto.Builder searchSpecBuilder =
SearchSpecToProtoConverter.toSearchSpecProto(searchSpec).toBuilder()
.setQuery(queryExpression);
@@ -804,9 +909,17 @@
// sending request to Icing.
if (!rewriteSearchSpecForPrefixesLocked(
searchSpecBuilder, prefixes, allowedPrefixedSchemas)) {
+ if (sStatsBuilder != null) {
+ sStatsBuilder.setRewriteSearchSpecLatencyMillis(
+ (int)
+ (SystemClock.elapsedRealtime()
+ - rewriteSearchSpecLatencyStartMillis));
+ }
return new SearchResultPage(Bundle.EMPTY);
}
+ // rewriteSearchSpec, rewriteResultSpec and convertScoringSpec are all counted in
+ // rewriteSearchSpecLatencyMillis
ResultSpecProto.Builder resultSpecBuilder =
SearchSpecToProtoConverter.toResultSpecProto(searchSpec).toBuilder();
@@ -822,15 +935,38 @@
addPerNamespaceResultGroupingsLocked(
resultSpecBuilder, prefixes, searchSpec.getResultGroupingLimit());
}
- rewriteResultSpecForPrefixesLocked(resultSpecBuilder, prefixes, allowedPrefixedSchemas);
+ rewriteResultSpecForPrefixesLocked(resultSpecBuilder, prefixes, allowedPrefixedSchemas);
ScoringSpecProto scoringSpec = SearchSpecToProtoConverter.toScoringSpecProto(searchSpec);
+
+ long rewriteSearchSpecLatencyEndMillis = SystemClock.elapsedRealtime();
+
SearchResultProto searchResultProto =
mIcingSearchEngineLocked.search(
searchSpecBuilder.build(), scoringSpec, resultSpecBuilder.build());
+
+ if (sStatsBuilder != null) {
+ sStatsBuilder
+ .setStatusCode(
+ statusProtoToAppSearchException(searchResultProto.getStatus())
+ .getResultCode())
+ .setRewriteSearchSpecLatencyMillis(
+ (int)
+ (rewriteSearchSpecLatencyEndMillis
+ - rewriteSearchSpecLatencyStartMillis));
+ AppSearchLoggerHelper.copyNativeStats(searchResultProto.getQueryStats(), sStatsBuilder);
+ }
+
checkSuccess(searchResultProto.getStatus());
- return rewriteSearchResultProto(searchResultProto, mSchemaMapLocked);
+ long rewriteSearchResultLatencyStartMillis = SystemClock.elapsedRealtime();
+ SearchResultPage resultPage = rewriteSearchResultProto(searchResultProto, mSchemaMapLocked);
+ if (sStatsBuilder != null) {
+ sStatsBuilder.setRewriteSearchResultLatencyMillis(
+ (int) (SystemClock.elapsedRealtime() - rewriteSearchResultLatencyStartMillis));
+ }
+
+ return resultPage;
}
/**
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java
index 0f23d92..97c8869 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java
@@ -20,7 +20,9 @@
import android.app.appsearch.exceptions.AppSearchException;
import com.android.server.appsearch.external.localstorage.stats.CallStats;
+import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
+import com.android.server.appsearch.external.localstorage.stats.SearchStats;
/**
* An interface for implementing client-defined logging AppSearch operations stats.
@@ -39,5 +41,11 @@
/** Logs {@link PutDocumentStats} */
void logStats(@NonNull PutDocumentStats stats) throws AppSearchException;
+ /** Logs {@link InitializeStats} */
+ void logStats(@NonNull InitializeStats stats) throws AppSearchException;
+
+ /** Logs {@link SearchStats} */
+ void logStats(@NonNull SearchStats stats) throws AppSearchException;
+
// TODO(b/173532925) Add remaining logStats once we add all the stats.
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
index cdd7952..4a5ecf1 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
@@ -18,9 +18,13 @@
import android.annotation.NonNull;
+import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
+import com.android.server.appsearch.external.localstorage.stats.SearchStats;
+import com.google.android.icing.proto.InitializeStatsProto;
import com.google.android.icing.proto.PutDocumentStatsProto;
+import com.google.android.icing.proto.QueryStatsProto;
import java.util.Objects;
@@ -35,7 +39,7 @@
private AppSearchLoggerHelper() {}
/**
- * Copies native stats to builder.
+ * Copies native PutDocument stats to builder.
*
* @param fromNativeStats stats copied from
* @param toStatsBuilder stats copied to
@@ -56,4 +60,64 @@
.setNativeExceededMaxNumTokens(
fromNativeStats.getTokenizationStats().getExceededMaxTokenNum());
}
+
+ /**
+ * Copies native Initialize stats to builder.
+ *
+ * @param fromNativeStats stats copied from
+ * @param toStatsBuilder stats copied to
+ */
+ static void copyNativeStats(
+ @NonNull InitializeStatsProto fromNativeStats,
+ @NonNull InitializeStats.Builder toStatsBuilder) {
+ Objects.requireNonNull(fromNativeStats);
+ Objects.requireNonNull(toStatsBuilder);
+ toStatsBuilder
+ .setNativeLatencyMillis(fromNativeStats.getLatencyMs())
+ .setDocumentStoreRecoveryCause(
+ fromNativeStats.getDocumentStoreRecoveryCause().getNumber())
+ .setIndexRestorationCause(fromNativeStats.getIndexRestorationCause().getNumber())
+ .setSchemaStoreRecoveryCause(
+ fromNativeStats.getSchemaStoreRecoveryCause().getNumber())
+ .setDocumentStoreRecoveryLatencyMillis(
+ fromNativeStats.getDocumentStoreRecoveryLatencyMs())
+ .setIndexRestorationLatencyMillis(fromNativeStats.getIndexRestorationLatencyMs())
+ .setSchemaStoreRecoveryLatencyMillis(
+ fromNativeStats.getSchemaStoreRecoveryLatencyMs())
+ .setDocumentStoreDataStatus(
+ fromNativeStats.getDocumentStoreDataStatus().getNumber())
+ .setDocumentCount(fromNativeStats.getNumDocuments())
+ .setSchemaTypeCount(fromNativeStats.getNumSchemaTypes());
+ }
+
+ /*
+ * Copy native Query stats to buiilder.
+ *
+ * @param fromNativeStats Stats copied from.
+ * @param toStatsBuilder Stats copied to.
+ */
+ static void copyNativeStats(
+ @NonNull QueryStatsProto fromNativeStats, @NonNull SearchStats.Builder toStatsBuilder) {
+ Objects.requireNonNull(fromNativeStats);
+ Objects.requireNonNull(toStatsBuilder);
+ toStatsBuilder
+ .setNativeLatencyMillis(fromNativeStats.getLatencyMs())
+ .setTermCount(fromNativeStats.getNumTerms())
+ // TODO(b/173532925) query length missing in native
+ // .setNativeQueryLength(0)
+ .setFilteredNamespaceCount(fromNativeStats.getNumNamespacesFiltered())
+ .setFilteredSchemaTypeCount(fromNativeStats.getNumSchemaTypesFiltered())
+ .setRequestedPageSize(fromNativeStats.getRequestedPageSize())
+ .setCurrentPageReturnedResultCount(
+ fromNativeStats.getNumResultsReturnedCurrentPage())
+ .setIsFirstPage(fromNativeStats.getIsFirstPage())
+ .setParseQueryLatencyMillis(fromNativeStats.getParseQueryLatencyMs())
+ .setRankingStrategy(fromNativeStats.getRankingStrategy().getNumber())
+ .setScoredDocumentCount(fromNativeStats.getNumDocumentsScored())
+ .setScoringLatencyMillis(fromNativeStats.getScoringLatencyMs())
+ .setRankingLatencyMillis(fromNativeStats.getRankingLatencyMs())
+ .setResultWithSnippetsCount(fromNativeStats.getNumResultsWithSnippets())
+ .setDocumentRetrievingLatencyMillis(
+ fromNativeStats.getDocumentRetrievalLatencyMs());
+ }
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/InitializeStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/InitializeStats.java
new file mode 100644
index 0000000..5364a0c
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/InitializeStats.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright 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.appsearch.external.localstorage.stats;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchResult;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Class holds detailed stats for initialization
+ *
+ * @hide
+ */
+public final class InitializeStats {
+ /**
+ * The cause of IcingSearchEngine recovering from a previous bad state during initialization.
+ */
+ @IntDef(
+ value = {
+ // It needs to be sync with RecoveryCause in
+ // external/icing/proto/icing/proto/logging.proto#InitializeStatsProto
+ RECOVERY_CAUSE_NONE,
+ RECOVERY_CAUSE_DATA_LOSS,
+ RECOVERY_CAUSE_INCONSISTENT_WITH_GROUND_TRUTH,
+ RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH,
+ RECOVERY_CAUSE_IO_ERROR,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RecoveryCause {}
+
+ // No recovery happened.
+ public static final int RECOVERY_CAUSE_NONE = 0;
+ // Data loss in ground truth.
+ public static final int RECOVERY_CAUSE_DATA_LOSS = 1;
+ // Data in index is inconsistent with ground truth.
+ public static final int RECOVERY_CAUSE_INCONSISTENT_WITH_GROUND_TRUTH = 2;
+ // Total checksum of all the components does not match.
+ public static final int RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH = 3;
+ // Random I/O errors.
+ public static final int RECOVERY_CAUSE_IO_ERROR = 4;
+
+ /** Status regarding how much data is lost during the initialization. */
+ @IntDef(
+ value = {
+ // It needs to be sync with DocumentStoreDataStatus in
+ // external/icing/proto/icing/proto/logging.proto#InitializeStatsProto
+
+ DOCUMENT_STORE_DATA_STATUS_NO_DATA_LOSS,
+ DOCUMENT_STORE_DATA_STATUS_PARTIAL_LOSS,
+ DOCUMENT_STORE_DATA_STATUS_COMPLETE_LOSS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DocumentStoreDataStatus {}
+
+ // Document store is successfully initialized or fully recovered.
+ public static final int DOCUMENT_STORE_DATA_STATUS_NO_DATA_LOSS = 0;
+ // Ground truth data is partially lost.
+ public static final int DOCUMENT_STORE_DATA_STATUS_PARTIAL_LOSS = 1;
+ // Ground truth data is completely lost.
+ public static final int DOCUMENT_STORE_DATA_STATUS_COMPLETE_LOSS = 2;
+
+ @AppSearchResult.ResultCode private final int mStatusCode;
+ private final int mTotalLatencyMillis;
+ /** Whether the initialize() detects deSync. */
+ private final boolean mHasDeSync;
+ /** Time used to read and process the schema and namespaces. */
+ private final int mPrepareSchemaAndNamespacesLatencyMillis;
+ /** Time used to read and process the visibility store. */
+ private final int mPrepareVisibilityStoreLatencyMillis;
+ /** Overall time used for the native function call. */
+ private final int mNativeLatencyMillis;
+
+ @RecoveryCause private final int mNativeDocumentStoreRecoveryCause;
+ @RecoveryCause private final int mNativeIndexRestorationCause;
+ @RecoveryCause private final int mNativeSchemaStoreRecoveryCause;
+ /** Time used to recover the document store. */
+ private final int mNativeDocumentStoreRecoveryLatencyMillis;
+ /** Time used to restore the index. */
+ private final int mNativeIndexRestorationLatencyMillis;
+ /** Time used to recover the schema store. */
+ private final int mNativeSchemaStoreRecoveryLatencyMillis;
+ /** Status regarding how much data is lost during the initialization. */
+ private final int mNativeDocumentStoreDataStatus;
+ /**
+ * Returns number of documents currently in document store. Those may include alive, deleted,
+ * and expired documents.
+ */
+ private final int mNativeNumDocuments;
+ /** Returns number of schema types currently in the schema store. */
+ private final int mNativeNumSchemaTypes;
+
+ /** Returns the status of the initialization. */
+ @AppSearchResult.ResultCode
+ public int getStatusCode() {
+ return mStatusCode;
+ }
+
+ /** Returns the total latency in milliseconds for the initialization. */
+ public int getTotalLatencyMillis() {
+ return mTotalLatencyMillis;
+ }
+
+ /**
+ * Returns whether the initialize() detects deSync.
+ *
+ * <p>If there is a deSync, it means AppSearch and IcingSearchEngine have an inconsistent view
+ * of what data should exist.
+ */
+ public boolean hasDeSync() {
+ return mHasDeSync;
+ }
+
+ /** Returns time used to read and process the schema and namespaces. */
+ public int getPrepareSchemaAndNamespacesLatencyMillis() {
+ return mPrepareSchemaAndNamespacesLatencyMillis;
+ }
+
+ /** Returns time used to read and process the visibility file. */
+ public int getPrepareVisibilityStoreLatencyMillis() {
+ return mPrepareVisibilityStoreLatencyMillis;
+ }
+
+ /** Returns overall time used for the native function call. */
+ public int getNativeLatencyMillis() {
+ return mNativeLatencyMillis;
+ }
+
+ /**
+ * Returns recovery cause for document store.
+ *
+ * <p>Possible recovery causes for document store:
+ * <li>{@link InitializeStats#RECOVERY_CAUSE_DATA_LOSS}
+ * <li>{@link InitializeStats#RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH}
+ * <li>{@link InitializeStats#RECOVERY_CAUSE_IO_ERROR}
+ */
+ @RecoveryCause
+ public int getDocumentStoreRecoveryCause() {
+ return mNativeDocumentStoreRecoveryCause;
+ }
+
+ /**
+ * Returns restoration cause for index store.
+ *
+ * <p>Possible causes:
+ * <li>{@link InitializeStats#RECOVERY_CAUSE_INCONSISTENT_WITH_GROUND_TRUTH}
+ * <li>{@link InitializeStats#RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH}
+ * <li>{@link InitializeStats#RECOVERY_CAUSE_IO_ERROR}
+ */
+ @RecoveryCause
+ public int getIndexRestorationCause() {
+ return mNativeIndexRestorationCause;
+ }
+
+ /**
+ * Returns recovery cause for schema store.
+ *
+ * <p>Possible causes:
+ * <li>IO_ERROR
+ */
+ @RecoveryCause
+ public int getSchemaStoreRecoveryCause() {
+ return mNativeSchemaStoreRecoveryCause;
+ }
+
+ /** Returns time used to recover the document store. */
+ public int getDocumentStoreRecoveryLatencyMillis() {
+ return mNativeDocumentStoreRecoveryLatencyMillis;
+ }
+
+ /** Returns time used to restore the index. */
+ public int getIndexRestorationLatencyMillis() {
+ return mNativeIndexRestorationLatencyMillis;
+ }
+
+ /** Returns time used to recover the schema store. */
+ public int getSchemaStoreRecoveryLatencyMillis() {
+ return mNativeSchemaStoreRecoveryLatencyMillis;
+ }
+
+ /** Returns status about how much data is lost during the initialization. */
+ @DocumentStoreDataStatus
+ public int getDocumentStoreDataStatus() {
+ return mNativeDocumentStoreDataStatus;
+ }
+
+ /**
+ * Returns number of documents currently in document store. Those may include alive, deleted,
+ * and expired documents.
+ */
+ public int getDocumentCount() {
+ return mNativeNumDocuments;
+ }
+
+ /** Returns number of schema types currently in the schema store. */
+ public int getSchemaTypeCount() {
+ return mNativeNumSchemaTypes;
+ }
+
+ InitializeStats(@NonNull Builder builder) {
+ Objects.requireNonNull(builder);
+ mStatusCode = builder.mStatusCode;
+ mTotalLatencyMillis = builder.mTotalLatencyMillis;
+ mHasDeSync = builder.mHasDeSync;
+ mPrepareSchemaAndNamespacesLatencyMillis = builder.mPrepareSchemaAndNamespacesLatencyMillis;
+ mPrepareVisibilityStoreLatencyMillis = builder.mPrepareVisibilityStoreLatencyMillis;
+ mNativeLatencyMillis = builder.mNativeLatencyMillis;
+ mNativeDocumentStoreRecoveryCause = builder.mNativeDocumentStoreRecoveryCause;
+ mNativeIndexRestorationCause = builder.mNativeIndexRestorationCause;
+ mNativeSchemaStoreRecoveryCause = builder.mNativeSchemaStoreRecoveryCause;
+ mNativeDocumentStoreRecoveryLatencyMillis =
+ builder.mNativeDocumentStoreRecoveryLatencyMillis;
+ mNativeIndexRestorationLatencyMillis = builder.mNativeIndexRestorationLatencyMillis;
+ mNativeSchemaStoreRecoveryLatencyMillis = builder.mNativeSchemaStoreRecoveryLatencyMillis;
+ mNativeDocumentStoreDataStatus = builder.mNativeDocumentStoreDataStatus;
+ mNativeNumDocuments = builder.mNativeNumDocuments;
+ mNativeNumSchemaTypes = builder.mNativeNumSchemaTypes;
+ }
+
+ /** Builder for {@link InitializeStats}. */
+ public static class Builder {
+ @AppSearchResult.ResultCode int mStatusCode;
+ int mTotalLatencyMillis;
+ boolean mHasDeSync;
+ int mPrepareSchemaAndNamespacesLatencyMillis;
+ int mPrepareVisibilityStoreLatencyMillis;
+ int mNativeLatencyMillis;
+ @RecoveryCause int mNativeDocumentStoreRecoveryCause;
+ @RecoveryCause int mNativeIndexRestorationCause;
+ @RecoveryCause int mNativeSchemaStoreRecoveryCause;
+ int mNativeDocumentStoreRecoveryLatencyMillis;
+ int mNativeIndexRestorationLatencyMillis;
+ int mNativeSchemaStoreRecoveryLatencyMillis;
+ @DocumentStoreDataStatus int mNativeDocumentStoreDataStatus;
+ int mNativeNumDocuments;
+ int mNativeNumSchemaTypes;
+
+ /** Sets the status of the initialization. */
+ @NonNull
+ public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) {
+ mStatusCode = statusCode;
+ return this;
+ }
+
+ /** Sets the total latency of the initialization in milliseconds. */
+ @NonNull
+ public Builder setTotalLatencyMillis(int totalLatencyMillis) {
+ mTotalLatencyMillis = totalLatencyMillis;
+ return this;
+ }
+
+ /**
+ * Sets whether the initialize() detects deSync.
+ *
+ * <p>If there is a deSync, it means AppSearch and IcingSearchEngine have an inconsistent
+ * view of what data should exist.
+ */
+ @NonNull
+ public Builder setHasDeSync(boolean hasDeSync) {
+ mHasDeSync = hasDeSync;
+ return this;
+ }
+
+ /** Sets time used to read and process the schema and namespaces. */
+ @NonNull
+ public Builder setPrepareSchemaAndNamespacesLatencyMillis(
+ int prepareSchemaAndNamespacesLatencyMillis) {
+ mPrepareSchemaAndNamespacesLatencyMillis = prepareSchemaAndNamespacesLatencyMillis;
+ return this;
+ }
+
+ /** Sets time used to read and process the visibility file. */
+ @NonNull
+ public Builder setPrepareVisibilityStoreLatencyMillis(
+ int prepareVisibilityStoreLatencyMillis) {
+ mPrepareVisibilityStoreLatencyMillis = prepareVisibilityStoreLatencyMillis;
+ return this;
+ }
+
+ /** Sets overall time used for the native function call. */
+ @NonNull
+ public Builder setNativeLatencyMillis(int nativeLatencyMillis) {
+ mNativeLatencyMillis = nativeLatencyMillis;
+ return this;
+ }
+
+ /**
+ * Sets recovery cause for document store.
+ *
+ * <p>Possible recovery causes for document store:
+ * <li>{@link InitializeStats#RECOVERY_CAUSE_DATA_LOSS}
+ * <li>{@link InitializeStats#RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH}
+ * <li>{@link InitializeStats#RECOVERY_CAUSE_IO_ERROR}
+ */
+ @NonNull
+ public Builder setDocumentStoreRecoveryCause(
+ @RecoveryCause int documentStoreRecoveryCause) {
+ mNativeDocumentStoreRecoveryCause = documentStoreRecoveryCause;
+ return this;
+ }
+
+ /**
+ * Sets restoration cause for index store.
+ *
+ * <p>Possible causes:
+ * <li>{@link InitializeStats#DOCUMENT_STORE_DATA_STATUS_COMPLETE_LOSS}
+ * <li>{@link InitializeStats#RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH}
+ * <li>{@link InitializeStats#RECOVERY_CAUSE_IO_ERROR}
+ */
+ @NonNull
+ public Builder setIndexRestorationCause(@RecoveryCause int indexRestorationCause) {
+ mNativeIndexRestorationCause = indexRestorationCause;
+ return this;
+ }
+
+ /**
+ * Returns recovery cause for schema store.
+ *
+ * <p>Possible causes:
+ * <li>{@link InitializeStats#RECOVERY_CAUSE_IO_ERROR}
+ */
+ @NonNull
+ public Builder setSchemaStoreRecoveryCause(@RecoveryCause int schemaStoreRecoveryCause) {
+ mNativeSchemaStoreRecoveryCause = schemaStoreRecoveryCause;
+ return this;
+ }
+
+ /** Sets time used to recover the document store. */
+ @NonNull
+ public Builder setDocumentStoreRecoveryLatencyMillis(
+ int documentStoreRecoveryLatencyMillis) {
+ mNativeDocumentStoreRecoveryLatencyMillis = documentStoreRecoveryLatencyMillis;
+ return this;
+ }
+
+ /** Sets time used to restore the index. */
+ @NonNull
+ public Builder setIndexRestorationLatencyMillis(int indexRestorationLatencyMillis) {
+ mNativeIndexRestorationLatencyMillis = indexRestorationLatencyMillis;
+ return this;
+ }
+
+ /** Sets time used to recover the schema store. */
+ @NonNull
+ public Builder setSchemaStoreRecoveryLatencyMillis(int schemaStoreRecoveryLatencyMillis) {
+ mNativeSchemaStoreRecoveryLatencyMillis = schemaStoreRecoveryLatencyMillis;
+ return this;
+ }
+
+ /**
+ * Sets Native Document Store Data status. status is defined in
+ * external/icing/proto/icing/proto/logging.proto
+ */
+ @NonNull
+ public Builder setDocumentStoreDataStatus(
+ @DocumentStoreDataStatus int documentStoreDataStatus) {
+ mNativeDocumentStoreDataStatus = documentStoreDataStatus;
+ return this;
+ }
+
+ /**
+ * Sets number of documents currently in document store. Those may include alive, deleted,
+ * and expired documents.
+ */
+ @NonNull
+ public Builder setDocumentCount(int numDocuments) {
+ mNativeNumDocuments = numDocuments;
+ return this;
+ }
+
+ /** Sets number of schema types currently in the schema store. */
+ @NonNull
+ public Builder setSchemaTypeCount(int numSchemaTypes) {
+ mNativeNumSchemaTypes = numSchemaTypes;
+ return this;
+ }
+
+ /**
+ * Constructs a new {@link InitializeStats} from the contents of this {@link
+ * InitializeStats.Builder}
+ */
+ @NonNull
+ public InitializeStats build() {
+ return new InitializeStats(/* builder= */ this);
+ }
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SearchStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SearchStats.java
new file mode 100644
index 0000000..7b22b2f
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SearchStats.java
@@ -0,0 +1,460 @@
+/*
+ * Copyright 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.appsearch.external.localstorage.stats;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.SearchSpec;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Class holds detailed stats for {@link android.app.appsearch.AppSearchSession#search(String,
+ * SearchSpec)}
+ *
+ * @hide
+ */
+public final class SearchStats {
+ @IntDef(
+ value = {
+ // Searches apps' own documents.
+ VISIBILITY_SCOPE_LOCAL,
+ // Searches the global documents. Including platform surfaceable and 3p-access.
+ VISIBILITY_SCOPE_GLOBAL,
+ // TODO(b/173532925) Add THIRD_PARTY_ACCESS once we can distinguish platform
+ // surfaceable from 3p access(right both of them are categorized as
+ // VISIBILITY_SCOPE_GLOBAL)
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VisibilityScope {}
+
+ // Searches apps' own documents.
+ public static final int VISIBILITY_SCOPE_LOCAL = 1;
+ // Searches the global documents. Including platform surfaceable and 3p-access.
+ public static final int VISIBILITY_SCOPE_GLOBAL = 2;
+
+ @NonNull private final String mPackageName;
+ @Nullable private final String mDatabase;
+ /**
+ * The status code returned by {@link AppSearchResult#getResultCode()} for the call or internal
+ * state.
+ */
+ @AppSearchResult.ResultCode private final int mStatusCode;
+
+ private final int mTotalLatencyMillis;
+ /** Time used to rewrite the search spec. */
+ private final int mRewriteSearchSpecLatencyMillis;
+ /** Time used to rewrite the search results. */
+ private final int mRewriteSearchResultLatencyMillis;
+ /** Defines the scope the query is searching over */
+ @VisibilityScope private final int mVisibilityScope;
+ /** Overall time used for the native function call. */
+ private final int mNativeLatencyMillis;
+ /** Number of terms in the query string. */
+ private final int mNativeNumTerms;
+ /** Length of the query string. */
+ private final int mNativeQueryLength;
+ /** Number of namespaces filtered. */
+ private final int mNativeNumNamespacesFiltered;
+ /** Number of schema types filtered. */
+ private final int mNativeNumSchemaTypesFiltered;
+ /** The requested number of results in one page. */
+ private final int mNativeRequestedPageSize;
+ /** The actual number of results returned in the current page. */
+ private final int mNativeNumResultsReturnedCurrentPage;
+ /**
+ * Whether the function call is querying the first page. If it's not, Icing will fetch the
+ * results from cache so that some steps may be skipped.
+ */
+ private final boolean mNativeIsFirstPage;
+ /**
+ * Time used to parse the query, including 2 parts: tokenizing and transforming tokens into an
+ * iterator tree.
+ */
+ private final int mNativeParseQueryLatencyMillis;
+ /** Strategy of scoring and ranking. */
+ @SearchSpec.RankingStrategy private final int mNativeRankingStrategy;
+ /** Number of documents scored. */
+ private final int mNativeNumDocumentsScored;
+ /** Time used to score the raw results. */
+ private final int mNativeScoringLatencyMillis;
+ /** Time used to rank the scored results. */
+ private final int mNativeRankingLatencyMillis;
+ /**
+ * Time used to fetch the document protos. Note that it includes the time to snippet if {@link
+ * SearchStats#mNativeNumResultsWithSnippets} is greater than 0.
+ */
+ private final int mNativeDocumentRetrievingLatencyMillis;
+ /** How many snippets are calculated. */
+ private final int mNativeNumResultsWithSnippets;
+
+ SearchStats(@NonNull Builder builder) {
+ Objects.requireNonNull(builder);
+ mPackageName = builder.mPackageName;
+ mDatabase = builder.mDatabase;
+ mStatusCode = builder.mStatusCode;
+ mTotalLatencyMillis = builder.mTotalLatencyMillis;
+ mRewriteSearchSpecLatencyMillis = builder.mRewriteSearchSpecLatencyMillis;
+ mRewriteSearchResultLatencyMillis = builder.mRewriteSearchResultLatencyMillis;
+ mVisibilityScope = builder.mVisibilityScope;
+ mNativeLatencyMillis = builder.mNativeLatencyMillis;
+ mNativeNumTerms = builder.mNativeNumTerms;
+ mNativeQueryLength = builder.mNativeQueryLength;
+ mNativeNumNamespacesFiltered = builder.mNativeNumNamespacesFiltered;
+ mNativeNumSchemaTypesFiltered = builder.mNativeNumSchemaTypesFiltered;
+ mNativeRequestedPageSize = builder.mNativeRequestedPageSize;
+ mNativeNumResultsReturnedCurrentPage = builder.mNativeNumResultsReturnedCurrentPage;
+ mNativeIsFirstPage = builder.mNativeIsFirstPage;
+ mNativeParseQueryLatencyMillis = builder.mNativeParseQueryLatencyMillis;
+ mNativeRankingStrategy = builder.mNativeRankingStrategy;
+ mNativeNumDocumentsScored = builder.mNativeNumDocumentsScored;
+ mNativeScoringLatencyMillis = builder.mNativeScoringLatencyMillis;
+ mNativeRankingLatencyMillis = builder.mNativeRankingLatencyMillis;
+ mNativeNumResultsWithSnippets = builder.mNativeNumResultsWithSnippets;
+ mNativeDocumentRetrievingLatencyMillis = builder.mNativeDocumentRetrievingLatencyMillis;
+ }
+
+ /** Returns the package name of the session. */
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * Returns the database name of the session.
+ *
+ * @return database name used by the session. {@code null} if and only if it is a global
+ * search(visibilityScope is {@link SearchStats#VISIBILITY_SCOPE_GLOBAL}).
+ */
+ @Nullable
+ public String getDatabase() {
+ return mDatabase;
+ }
+
+ /** Returns status of the search. */
+ @AppSearchResult.ResultCode
+ public int getStatusCode() {
+ return mStatusCode;
+ }
+
+ /** Returns the total latency of the search. */
+ public int getTotalLatencyMillis() {
+ return mTotalLatencyMillis;
+ }
+
+ /** Returns how much time spent on rewriting the {@link SearchSpec}. */
+ public int getRewriteSearchSpecLatencyMillis() {
+ return mRewriteSearchSpecLatencyMillis;
+ }
+
+ /** Returns how much time spent on rewriting the {@link android.app.appsearch.SearchResult}. */
+ public int getRewriteSearchResultLatencyMillis() {
+ return mRewriteSearchResultLatencyMillis;
+ }
+
+ /** Returns the visibility scope of the search. */
+ @VisibilityScope
+ public int getVisibilityScope() {
+ return mVisibilityScope;
+ }
+
+ /** Returns how much time spent on the native calls. */
+ public int getNativeLatencyMillis() {
+ return mNativeLatencyMillis;
+ }
+
+ /** Returns number of terms in the search string. */
+ public int getTermCount() {
+ return mNativeNumTerms;
+ }
+
+ /** Returns the length of the search string. */
+ public int getQueryLength() {
+ return mNativeQueryLength;
+ }
+
+ /** Returns number of namespaces filtered. */
+ public int getFilteredNamespaceCount() {
+ return mNativeNumNamespacesFiltered;
+ }
+
+ /** Returns number of schema types filtered. */
+ public int getFilteredSchemaTypeCount() {
+ return mNativeNumSchemaTypesFiltered;
+ }
+
+ /** Returns the requested number of results in one page. */
+ public int getRequestedPageSize() {
+ return mNativeRequestedPageSize;
+ }
+
+ /** Returns the actual number of results returned in the current page. */
+ public int getCurrentPageReturnedResultCount() {
+ return mNativeNumResultsReturnedCurrentPage;
+ }
+
+ // TODO(b/185184738) Make it an integer to show how many pages having been returned.
+ /** Returns whether the function call is querying the first page. */
+ public boolean isFirstPage() {
+ return mNativeIsFirstPage;
+ }
+
+ /**
+ * Returns time used to parse the query, including 2 parts: tokenizing and transforming tokens
+ * into an iterator tree.
+ */
+ public int getParseQueryLatencyMillis() {
+ return mNativeParseQueryLatencyMillis;
+ }
+
+ /** Returns strategy of scoring and ranking. */
+ @SearchSpec.RankingStrategy
+ public int getRankingStrategy() {
+ return mNativeRankingStrategy;
+ }
+
+ /** Returns number of documents scored. */
+ public int getScoredDocumentCount() {
+ return mNativeNumDocumentsScored;
+ }
+
+ /** Returns time used to score the raw results. */
+ public int getScoringLatencyMillis() {
+ return mNativeScoringLatencyMillis;
+ }
+
+ /** Returns time used to rank the scored results. */
+ public int getRankingLatencyMillis() {
+ return mNativeRankingLatencyMillis;
+ }
+
+ /**
+ * Returns time used to fetch the document protos. Note that it includes the time to snippet if
+ * {@link SearchStats#mNativeNumResultsWithSnippets} is not zero.
+ */
+ public int getDocumentRetrievingLatencyMillis() {
+ return mNativeDocumentRetrievingLatencyMillis;
+ }
+
+ /** Returns the number of the results in the page returned were snippeted. */
+ public int getResultWithSnippetsCount() {
+ return mNativeNumResultsWithSnippets;
+ }
+
+ /** Builder for {@link SearchStats} */
+ public static class Builder {
+ @NonNull final String mPackageName;
+ @Nullable String mDatabase;
+ @AppSearchResult.ResultCode int mStatusCode;
+ int mTotalLatencyMillis;
+ int mRewriteSearchSpecLatencyMillis;
+ int mRewriteSearchResultLatencyMillis;
+ int mVisibilityScope;
+ int mNativeLatencyMillis;
+ int mNativeNumTerms;
+ int mNativeQueryLength;
+ int mNativeNumNamespacesFiltered;
+ int mNativeNumSchemaTypesFiltered;
+ int mNativeRequestedPageSize;
+ int mNativeNumResultsReturnedCurrentPage;
+ boolean mNativeIsFirstPage;
+ int mNativeParseQueryLatencyMillis;
+ int mNativeRankingStrategy;
+ int mNativeNumDocumentsScored;
+ int mNativeScoringLatencyMillis;
+ int mNativeRankingLatencyMillis;
+ int mNativeNumResultsWithSnippets;
+ int mNativeDocumentRetrievingLatencyMillis;
+
+ /**
+ * Constructor
+ *
+ * @param visibilityScope scope for the corresponding search.
+ * @param packageName name of the calling package.
+ */
+ public Builder(@VisibilityScope int visibilityScope, @NonNull String packageName) {
+ mVisibilityScope = visibilityScope;
+ mPackageName = Objects.requireNonNull(packageName);
+ }
+
+ /** Sets the database used by the session. */
+ @NonNull
+ public Builder setDatabase(@NonNull String database) {
+ mDatabase = Objects.requireNonNull(database);
+ return this;
+ }
+
+ /** Sets the status of the search. */
+ @NonNull
+ public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) {
+ mStatusCode = statusCode;
+ return this;
+ }
+
+ /** Sets total latency for the search. */
+ @NonNull
+ public Builder setTotalLatencyMillis(int totalLatencyMillis) {
+ mTotalLatencyMillis = totalLatencyMillis;
+ return this;
+ }
+
+ /** Sets time used to rewrite the search spec. */
+ @NonNull
+ public Builder setRewriteSearchSpecLatencyMillis(int rewriteSearchSpecLatencyMillis) {
+ mRewriteSearchSpecLatencyMillis = rewriteSearchSpecLatencyMillis;
+ return this;
+ }
+
+ /** Sets time used to rewrite the search results. */
+ @NonNull
+ public Builder setRewriteSearchResultLatencyMillis(int rewriteSearchResultLatencyMillis) {
+ mRewriteSearchResultLatencyMillis = rewriteSearchResultLatencyMillis;
+ return this;
+ }
+
+ /** Sets overall time used for the native function calls. */
+ @NonNull
+ public Builder setNativeLatencyMillis(int nativeLatencyMillis) {
+ mNativeLatencyMillis = nativeLatencyMillis;
+ return this;
+ }
+
+ /** Sets number of terms in the search string. */
+ @NonNull
+ public Builder setTermCount(int termCount) {
+ mNativeNumTerms = termCount;
+ return this;
+ }
+
+ /** Sets length of the search string. */
+ @NonNull
+ public Builder setQueryLength(int queryLength) {
+ mNativeQueryLength = queryLength;
+ return this;
+ }
+
+ /** Sets number of namespaces filtered. */
+ @NonNull
+ public Builder setFilteredNamespaceCount(int filteredNamespaceCount) {
+ mNativeNumNamespacesFiltered = filteredNamespaceCount;
+ return this;
+ }
+
+ /** Sets number of schema types filtered. */
+ @NonNull
+ public Builder setFilteredSchemaTypeCount(int filteredSchemaTypeCount) {
+ mNativeNumSchemaTypesFiltered = filteredSchemaTypeCount;
+ return this;
+ }
+
+ /** Sets the requested number of results in one page. */
+ @NonNull
+ public Builder setRequestedPageSize(int requestedPageSize) {
+ mNativeRequestedPageSize = requestedPageSize;
+ return this;
+ }
+
+ /** Sets the actual number of results returned in the current page. */
+ @NonNull
+ public Builder setCurrentPageReturnedResultCount(int currentPageReturnedResultCount) {
+ mNativeNumResultsReturnedCurrentPage = currentPageReturnedResultCount;
+ return this;
+ }
+
+ /**
+ * Sets whether the function call is querying the first page. If it's not, Icing will fetch
+ * the results from cache so that some steps may be skipped.
+ */
+ @NonNull
+ public Builder setIsFirstPage(boolean nativeIsFirstPage) {
+ mNativeIsFirstPage = nativeIsFirstPage;
+ return this;
+ }
+
+ /**
+ * Sets time used to parse the query, including 2 parts: tokenizing and transforming tokens
+ * into an iterator tree.
+ */
+ @NonNull
+ public Builder setParseQueryLatencyMillis(int parseQueryLatencyMillis) {
+ mNativeParseQueryLatencyMillis = parseQueryLatencyMillis;
+ return this;
+ }
+
+ /** Sets strategy of scoring and ranking. */
+ @NonNull
+ public Builder setRankingStrategy(@SearchSpec.RankingStrategy int rankingStrategy) {
+ mNativeRankingStrategy = rankingStrategy;
+ return this;
+ }
+
+ /** Sets number of documents scored. */
+ @NonNull
+ public Builder setScoredDocumentCount(int scoredDocumentCount) {
+ mNativeNumDocumentsScored = scoredDocumentCount;
+ return this;
+ }
+
+ /** Sets time used to score the raw results. */
+ @NonNull
+ public Builder setScoringLatencyMillis(int scoringLatencyMillis) {
+ mNativeScoringLatencyMillis = scoringLatencyMillis;
+ return this;
+ }
+
+ /** Sets time used to rank the scored results. */
+ @NonNull
+ public Builder setRankingLatencyMillis(int rankingLatencyMillis) {
+ mNativeRankingLatencyMillis = rankingLatencyMillis;
+ return this;
+ }
+
+ /** Sets time used to fetch the document protos. */
+ @NonNull
+ public Builder setDocumentRetrievingLatencyMillis(int documentRetrievingLatencyMillis) {
+ mNativeDocumentRetrievingLatencyMillis = documentRetrievingLatencyMillis;
+ return this;
+ }
+
+ /** Sets how many snippets are calculated. */
+ @NonNull
+ public Builder setResultWithSnippetsCount(int resultWithSnippetsCount) {
+ mNativeNumResultsWithSnippets = resultWithSnippetsCount;
+ return this;
+ }
+
+ /**
+ * Constructs a new {@link SearchStats} from the contents of this {@link
+ * SearchStats.Builder}.
+ */
+ @NonNull
+ public SearchStats build() {
+ if (mDatabase == null) {
+ Preconditions.checkState(
+ mVisibilityScope != SearchStats.VISIBILITY_SCOPE_LOCAL,
+ "database can not be null if visibilityScope is local.");
+ }
+
+ return new SearchStats(/* builder= */ this);
+ }
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
index 731ab35..88f238e 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
@@ -31,7 +31,9 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.appsearch.external.localstorage.AppSearchLogger;
import com.android.server.appsearch.external.localstorage.stats.CallStats;
+import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
+import com.android.server.appsearch.external.localstorage.stats.SearchStats;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
@@ -189,6 +191,16 @@
}
}
+ @Override
+ public void logStats(@NonNull InitializeStats stats) throws AppSearchException {
+ // TODO(b/173532925): Implement
+ }
+
+ @Override
+ public void logStats(@NonNull SearchStats stats) throws AppSearchException {
+ // TODO(b/173532925): Implement
+ }
+
/**
* Removes cached UID for package.
*
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index ce0ebe6..10bba13 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-I06df2c636d26419e653c5d8c9e7d15449da6816e
+I19dac52031c47099f621eced9f148931f1021f25
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 5729385..17682a5 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -1513,6 +1513,11 @@
* available quota (and the job will not be successfully scheduled).
*
* <p>
+ * Expedited job quota will replenish over time and as the user interacts with the app,
+ * so you should not have to worry about running out of quota because of processing from
+ * frequent user engagement.
+ *
+ * <p>
* Expedited jobs may only set network, storage-not-low, and persistence constraints.
* No other constraints are allowed.
*
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
index 48b62a6..32655c7 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java
@@ -45,25 +45,25 @@
/** @hide */
public static final int INTERNAL_STOP_REASON_CANCELED =
- JobProtoEnums.STOP_REASON_CANCELLED; // 0.
+ JobProtoEnums.INTERNAL_STOP_REASON_CANCELLED; // 0.
/** @hide */
public static final int INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED =
- JobProtoEnums.STOP_REASON_CONSTRAINTS_NOT_SATISFIED; // 1.
+ JobProtoEnums.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED; // 1.
/** @hide */
public static final int INTERNAL_STOP_REASON_PREEMPT =
- JobProtoEnums.STOP_REASON_PREEMPT; // 2.
+ JobProtoEnums.INTERNAL_STOP_REASON_PREEMPT; // 2.
/**
* The job ran for at least its minimum execution limit.
* @hide
*/
public static final int INTERNAL_STOP_REASON_TIMEOUT =
- JobProtoEnums.STOP_REASON_TIMEOUT; // 3.
+ JobProtoEnums.INTERNAL_STOP_REASON_TIMEOUT; // 3.
/** @hide */
public static final int INTERNAL_STOP_REASON_DEVICE_IDLE =
- JobProtoEnums.STOP_REASON_DEVICE_IDLE; // 4.
+ JobProtoEnums.INTERNAL_STOP_REASON_DEVICE_IDLE; // 4.
/** @hide */
public static final int INTERNAL_STOP_REASON_DEVICE_THERMAL =
- JobProtoEnums.STOP_REASON_DEVICE_THERMAL; // 5.
+ JobProtoEnums.INTERNAL_STOP_REASON_DEVICE_THERMAL; // 5.
/**
* The job is in the {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
* bucket.
@@ -71,26 +71,30 @@
* @hide
*/
public static final int INTERNAL_STOP_REASON_RESTRICTED_BUCKET =
- JobProtoEnums.STOP_REASON_RESTRICTED_BUCKET; // 6.
+ JobProtoEnums.INTERNAL_STOP_REASON_RESTRICTED_BUCKET; // 6.
/**
* The app was uninstalled.
* @hide
*/
- public static final int INTERNAL_STOP_REASON_UNINSTALL = 7;
+ public static final int INTERNAL_STOP_REASON_UNINSTALL =
+ JobProtoEnums.INTERNAL_STOP_REASON_UNINSTALL; // 7.
/**
* The app's data was cleared.
* @hide
*/
- public static final int INTERNAL_STOP_REASON_DATA_CLEARED = 8;
+ public static final int INTERNAL_STOP_REASON_DATA_CLEARED =
+ JobProtoEnums.INTERNAL_STOP_REASON_DATA_CLEARED; // 8.
/**
* @hide
*/
- public static final int INTERNAL_STOP_REASON_RTC_UPDATED = 9;
+ public static final int INTERNAL_STOP_REASON_RTC_UPDATED =
+ JobProtoEnums.INTERNAL_STOP_REASON_RTC_UPDATED; // 9.
/**
* The app called jobFinished() on its own.
* @hide
*/
- public static final int INTERNAL_STOP_REASON_SUCCESSFUL_FINISH = 10;
+ public static final int INTERNAL_STOP_REASON_SUCCESSFUL_FINISH =
+ JobProtoEnums.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH; // 10.
/**
* All the stop reason codes. This should be regarded as an immutable array at runtime.
@@ -109,6 +113,10 @@
INTERNAL_STOP_REASON_DEVICE_IDLE,
INTERNAL_STOP_REASON_DEVICE_THERMAL,
INTERNAL_STOP_REASON_RESTRICTED_BUCKET,
+ INTERNAL_STOP_REASON_UNINSTALL,
+ INTERNAL_STOP_REASON_DATA_CLEARED,
+ INTERNAL_STOP_REASON_RTC_UPDATED,
+ INTERNAL_STOP_REASON_SUCCESSFUL_FINISH,
};
/**
@@ -363,14 +371,6 @@
}
/**
- * @deprecated Use {@link #isExpeditedJob()} instead.
- */
- @Deprecated
- public boolean isForegroundJob() {
- return mIsExpedited;
- }
-
- /**
* For jobs with {@link android.app.job.JobInfo.Builder#setOverrideDeadline(long)} set, this
* provides an easy way to tell whether the job is being executed due to the deadline
* expiring. Note: If the job is running because its deadline expired, it implies that its
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 aafeb48..54e47cf 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -43,6 +43,11 @@
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_NOT_APPLICABLE;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_PERMISSION;
import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_ALARM_CANCELLED;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_DATA_CLEARED;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_EXACT_PERMISSION_REVOKED;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_PI_CANCELLED;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_UNDEFINED;
import android.Manifest;
import android.annotation.NonNull;
@@ -116,6 +121,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.LocalLog;
+import com.android.internal.util.RingBuffer;
import com.android.internal.util.StatLogger;
import com.android.server.AlarmManagerInternal;
import com.android.server.AppStateTracker;
@@ -160,6 +166,7 @@
public class AlarmManagerService extends SystemService {
private static final int RTC_WAKEUP_MASK = 1 << RTC_WAKEUP;
private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << ELAPSED_REALTIME_WAKEUP;
+ private static final int REMOVAL_HISTORY_SIZE_PER_UID = 10;
static final int TIME_CHANGED_MASK = 1 << 16;
static final int IS_WAKEUP_MASK = RTC_WAKEUP_MASK | ELAPSED_REALTIME_WAKEUP_MASK;
@@ -238,6 +245,7 @@
AppWakeupHistory mAppWakeupHistory;
AppWakeupHistory mAllowWhileIdleHistory;
private final SparseLongArray mLastPriorityAlarmDispatch = new SparseLongArray();
+ private final SparseArray<RingBuffer<RemovedAlarm>> mRemovalHistory = new SparseArray<>();
ClockReceiver mClockReceiver;
final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
IBinder.DeathRecipient mListenerDeathRecipient;
@@ -392,6 +400,57 @@
}
}
+ static class RemovedAlarm {
+ static final int REMOVE_REASON_UNDEFINED = 0;
+ static final int REMOVE_REASON_ALARM_CANCELLED = 1;
+ static final int REMOVE_REASON_EXACT_PERMISSION_REVOKED = 2;
+ static final int REMOVE_REASON_DATA_CLEARED = 3;
+ static final int REMOVE_REASON_PI_CANCELLED = 4;
+
+ final String mTag;
+ final long mWhenRemovedElapsed;
+ final long mWhenRemovedRtc;
+ final int mRemoveReason;
+
+ RemovedAlarm(Alarm a, int removeReason, long nowRtc, long nowElapsed) {
+ mTag = a.statsTag;
+ mRemoveReason = removeReason;
+ mWhenRemovedRtc = nowRtc;
+ mWhenRemovedElapsed = nowElapsed;
+ }
+
+ static final boolean isLoggable(int reason) {
+ // We don't want to log meaningless reasons. This also gives a way for callers to
+ // opt out of logging, e.g. when replacing an alarm.
+ return reason != REMOVE_REASON_UNDEFINED;
+ }
+
+ static final String removeReasonToString(int reason) {
+ switch (reason) {
+ case REMOVE_REASON_ALARM_CANCELLED:
+ return "alarm_cancelled";
+ case REMOVE_REASON_EXACT_PERMISSION_REVOKED:
+ return "exact_alarm_permission_revoked";
+ case REMOVE_REASON_DATA_CLEARED:
+ return "data_cleared";
+ case REMOVE_REASON_PI_CANCELLED:
+ return "pi_cancelled";
+ default:
+ return "unknown:" + reason;
+ }
+ }
+
+ void dump(IndentingPrintWriter pw, long nowElapsed, SimpleDateFormat sdf) {
+ pw.print("[tag", mTag);
+ pw.print("reason", removeReasonToString(mRemoveReason));
+ pw.print("elapsed=");
+ TimeUtils.formatDuration(mWhenRemovedElapsed, nowElapsed, pw);
+ pw.print(" rtc=");
+ pw.print(sdf.format(new Date(mWhenRemovedRtc)));
+ pw.println("]");
+ }
+ }
+
/**
* All times are in milliseconds. These constants are kept synchronized with the system
* global Settings. Any access to this class or its fields should be done while
@@ -1691,7 +1750,7 @@
void removeImpl(PendingIntent operation, IAlarmListener listener) {
synchronized (mLock) {
- removeLocked(operation, listener);
+ removeLocked(operation, listener, REMOVE_REASON_UNDEFINED);
}
}
@@ -1812,7 +1871,7 @@
+ " -- package not allowed to start");
return;
}
- removeLocked(operation, directReceiver);
+ removeLocked(operation, directReceiver, REMOVE_REASON_UNDEFINED);
incrementAlarmCount(a.uid);
setImplLocked(a);
MetricsHelper.pushAlarmScheduled(a);
@@ -2106,7 +2165,7 @@
@Override
public void removeAlarmsForUid(int uid) {
synchronized (mLock) {
- removeLocked(uid);
+ removeLocked(uid, REMOVE_REASON_DATA_CLEARED);
}
}
@@ -2342,7 +2401,7 @@
return;
}
synchronized (mLock) {
- removeLocked(operation, listener);
+ removeLocked(operation, listener, REMOVE_REASON_ALARM_CANCELLED);
}
}
@@ -2688,6 +2747,7 @@
pw.println("Allow while idle history:");
mAllowWhileIdleHistory.dump(pw, nowELAPSED);
+ pw.println();
if (mLastPriorityAlarmDispatch.size() > 0) {
pw.println("Last priority alarm dispatches:");
@@ -2702,6 +2762,23 @@
pw.decreaseIndent();
}
+ if (mRemovalHistory.size() > 0) {
+ pw.println("Removal history: ");
+ pw.increaseIndent();
+ for (int i = 0; i < mRemovalHistory.size(); i++) {
+ UserHandle.formatUid(pw, mRemovalHistory.keyAt(i));
+ pw.println(":");
+ pw.increaseIndent();
+ final RemovedAlarm[] historyForUid = mRemovalHistory.valueAt(i).toArray();
+ for (final RemovedAlarm removedAlarm : historyForUid) {
+ removedAlarm.dump(pw, nowELAPSED, sdf);
+ }
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+ pw.println();
+ }
+
if (mLog.dump(pw, "Recent problems:")) {
pw.println();
}
@@ -3239,7 +3316,7 @@
}
return !hasScheduleExactAlarmInternal(a.packageName, a.uid);
};
- removeAlarmsInternalLocked(whichAlarms);
+ removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED);
}
/**
@@ -3247,7 +3324,6 @@
* that the app is no longer eligible to use.
*
* This is not expected to get called frequently.
- * TODO (b/179541791): Add revocation history to dumpsys.
*/
void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) {
Slog.w(TAG, "Package " + packageName + ", uid " + uid + " lost SCHEDULE_EXACT_ALARM!");
@@ -3263,26 +3339,22 @@
}
return false;
};
- removeAlarmsInternalLocked(whichAlarms);
+ removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_EXACT_PERMISSION_REVOKED);
}
- private void removeAlarmsInternalLocked(Predicate<Alarm> whichAlarms) {
+ private void removeAlarmsInternalLocked(Predicate<Alarm> whichAlarms, int reason) {
+ final long nowRtc = mInjector.getCurrentTimeMillis();
+ final long nowElapsed = mInjector.getElapsedRealtime();
+
final ArrayList<Alarm> removedAlarms = mAlarmStore.remove(whichAlarms);
- final boolean didRemove = !removedAlarms.isEmpty();
- if (didRemove) {
- for (final Alarm removed : removedAlarms) {
- decrementAlarmCount(removed.uid, 1);
- }
- }
+ final boolean removedFromStore = !removedAlarms.isEmpty();
for (int i = mPendingBackgroundAlarms.size() - 1; i >= 0; i--) {
final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.valueAt(i);
for (int j = alarmsForUid.size() - 1; j >= 0; j--) {
final Alarm alarm = alarmsForUid.get(j);
if (whichAlarms.test(alarm)) {
- // Don't set didRemove, since this doesn't impact the scheduled alarms.
- alarmsForUid.remove(j);
- decrementAlarmCount(alarm.uid, 1);
+ removedAlarms.add(alarmsForUid.remove(j));
}
}
if (alarmsForUid.size() == 0) {
@@ -3292,13 +3364,24 @@
for (int i = mPendingNonWakeupAlarms.size() - 1; i >= 0; i--) {
final Alarm a = mPendingNonWakeupAlarms.get(i);
if (whichAlarms.test(a)) {
- // Don't set didRemove, since this doesn't impact the scheduled alarms.
- mPendingNonWakeupAlarms.remove(i);
- decrementAlarmCount(a.uid, 1);
+ removedAlarms.add(mPendingNonWakeupAlarms.remove(i));
}
}
- if (didRemove) {
+ for (final Alarm removed : removedAlarms) {
+ decrementAlarmCount(removed.uid, 1);
+ if (!RemovedAlarm.isLoggable(reason)) {
+ continue;
+ }
+ RingBuffer<RemovedAlarm> bufferForUid = mRemovalHistory.get(removed.uid);
+ if (bufferForUid == null) {
+ bufferForUid = new RingBuffer<>(RemovedAlarm.class, REMOVAL_HISTORY_SIZE_PER_UID);
+ mRemovalHistory.put(removed.uid, bufferForUid);
+ }
+ bufferForUid.append(new RemovedAlarm(removed, reason, nowRtc, nowElapsed));
+ }
+
+ if (removedFromStore) {
boolean idleUntilUpdated = false;
if (mPendingIdleUntil != null && whichAlarms.test(mPendingIdleUntil)) {
mPendingIdleUntil = null;
@@ -3320,7 +3403,7 @@
}
}
- void removeLocked(PendingIntent operation, IAlarmListener directReceiver) {
+ void removeLocked(PendingIntent operation, IAlarmListener directReceiver, int reason) {
if (operation == null && directReceiver == null) {
if (localLOGV) {
Slog.w(TAG, "requested remove() of null operation",
@@ -3328,15 +3411,15 @@
}
return;
}
- removeAlarmsInternalLocked(a -> a.matches(operation, directReceiver));
+ removeAlarmsInternalLocked(a -> a.matches(operation, directReceiver), reason);
}
- void removeLocked(final int uid) {
+ void removeLocked(final int uid, int reason) {
if (uid == Process.SYSTEM_UID) {
// If a force-stop occurs for a system-uid package, ignore it.
return;
}
- removeAlarmsInternalLocked(a -> a.uid == uid);
+ removeAlarmsInternalLocked(a -> a.uid == uid, reason);
}
void removeLocked(final String packageName) {
@@ -3347,7 +3430,7 @@
}
return;
}
- removeAlarmsInternalLocked(a -> a.matches(packageName));
+ removeAlarmsInternalLocked(a -> a.matches(packageName), REMOVE_REASON_UNDEFINED);
}
// Only called for ephemeral apps
@@ -3358,7 +3441,7 @@
}
final Predicate<Alarm> whichAlarms = (a) -> (a.uid == uid
&& mActivityManagerInternal.isAppStartModeDisabled(uid, a.packageName));
- removeAlarmsInternalLocked(whichAlarms);
+ removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_UNDEFINED);
}
void removeUserLocked(int userHandle) {
@@ -3368,13 +3451,18 @@
}
final Predicate<Alarm> whichAlarms =
(Alarm a) -> UserHandle.getUserId(a.uid) == userHandle;
- removeAlarmsInternalLocked(whichAlarms);
+ removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_UNDEFINED);
for (int i = mLastPriorityAlarmDispatch.size() - 1; i >= 0; i--) {
if (UserHandle.getUserId(mLastPriorityAlarmDispatch.keyAt(i)) == userHandle) {
mLastPriorityAlarmDispatch.removeAt(i);
}
}
+ for (int i = mRemovalHistory.size() - 1; i >= 0; i--) {
+ if (UserHandle.getUserId(mRemovalHistory.keyAt(i)) == userHandle) {
+ mRemovalHistory.removeAt(i);
+ }
+ }
}
void interactiveStateChangedLocked(boolean interactive) {
@@ -4018,7 +4106,7 @@
case REMOVE_FOR_CANCELED:
final PendingIntent operation = (PendingIntent) msg.obj;
synchronized (mLock) {
- removeLocked(operation, null);
+ removeLocked(operation, null, REMOVE_REASON_PI_CANCELLED);
}
break;
@@ -4198,6 +4286,7 @@
return;
case Intent.ACTION_UID_REMOVED:
mLastPriorityAlarmDispatch.delete(uid);
+ mRemovalHistory.delete(uid);
return;
case Intent.ACTION_PACKAGE_REMOVED:
if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
@@ -4227,7 +4316,7 @@
// package-removed and package-restarted case
mAppWakeupHistory.removeForPackage(pkg, UserHandle.getUserId(uid));
mAllowWhileIdleHistory.removeForPackage(pkg, UserHandle.getUserId(uid));
- removeLocked(uid);
+ removeLocked(uid, REMOVE_REASON_UNDEFINED);
} else {
// external-applications-unavailable case
removeLocked(pkg);
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 313cd20..452be30 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1088,7 +1088,7 @@
FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
uId, null, jobStatus.getBatteryName(),
FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
- JobProtoEnums.STOP_REASON_UNKNOWN, jobStatus.getStandbyBucket(),
+ JobProtoEnums.INTERNAL_STOP_REASON_UNKNOWN, jobStatus.getStandbyBucket(),
jobStatus.getJobId(),
jobStatus.hasChargingConstraint(),
jobStatus.hasBatteryNotLowConstraint(),
@@ -1099,7 +1099,8 @@
jobStatus.hasConnectivityConstraint(),
jobStatus.hasContentTriggerConstraint(),
jobStatus.isRequestedExpeditedJob(),
- /* isRunningAsExpeditedJob */ false);
+ /* isRunningAsExpeditedJob */ false,
+ JobProtoEnums.STOP_REASON_UNDEFINED);
// If the job is immediately ready to run, then we can just immediately
// put it in the pending list and try to schedule it. This is especially
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 6ab2be3..3fa1c12 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -314,7 +314,8 @@
FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
job.getSourceUid(), null, job.getBatteryName(),
FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED,
- JobProtoEnums.STOP_REASON_UNKNOWN, job.getStandbyBucket(), job.getJobId(),
+ JobProtoEnums.INTERNAL_STOP_REASON_UNKNOWN,
+ job.getStandbyBucket(), job.getJobId(),
job.hasChargingConstraint(),
job.hasBatteryNotLowConstraint(),
job.hasStorageNotLowConstraint(),
@@ -324,7 +325,8 @@
job.hasConnectivityConstraint(),
job.hasContentTriggerConstraint(),
job.isRequestedExpeditedJob(),
- job.shouldTreatAsExpeditedJob());
+ job.shouldTreatAsExpeditedJob(),
+ JobProtoEnums.STOP_REASON_UNDEFINED);
try {
mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
} catch (RemoteException e) {
@@ -923,7 +925,8 @@
completedJob.hasConnectivityConstraint(),
completedJob.hasContentTriggerConstraint(),
completedJob.isRequestedExpeditedJob(),
- completedJob.startedAsExpeditedJob);
+ completedJob.startedAsExpeditedJob,
+ mParams.getStopReason());
try {
mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(),
internalStopReason);
diff --git a/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl b/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl
index 3d5321c..e347ebf 100644
--- a/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl
+++ b/apex/media/aidl/private/android/media/IMediaCommunicationServiceCallback.aidl
@@ -19,7 +19,7 @@
import android.media.MediaParceledListSlice;
/** {@hide} */
-interface IMediaCommunicationServiceCallback {
+oneway interface IMediaCommunicationServiceCallback {
void onSession2Created(in Session2Token token);
void onSession2Changed(in MediaParceledListSlice tokens);
}
diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
index 06de3f8..ed31aa3 100644
--- a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
+++ b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
@@ -65,17 +65,17 @@
final Context mContext;
- private final Object mLock = new Object();
- private final Handler mHandler = new Handler(Looper.getMainLooper());
+ final Object mLock = new Object();
+ final Handler mHandler = new Handler(Looper.getMainLooper());
@GuardedBy("mLock")
private final SparseIntArray mFullUserIds = new SparseIntArray();
@GuardedBy("mLock")
private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<>();
- private final Executor mRecordExecutor = Executors.newSingleThreadExecutor();
+ final Executor mRecordExecutor = Executors.newSingleThreadExecutor();
@GuardedBy("mLock")
- private final List<CallbackRecord> mCallbackRecords = new ArrayList<>();
+ final List<CallbackRecord> mCallbackRecords = new ArrayList<>();
final NotificationManager mNotificationManager;
public MediaCommunicationService(Context context) {
@@ -111,14 +111,14 @@
FullUserRecord user = getFullUserRecordLocked(userId);
if (user != null) {
if (user.getFullUserId() == userId) {
- user.destroySessionsForUserLocked(UserHandle.ALL.getIdentifier());
+ user.destroyAllSessions();
mUserRecords.remove(userId);
} else {
- user.destroySessionsForUserLocked(userId);
+ user.destroySessionsForUser(userId);
}
}
- updateUser();
}
+ updateUser();
}
@Nullable
@@ -134,6 +134,22 @@
return null;
}
+ List<Session2Token> getSession2TokensLocked(int userId) {
+ List<Session2Token> list = new ArrayList<>();
+ if (userId == ALL.getIdentifier()) {
+ int size = mUserRecords.size();
+ for (int i = 0; i < size; i++) {
+ list.addAll(mUserRecords.valueAt(i).getAllSession2Tokens());
+ }
+ } else {
+ FullUserRecord user = getFullUserRecordLocked(userId);
+ if (user != null) {
+ list.addAll(user.getSession2Tokens(userId));
+ }
+ }
+ return list;
+ }
+
private FullUserRecord getFullUserRecordLocked(int userId) {
int fullUserId = mFullUserIds.get(userId, -1);
if (fullUserId < 0) {
@@ -188,27 +204,54 @@
}
}
- void dispatchSessionCreated(Session2Token token) {
- for (CallbackRecord record : mCallbackRecords) {
- if (record.mUserId != ALL.getIdentifier()
- && record.mUserId != getUserHandleForUid(token.getUid()).getIdentifier()) {
- continue;
- }
- try {
- record.mCallback.onSession2Created(token);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- }
-
- void onSessionDied(Session2Record record) {
+ void dispatchSession2Created(Session2Token token) {
synchronized (mLock) {
- destroySessionLocked(record);
+ for (CallbackRecord record : mCallbackRecords) {
+ if (record.mUserId != ALL.getIdentifier()
+ && record.mUserId != getUserHandleForUid(token.getUid()).getIdentifier()) {
+ continue;
+ }
+ try {
+ record.mCallback.onSession2Created(token);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to notify session2 token created " + record);
+ }
+ }
}
}
- private void destroySessionLocked(Session2Record session) {
+ void dispatchSession2Changed(int userId) {
+ MediaParceledListSlice<Session2Token> allSession2Tokens;
+ MediaParceledListSlice<Session2Token> userSession2Tokens;
+
+ synchronized (mLock) {
+ allSession2Tokens =
+ new MediaParceledListSlice<>(getSession2TokensLocked(ALL.getIdentifier()));
+ userSession2Tokens = new MediaParceledListSlice<>(getSession2TokensLocked(userId));
+ }
+ allSession2Tokens.setInlineCountLimit(1);
+ userSession2Tokens.setInlineCountLimit(1);
+
+ synchronized (mLock) {
+ for (CallbackRecord record : mCallbackRecords) {
+ if (record.mUserId == ALL.getIdentifier()) {
+ try {
+ record.mCallback.onSession2Changed(allSession2Tokens);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to notify session2 tokens changed " + record);
+ }
+ } else if (record.mUserId == userId) {
+ try {
+ record.mCallback.onSession2Changed(userSession2Tokens);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to notify session2 tokens changed " + record);
+ }
+ }
+ }
+ }
+ }
+
+ void onSessionDied(Session2Record session) {
if (DEBUG) {
Log.d(TAG, "Destroying " + session);
}
@@ -217,12 +260,10 @@
return;
}
- FullUserRecord user = getFullUserRecordLocked(session.getUserId());
-
+ FullUserRecord user = session.getFullUser();
if (user != null) {
user.removeSession(session);
}
-
session.close();
}
@@ -241,17 +282,17 @@
throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
+ " but actually=" + sessionToken.getUid());
}
+ FullUserRecord user;
+ int userId = getUserHandleForUid(sessionToken.getUid()).getIdentifier();
synchronized (mLock) {
- int userId = getUserHandleForUid(sessionToken.getUid()).getIdentifier();
- FullUserRecord user = getFullUserRecordLocked(userId);
- if (user == null) {
- Log.w(TAG, "notifySession2Created: Ignore session of an unknown user");
- return;
- }
- user.addSession(new Session2Record(MediaCommunicationService.this,
- sessionToken, mRecordExecutor));
- mHandler.post(() -> dispatchSessionCreated(sessionToken));
+ user = getFullUserRecordLocked(userId);
}
+ if (user == null) {
+ Log.w(TAG, "notifySession2Created: Ignore session of an unknown user");
+ return;
+ }
+ user.addSession(new Session2Record(MediaCommunicationService.this,
+ user, sessionToken, mRecordExecutor));
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -299,10 +340,11 @@
int resolvedUserId = handleIncomingUser(pid, uid, userId, null);
List<Session2Token> result;
synchronized (mLock) {
- FullUserRecord user = getFullUserRecordLocked(userId);
- result = user.getSession2Tokens(resolvedUserId);
+ result = getSession2TokensLocked(resolvedUserId);
}
- return new MediaParceledListSlice(result);
+ MediaParceledListSlice parceledListSlice = new MediaParceledListSlice<>(result);
+ parceledListSlice.setInlineCountLimit(1);
+ return parceledListSlice;
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -427,7 +469,8 @@
final class FullUserRecord {
private final int mFullUserId;
- /** Sorted list of media sessions */
+ private final Object mUserLock = new Object();
+ @GuardedBy("mUserLock")
private final List<Session2Record> mSessionRecords = new ArrayList<>();
FullUserRecord(int fullUserId) {
@@ -435,11 +478,18 @@
}
public void addSession(Session2Record record) {
- mSessionRecords.add(record);
+ synchronized (mUserLock) {
+ mSessionRecords.add(record);
+ }
+ mHandler.post(() -> dispatchSession2Created(record.mSessionToken));
+ mHandler.post(() -> dispatchSession2Changed(mFullUserId));
}
- public void removeSession(Session2Record record) {
- mSessionRecords.remove(record);
+ private void removeSession(Session2Record record) {
+ synchronized (mUserLock) {
+ mSessionRecords.remove(record);
+ }
+ mHandler.post(() -> dispatchSession2Changed(mFullUserId));
//TODO: Handle if the removed session was the media button session.
}
@@ -447,42 +497,68 @@
return mFullUserId;
}
- public List<Session2Token> getSession2Tokens(int userId) {
- return mSessionRecords.stream()
- .filter(record -> record.isActive()
- && (userId == UserHandle.ALL.getIdentifier()
- || record.getUserId() == userId))
- .map(Session2Record::getSessionToken)
- .collect(Collectors.toList());
+ public List<Session2Token> getAllSession2Tokens() {
+ synchronized (mUserLock) {
+ return mSessionRecords.stream()
+ .map(Session2Record::getSessionToken)
+ .collect(Collectors.toList());
+ }
}
- public void destroySessionsForUserLocked(int userId) {
- synchronized (mLock) {
- for (Session2Record record : mSessionRecords) {
- if (userId == UserHandle.ALL.getIdentifier()
- || record.getUserId() == userId) {
- destroySessionLocked(record);
+ public List<Session2Token> getSession2Tokens(int userId) {
+ synchronized (mUserLock) {
+ return mSessionRecords.stream()
+ .filter(record -> record.getUserId() == userId)
+ .map(Session2Record::getSessionToken)
+ .collect(Collectors.toList());
+ }
+ }
+
+ public void destroyAllSessions() {
+ synchronized (mUserLock) {
+ for (Session2Record session : mSessionRecords) {
+ session.close();
+ }
+ mSessionRecords.clear();
+ }
+ mHandler.post(() -> dispatchSession2Changed(mFullUserId));
+ }
+
+ public void destroySessionsForUser(int userId) {
+ boolean changed = false;
+ synchronized (mUserLock) {
+ for (int i = mSessionRecords.size() - 1; i >= 0; i--) {
+ Session2Record session = mSessionRecords.get(i);
+ if (session.getUserId() == userId) {
+ mSessionRecords.remove(i);
+ session.close();
+ changed = true;
}
}
}
+ if (changed) {
+ mHandler.post(() -> dispatchSession2Changed(mFullUserId));
+ }
}
}
static final class Session2Record {
- private final Session2Token mSessionToken;
- private final Object mLock = new Object();
- private final WeakReference<MediaCommunicationService> mServiceRef;
- @GuardedBy("mLock")
+ final Session2Token mSessionToken;
+ final Object mSession2RecordLock = new Object();
+ final WeakReference<MediaCommunicationService> mServiceRef;
+ final WeakReference<FullUserRecord> mFullUserRef;
+ @GuardedBy("mSession2RecordLock")
private final MediaController2 mController;
- @GuardedBy("mLock")
- private boolean mIsConnected;
- @GuardedBy("mLock")
+ @GuardedBy("mSession2RecordLock")
+ boolean mIsConnected;
+ @GuardedBy("mSession2RecordLock")
private boolean mIsClosed;
- Session2Record(MediaCommunicationService service, Session2Token token,
- Executor controllerExecutor) {
+ Session2Record(MediaCommunicationService service, FullUserRecord fullUser,
+ Session2Token token, Executor controllerExecutor) {
mServiceRef = new WeakReference<>(service);
+ mFullUserRef = new WeakReference<>(fullUser);
mSessionToken = token;
mController = new MediaController2.Builder(service.getContext(), token)
.setControllerCallback(controllerExecutor, new Controller2Callback())
@@ -493,23 +569,19 @@
return UserHandle.getUserHandleForUid(mSessionToken.getUid()).getIdentifier();
}
- public boolean isActive() {
- synchronized (mLock) {
- return mIsConnected;
- }
+ public FullUserRecord getFullUser() {
+ return mFullUserRef.get();
}
public boolean isClosed() {
- synchronized (mLock) {
+ synchronized (mSession2RecordLock) {
return mIsClosed;
}
}
public void close() {
- synchronized (mLock) {
+ synchronized (mSession2RecordLock) {
mIsClosed = true;
- // Call close regardless of the mIsConnected. This may be called when it's not yet
- // connected.
mController.close();
}
}
@@ -525,13 +597,9 @@
if (DEBUG) {
Log.d(TAG, "connected to " + mSessionToken + ", allowed=" + allowedCommands);
}
- synchronized (mLock) {
+ synchronized (mSession2RecordLock) {
mIsConnected = true;
}
- MediaCommunicationService service = mServiceRef.get();
- if (service != null) {
- //TODO: notify session state changed
- }
}
@Override
@@ -539,7 +607,7 @@
if (DEBUG) {
Log.d(TAG, "disconnected from " + mSessionToken);
}
- synchronized (mLock) {
+ synchronized (mSession2RecordLock) {
mIsConnected = false;
}
MediaCommunicationService service = mServiceRef.get();
diff --git a/api/Android.bp b/api/Android.bp
index 6571270..5b73388 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -370,7 +370,7 @@
{
targets: ["sdk", "win_sdk"],
dir: "apistubs/android/system-server/api",
- dest: "merge-android.txt",
+ dest: "android.txt",
},
],
}
@@ -394,7 +394,7 @@
{
targets: ["sdk", "win_sdk"],
dir: "apistubs/android/system-server/api",
- dest: "merge-removed.txt",
+ dest: "removed.txt",
},
],
}
diff --git a/cmds/dpm/Android.bp b/cmds/dpm/Android.bp
index 665abcd..6819d09 100644
--- a/cmds/dpm/Android.bp
+++ b/cmds/dpm/Android.bp
@@ -18,8 +18,7 @@
],
}
-java_binary {
+sh_binary {
name: "dpm",
- wrapper: "dpm",
- srcs: ["**/*.java"],
+ src: "dpm",
}
diff --git a/cmds/dpm/dpm b/cmds/dpm/dpm
index e0efdc1..784db5b 100755
--- a/cmds/dpm/dpm
+++ b/cmds/dpm/dpm
@@ -1,7 +1,5 @@
#!/system/bin/sh
# Script to start "dpm" on the device
#
-base=/system
-export CLASSPATH=$base/framework/dpm.jar
-exec app_process $base/bin com.android.commands.dpm.Dpm "$@"
+cmd device_policy "$@"
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
deleted file mode 100644
index d0c2a24..0000000
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.commands.dpm;
-
-import android.app.ActivityManager;
-import android.app.IActivityManager;
-import android.app.admin.DevicePolicyManager;
-import android.app.admin.IDevicePolicyManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-
-import com.android.internal.os.BaseCommand;
-
-import java.io.PrintStream;
-
-public final class Dpm extends BaseCommand {
-
- /**
- * Command-line entry point.
- *
- * @param args The command-line arguments
- */
- public static void main(String[] args) {
- (new Dpm()).run(args);
- }
-
- private static final String COMMAND_SET_ACTIVE_ADMIN = "set-active-admin";
- private static final String COMMAND_SET_DEVICE_OWNER = "set-device-owner";
- private static final String COMMAND_SET_PROFILE_OWNER = "set-profile-owner";
- private static final String COMMAND_REMOVE_ACTIVE_ADMIN = "remove-active-admin";
- private static final String COMMAND_CLEAR_FREEZE_PERIOD_RECORD = "clear-freeze-period-record";
- private static final String COMMAND_FORCE_NETWORK_LOGS = "force-network-logs";
- private static final String COMMAND_FORCE_SECURITY_LOGS = "force-security-logs";
- private static final String COMMAND_MARK_PO_ON_ORG_OWNED_DEVICE =
- "mark-profile-owner-on-organization-owned-device";
-
- private IDevicePolicyManager mDevicePolicyManager;
- private int mUserId = UserHandle.USER_SYSTEM;
- private String mName = "";
- private ComponentName mComponent = null;
-
- @Override
- public void onShowUsage(PrintStream out) {
- out.println(
- "usage: dpm [subcommand] [options]\n" +
- "usage: dpm set-active-admin [ --user <USER_ID> | current ] <COMPONENT>\n" +
- // STOPSHIP Finalize it
- "usage: dpm set-device-owner [ --user <USER_ID> | current *EXPERIMENTAL* ] " +
- "[ --name <NAME> ] <COMPONENT>\n" +
- "usage: dpm set-profile-owner [ --user <USER_ID> | current ] [ --name <NAME> ] " +
- "<COMPONENT>\n" +
- "usage: dpm remove-active-admin [ --user <USER_ID> | current ] [ --name <NAME> ] " +
- "<COMPONENT>\n" +
- "\n" +
- "dpm set-active-admin: Sets the given component as active admin" +
- " for an existing user.\n" +
- "\n" +
- "dpm set-device-owner: Sets the given component as active admin, and its" +
- " package as device owner.\n" +
- "\n" +
- "dpm set-profile-owner: Sets the given component as active admin and profile" +
- " owner for an existing user.\n" +
- "\n" +
- "dpm remove-active-admin: Disables an active admin, the admin must have declared" +
- " android:testOnly in the application in its manifest. This will also remove" +
- " device and profile owners.\n" +
- "\n" +
- "dpm " + COMMAND_CLEAR_FREEZE_PERIOD_RECORD + ": clears framework-maintained " +
- "record of past freeze periods that the device went through. For use during " +
- "feature development to prevent triggering restriction on setting freeze " +
- "periods.\n" +
- "\n" +
- "dpm " + COMMAND_FORCE_NETWORK_LOGS + ": makes all network logs available to " +
- "the DPC and triggers DeviceAdminReceiver.onNetworkLogsAvailable() if needed.\n" +
- "\n" +
- "dpm " + COMMAND_FORCE_SECURITY_LOGS + ": makes all security logs available to " +
- "the DPC and triggers DeviceAdminReceiver.onSecurityLogsAvailable() if needed."
- + "\n"
- + "usage: dpm " + COMMAND_MARK_PO_ON_ORG_OWNED_DEVICE + ": "
- + "[ --user <USER_ID> | current ] <COMPONENT>\n");
- }
-
- @Override
- public void onRun() throws Exception {
- mDevicePolicyManager = IDevicePolicyManager.Stub.asInterface(
- ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
- if (mDevicePolicyManager == null) {
- showError("Error: Could not access the Device Policy Manager. Is the system running?");
- return;
- }
-
- String command = nextArgRequired();
- switch (command) {
- case COMMAND_SET_ACTIVE_ADMIN:
- runSetActiveAdmin();
- break;
- case COMMAND_SET_DEVICE_OWNER:
- runSetDeviceOwner();
- break;
- case COMMAND_SET_PROFILE_OWNER:
- runSetProfileOwner();
- break;
- case COMMAND_REMOVE_ACTIVE_ADMIN:
- runRemoveActiveAdmin();
- break;
- case COMMAND_CLEAR_FREEZE_PERIOD_RECORD:
- runClearFreezePeriodRecord();
- break;
- case COMMAND_FORCE_NETWORK_LOGS:
- runForceNetworkLogs();
- break;
- case COMMAND_FORCE_SECURITY_LOGS:
- runForceSecurityLogs();
- break;
- case COMMAND_MARK_PO_ON_ORG_OWNED_DEVICE:
- runMarkProfileOwnerOnOrganizationOwnedDevice();
- break;
- default:
- throw new IllegalArgumentException ("unknown command '" + command + "'");
- }
- }
-
- private void runForceNetworkLogs() throws RemoteException, InterruptedException {
- while (true) {
- final long toWait = mDevicePolicyManager.forceNetworkLogs();
- if (toWait == 0) {
- break;
- }
- System.out.println("We have to wait for " + toWait + " milliseconds...");
- Thread.sleep(toWait);
- }
- System.out.println("Success");
- }
-
- private void runForceSecurityLogs() throws RemoteException, InterruptedException {
- while (true) {
- final long toWait = mDevicePolicyManager.forceSecurityLogs();
- if (toWait == 0) {
- break;
- }
- System.out.println("We have to wait for " + toWait + " milliseconds...");
- Thread.sleep(toWait);
- }
- System.out.println("Success");
- }
-
- private void parseArgs(boolean canHaveName) {
- String opt;
- while ((opt = nextOption()) != null) {
- if ("--user".equals(opt)) {
- String arg = nextArgRequired();
- if ("current".equals(arg) || "cur".equals(arg)) {
- mUserId = UserHandle.USER_CURRENT;
- } else {
- mUserId = parseInt(arg);
- }
- if (mUserId == UserHandle.USER_CURRENT) {
- IActivityManager activityManager = ActivityManager.getService();
- try {
- mUserId = activityManager.getCurrentUser().id;
- } catch (RemoteException e) {
- e.rethrowAsRuntimeException();
- }
- }
- } else if (canHaveName && "--name".equals(opt)) {
- mName = nextArgRequired();
- } else {
- throw new IllegalArgumentException("Unknown option: " + opt);
- }
- }
- mComponent = parseComponentName(nextArgRequired());
- }
-
- private void runSetActiveAdmin() throws RemoteException {
- parseArgs(/*canHaveName=*/ false);
- mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
-
- System.out.println("Success: Active admin set to component " + mComponent.toShortString());
- }
-
- private void runSetDeviceOwner() throws RemoteException {
- parseArgs(/*canHaveName=*/ true);
- mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
-
- try {
- if (!mDevicePolicyManager.setDeviceOwner(mComponent, mName, mUserId)) {
- throw new RuntimeException(
- "Can't set package " + mComponent + " as device owner.");
- }
- } catch (Exception e) {
- // Need to remove the admin that we just added.
- mDevicePolicyManager.removeActiveAdmin(mComponent, UserHandle.USER_SYSTEM);
- throw e;
- }
-
- mDevicePolicyManager.setUserProvisioningState(
- DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId);
-
- System.out.println("Success: Device owner set to package " + mComponent);
- System.out.println("Active admin set to component " + mComponent.toShortString());
- }
-
- private void runRemoveActiveAdmin() throws RemoteException {
- parseArgs(/*canHaveName=*/ false);
- mDevicePolicyManager.forceRemoveActiveAdmin(mComponent, mUserId);
- System.out.println("Success: Admin removed " + mComponent);
- }
-
- private void runSetProfileOwner() throws RemoteException {
- parseArgs(/*canHaveName=*/ true);
- mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
-
- try {
- if (!mDevicePolicyManager.setProfileOwner(mComponent, mName, mUserId)) {
- throw new RuntimeException("Can't set component " + mComponent.toShortString() +
- " as profile owner for user " + mUserId);
- }
- } catch (Exception e) {
- // Need to remove the admin that we just added.
- mDevicePolicyManager.removeActiveAdmin(mComponent, mUserId);
- throw e;
- }
-
- mDevicePolicyManager.setUserProvisioningState(
- DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId);
-
- System.out.println("Success: Active admin and profile owner set to "
- + mComponent.toShortString() + " for user " + mUserId);
- }
-
- private void runClearFreezePeriodRecord() throws RemoteException {
- mDevicePolicyManager.clearSystemUpdatePolicyFreezePeriodRecord();
- System.out.println("Success");
- }
-
-
- private void runMarkProfileOwnerOnOrganizationOwnedDevice() throws RemoteException {
- parseArgs(/*canHaveName=*/ false);
- mDevicePolicyManager.markProfileOwnerOnOrganizationOwnedDevice(mComponent, mUserId);
- System.out.println("Success");
- }
-
- private ComponentName parseComponentName(String component) {
- ComponentName cn = ComponentName.unflattenFromString(component);
- if (cn == null) {
- throw new IllegalArgumentException ("Invalid component " + component);
- }
- return cn;
- }
-
- private int parseInt(String argument) {
- try {
- return Integer.parseInt(argument);
- } catch (NumberFormatException e) {
- throw new IllegalArgumentException ("Invalid integer argument '" + argument + "'", e);
- }
- }
-}
diff --git a/core/api/current.txt b/core/api/current.txt
index b223bc6..bbed3e6 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8014,7 +8014,6 @@
method @Nullable public String[] getTriggeredContentAuthorities();
method @Nullable public android.net.Uri[] getTriggeredContentUris();
method public boolean isExpeditedJob();
- method @Deprecated public boolean isForegroundJob();
method public boolean isOverrideDeadlineExpired();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.job.JobParameters> CREATOR;
@@ -17137,11 +17136,11 @@
package android.hardware {
- public abstract class Battery {
- ctor public Battery();
+ public abstract class BatteryState {
+ ctor public BatteryState();
method @FloatRange(from=-1.0F, to=1.0f) public abstract float getCapacity();
method public abstract int getStatus();
- method public abstract boolean hasBattery();
+ method public abstract boolean isPresent();
field public static final int STATUS_CHARGING = 2; // 0x2
field public static final int STATUS_DISCHARGING = 3; // 0x3
field public static final int STATUS_FULL = 5; // 0x5
@@ -18992,7 +18991,6 @@
}
public abstract static class LightsManager.LightsSession implements java.lang.AutoCloseable {
- ctor public LightsManager.LightsSession();
method public abstract void close();
method public abstract void requestLights(@NonNull android.hardware.lights.LightsRequest);
}
@@ -27111,7 +27109,7 @@
method @NonNull public int[] getExposedCapabilities();
method @NonNull public String getGatewayConnectionName();
method @IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) public int getMaxMtu();
- method @NonNull public long[] getRetryIntervalsMs();
+ method @NonNull public long[] getRetryIntervalsMillis();
}
public static final class VcnGatewayConnectionConfig.Builder {
@@ -27120,7 +27118,7 @@
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig build();
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) int);
- method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMs(@NonNull long[]);
+ method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMillis(@NonNull long[]);
}
public class VcnManager {
@@ -41991,7 +41989,6 @@
method @IntRange(from=1, to=261) public int getBand();
method @IntRange(from=1) public int getCellBandwidthDownlinkKhz();
method @IntRange(from=1) public int getCellBandwidthUplinkKhz();
- method @Deprecated public int getChannelNumber();
method public int getConnectionStatus();
method @IntRange(from=0) public int getDownlinkChannelNumber();
method @IntRange(from=0) public int getDownlinkFrequencyKhz();
@@ -47132,7 +47129,7 @@
public final class InputDevice implements android.os.Parcelable {
method public int describeContents();
- method @NonNull public android.hardware.Battery getBattery();
+ method @NonNull public android.hardware.BatteryState getBatteryState();
method public int getControllerNumber();
method public String getDescriptor();
method public static android.view.InputDevice getDevice(int);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 677da39..e48a1da 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -223,9 +223,9 @@
public final class UnderlyingNetworkInfo implements android.os.Parcelable {
ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>);
method public int describeContents();
- method @NonNull public String getIface();
+ method @NonNull public String getInterface();
method public int getOwnerUid();
- method @NonNull public java.util.List<java.lang.String> getUnderlyingIfaces();
+ method @NonNull public java.util.List<java.lang.String> getUnderlyingInterfaces();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.UnderlyingNetworkInfo> CREATOR;
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 22af3f7..3537d8b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -317,6 +317,7 @@
public static final class R.attr {
field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
+ field public static final int durationBetweenRequestsMillis;
field public static final int hotwordDetectionService;
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int minExtensionVersion = 16844305; // 0x1010611
@@ -325,7 +326,6 @@
field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
field public static final int sdkVersion = 16844304; // 0x1010610
field public static final int supportsAmbientMode = 16844173; // 0x101058d
- field public static final int throttleDurationMillis;
field public static final int userRestriction = 16844164; // 0x1010584
}
@@ -1448,11 +1448,12 @@
package android.app.search {
public final class Query implements android.os.Parcelable {
- ctor public Query(@NonNull String, long, @Nullable android.os.Bundle);
+ ctor public Query(@NonNull String, long, @NonNull android.os.Bundle);
+ ctor public Query(@NonNull String, long);
method public int describeContents();
- method @Nullable public android.os.Bundle getExtras();
+ method @NonNull public android.os.Bundle getExtras();
method @NonNull public String getInput();
- method @NonNull public long getTimestamp();
+ method public long getTimestampMillis();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.search.Query> CREATOR;
}
@@ -1485,9 +1486,10 @@
}
public final class SearchContext implements android.os.Parcelable {
- ctor public SearchContext(int, int, @Nullable android.os.Bundle);
+ ctor public SearchContext(int, int);
+ ctor public SearchContext(int, int, @NonNull android.os.Bundle);
method public int describeContents();
- method @Nullable public android.os.Bundle getExtras();
+ method @NonNull public android.os.Bundle getExtras();
method @Nullable public String getPackageName();
method @NonNull public int getResultTypes();
method @NonNull public int getTimeoutMillis();
@@ -1497,7 +1499,6 @@
public final class SearchSession implements java.lang.AutoCloseable {
method public void close();
- method public void destroy();
method protected void finalize();
method public void notifyEvent(@NonNull android.app.search.Query, @NonNull android.app.search.SearchTargetEvent);
method @Nullable public void query(@NonNull android.app.search.Query, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.search.SearchTarget>>);
@@ -1512,7 +1513,7 @@
public final class SearchTarget implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.appwidget.AppWidgetProviderInfo getAppWidgetProviderInfo();
- method @Nullable public android.os.Bundle getExtras();
+ method @NonNull public android.os.Bundle getExtras();
method @NonNull public String getId();
method @NonNull public String getLayoutType();
method @NonNull public String getPackageName();
@@ -1526,13 +1527,17 @@
method public boolean shouldHide();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.search.SearchTarget> CREATOR;
+ field public static final int RESULT_TYPE_APPLICATION = 1; // 0x1
+ field public static final int RESULT_TYPE_SHORTCUT = 2; // 0x2
+ field public static final int RESULT_TYPE_SLICE = 4; // 0x4
+ field public static final int RESULT_TYPE_WIDGETS = 8; // 0x8
}
public static final class SearchTarget.Builder {
ctor public SearchTarget.Builder(int, @NonNull String, @NonNull String);
method @NonNull public android.app.search.SearchTarget build();
method @NonNull public android.app.search.SearchTarget.Builder setAppWidgetProviderInfo(@NonNull android.appwidget.AppWidgetProviderInfo);
- method @NonNull public android.app.search.SearchTarget.Builder setExtras(@Nullable android.os.Bundle);
+ method @NonNull public android.app.search.SearchTarget.Builder setExtras(@NonNull android.os.Bundle);
method @NonNull public android.app.search.SearchTarget.Builder setPackageName(@NonNull String);
method @NonNull public android.app.search.SearchTarget.Builder setParentId(@NonNull String);
method @NonNull public android.app.search.SearchTarget.Builder setScore(float);
@@ -9867,7 +9872,7 @@
public final class DisplayHashParams implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.util.Size getBufferSize();
- method public boolean isUseGrayscale();
+ method public boolean isGrayscaleBuffer();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.displayhash.DisplayHashParams> CREATOR;
}
@@ -9876,7 +9881,7 @@
ctor public DisplayHashParams.Builder();
method @NonNull public android.service.displayhash.DisplayHashParams build();
method @NonNull public android.service.displayhash.DisplayHashParams.Builder setBufferSize(int, int);
- method @NonNull public android.service.displayhash.DisplayHashParams.Builder setUseGrayscale(boolean);
+ method @NonNull public android.service.displayhash.DisplayHashParams.Builder setGrayscaleBuffer(boolean);
}
public abstract class DisplayHashingService extends android.app.Service {
@@ -10260,10 +10265,10 @@
public abstract class SearchUiService extends android.app.Service {
ctor public SearchUiService();
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
- method public void onCreateSearchSession(@NonNull android.app.search.SearchContext, @NonNull android.app.search.SearchSessionId);
method @MainThread public abstract void onDestroy(@NonNull android.app.search.SearchSessionId);
method @MainThread public abstract void onNotifyEvent(@NonNull android.app.search.SearchSessionId, @NonNull android.app.search.Query, @NonNull android.app.search.SearchTargetEvent);
method @MainThread public abstract void onQuery(@NonNull android.app.search.SearchSessionId, @NonNull android.app.search.Query, @NonNull java.util.function.Consumer<java.util.List<android.app.search.SearchTarget>>);
+ method public void onSearchSessionCreated(@NonNull android.app.search.SearchContext, @NonNull android.app.search.SearchSessionId);
}
}
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index f366a54..65a8780 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -48,6 +48,18 @@
}
+package android.app.search {
+
+ public final class Query implements android.os.Parcelable {
+ method @Deprecated @NonNull public long getTimestamp();
+ }
+
+ public final class SearchSession implements java.lang.AutoCloseable {
+ method @Deprecated public void destroy();
+ }
+
+}
+
package android.bluetooth {
public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
@@ -163,6 +175,14 @@
}
+package android.service.search {
+
+ public abstract class SearchUiService extends android.app.Service {
+ method @Deprecated public void onCreateSearchSession(@NonNull android.app.search.SearchContext, @NonNull android.app.search.SearchSessionId);
+ }
+
+}
+
package android.telecom {
public class TelecomManager {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index a24555f..6d2d023 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1947,8 +1947,9 @@
pw.print(((baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0));
pw.print(" activityType="); pw.print(activityTypeToString(getActivityType()));
pw.print(" windowingMode="); pw.print(windowingModeToString(getWindowingMode()));
- pw.print(" supportsSplitScreenMultiWindow=");
- pw.println(supportsSplitScreenMultiWindow);
+ pw.print(" supportsSplitScreenMultiWindow="); pw.print(supportsSplitScreenMultiWindow);
+ pw.print(" supportsMultiWindow=");
+ pw.println(supportsMultiWindow);
if (taskDescription != null) {
pw.print(" ");
final ActivityManager.TaskDescription td = taskDescription;
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 6df9f4d..4a7fcd2 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -349,20 +349,6 @@
}
/**
- * Whether to allow non-resizable apps to be shown in multi-window. The app will be letterboxed
- * if the request orientation is not met, and will be shown in size-compat mode if the container
- * size has changed.
- * @hide
- */
- public static boolean supportsNonResizableMultiWindow() {
- try {
- return ActivityTaskManager.getService().supportsNonResizableMultiWindow();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* @return whether the UI mode of the given config supports error dialogs (ANR, crash, etc).
* @hide
*/
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 1cb46b1..d798620 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -6511,13 +6511,21 @@
NoteOpEvent note = new NoteOpEvent(discreteAccessTime, discreteAccessDuration, null);
accessEvents.append(key, note);
AttributedOpEntry access = new AttributedOpEntry(mOp, false, accessEvents, null);
- for (int i = discreteAccesses.size() - 1; i >= 0; i--) {
- if (discreteAccesses.get(i).getLastAccessTime(OP_FLAGS_ALL) < discreteAccessTime) {
- discreteAccesses.add(i + 1, access);
- return;
+ int insertionPoint = discreteAccesses.size() - 1;
+ for (; insertionPoint >= 0; insertionPoint--) {
+ if (discreteAccesses.get(insertionPoint).getLastAccessTime(OP_FLAGS_ALL)
+ < discreteAccessTime) {
+ break;
}
}
- discreteAccesses.add(0, access);
+ insertionPoint++;
+ if (insertionPoint < discreteAccesses.size() && discreteAccesses.get(
+ insertionPoint).getLastAccessTime(OP_FLAGS_ALL) == discreteAccessTime) {
+ discreteAccesses.set(insertionPoint, mergeAttributedOpEntries(
+ Arrays.asList(discreteAccesses.get(insertionPoint), access)));
+ } else {
+ discreteAccesses.add(insertionPoint, access);
+ }
}
/**
@@ -9858,7 +9866,10 @@
NoteOpEvent reject = a.getLastRejectEvent(uidState, uidState, flags);
if (access != null) {
- accessEvents.append(key, access);
+ NoteOpEvent existingAccess = accessEvents.get(key);
+ if (existingAccess == null || existingAccess.getDuration() == -1) {
+ accessEvents.append(key, access);
+ }
}
if (reject != null) {
rejectEvents.append(key, reject);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 656942d..9ce37e4 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3273,6 +3273,13 @@
dir = null;
}
}
+ if (dir != null && !dir.canWrite()) {
+ // Older versions of the MediaProvider mainline module had a rare early boot race
+ // condition where app-private dirs could be created with the wrong permissions;
+ // fix this up here. This check should be very fast, because dir.exists() above
+ // will already have loaded the dentry in the cache.
+ sm.fixupAppDir(dir);
+ }
result[i] = dir;
}
return result;
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index b75e89c..74d51a0 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -259,13 +259,6 @@
void setSplitScreenResizing(boolean resizing);
boolean supportsLocalVoiceInteraction();
- /**
- * Whether to allow non-resizable apps to be shown in multi-window. The app will be letterboxed
- * if the request orientation is not met, and will be shown in size-compat mode if the container
- * size has changed.
- */
- boolean supportsNonResizableMultiWindow();
-
// Get device configuration
ConfigurationInfo getDeviceConfigurationInfo();
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 83d0246..ea6c874 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -58,6 +58,7 @@
import android.util.SparseArray;
import android.view.DisplayAdjustments;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import dalvik.system.BaseDexClassLoader;
@@ -156,6 +157,7 @@
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
= new ArrayMap<>();
private AppComponentFactory mAppComponentFactory;
+ private final Object mLock = new Object();
Application getApplication() {
return mApplication;
@@ -354,7 +356,7 @@
} else {
addedPaths.addAll(newPaths);
}
- synchronized (this) {
+ synchronized (mLock) {
createOrUpdateClassLoaderLocked(addedPaths);
if (mResources != null) {
final String[] splitPaths;
@@ -589,7 +591,9 @@
* include the base APK in the list of splits.
*/
private class SplitDependencyLoaderImpl extends SplitDependencyLoader<NameNotFoundException> {
+ @GuardedBy("mLock")
private final String[][] mCachedResourcePaths;
+ @GuardedBy("mLock")
private final ClassLoader[] mCachedClassLoaders;
SplitDependencyLoaderImpl(@NonNull SparseArray<int[]> dependencies) {
@@ -600,37 +604,41 @@
@Override
protected boolean isSplitCached(int splitIdx) {
- return mCachedClassLoaders[splitIdx] != null;
+ synchronized (mLock) {
+ return mCachedClassLoaders[splitIdx] != null;
+ }
}
@Override
protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
int parentSplitIdx) throws NameNotFoundException {
- final ArrayList<String> splitPaths = new ArrayList<>();
- if (splitIdx == 0) {
- createOrUpdateClassLoaderLocked(null);
- mCachedClassLoaders[0] = mClassLoader;
+ synchronized (mLock) {
+ final ArrayList<String> splitPaths = new ArrayList<>();
+ if (splitIdx == 0) {
+ createOrUpdateClassLoaderLocked(null);
+ mCachedClassLoaders[0] = mClassLoader;
- // Never add the base resources here, they always get added no matter what.
+ // Never add the base resources here, they always get added no matter what.
+ for (int configSplitIdx : configSplitIndices) {
+ splitPaths.add(mSplitResDirs[configSplitIdx - 1]);
+ }
+ mCachedResourcePaths[0] = splitPaths.toArray(new String[splitPaths.size()]);
+ return;
+ }
+
+ // Since we handled the special base case above, parentSplitIdx is always valid.
+ final ClassLoader parent = mCachedClassLoaders[parentSplitIdx];
+ mCachedClassLoaders[splitIdx] = ApplicationLoaders.getDefault().getClassLoader(
+ mSplitAppDirs[splitIdx - 1], getTargetSdkVersion(), false, null,
+ null, parent, mSplitClassLoaderNames[splitIdx - 1]);
+
+ Collections.addAll(splitPaths, mCachedResourcePaths[parentSplitIdx]);
+ splitPaths.add(mSplitResDirs[splitIdx - 1]);
for (int configSplitIdx : configSplitIndices) {
splitPaths.add(mSplitResDirs[configSplitIdx - 1]);
}
- mCachedResourcePaths[0] = splitPaths.toArray(new String[splitPaths.size()]);
- return;
+ mCachedResourcePaths[splitIdx] = splitPaths.toArray(new String[splitPaths.size()]);
}
-
- // Since we handled the special base case above, parentSplitIdx is always valid.
- final ClassLoader parent = mCachedClassLoaders[parentSplitIdx];
- mCachedClassLoaders[splitIdx] = ApplicationLoaders.getDefault().getClassLoader(
- mSplitAppDirs[splitIdx - 1], getTargetSdkVersion(), false, null, null, parent,
- mSplitClassLoaderNames[splitIdx - 1]);
-
- Collections.addAll(splitPaths, mCachedResourcePaths[parentSplitIdx]);
- splitPaths.add(mSplitResDirs[splitIdx - 1]);
- for (int configSplitIdx : configSplitIndices) {
- splitPaths.add(mSplitResDirs[configSplitIdx - 1]);
- }
- mCachedResourcePaths[splitIdx] = splitPaths.toArray(new String[splitPaths.size()]);
}
private int ensureSplitLoaded(String splitName) throws NameNotFoundException {
@@ -648,11 +656,17 @@
}
ClassLoader getClassLoaderForSplit(String splitName) throws NameNotFoundException {
- return mCachedClassLoaders[ensureSplitLoaded(splitName)];
+ final int idx = ensureSplitLoaded(splitName);
+ synchronized (mLock) {
+ return mCachedClassLoaders[idx];
+ }
}
String[] getSplitPathsForSplit(String splitName) throws NameNotFoundException {
- return mCachedResourcePaths[ensureSplitLoaded(splitName)];
+ final int idx = ensureSplitLoaded(splitName);
+ synchronized (mLock) {
+ return mCachedResourcePaths[idx];
+ }
}
}
@@ -749,6 +763,7 @@
}
}
+ @GuardedBy("mLock")
private void createOrUpdateClassLoaderLocked(List<String> addedPaths) {
if (mPackageName.equals("android")) {
// Note: This branch is taken for system server and we don't need to setup
@@ -1023,7 +1038,7 @@
@UnsupportedAppUsage
public ClassLoader getClassLoader() {
- synchronized (this) {
+ synchronized (mLock) {
if (mClassLoader == null) {
createOrUpdateClassLoaderLocked(null /*addedPaths*/);
}
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 6ad5eea..b95412f 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -138,6 +138,13 @@
public boolean supportsSplitScreenMultiWindow;
/**
+ * Whether this task supports multi windowing modes based on the device settings and the
+ * root activity resizability and configuration.
+ * @hide
+ */
+ public boolean supportsMultiWindow;
+
+ /**
* The resize mode of the task. See {@link ActivityInfo#resizeMode}.
* @hide
*/
@@ -329,6 +336,7 @@
}
return topActivityType == that.topActivityType
&& isResizeable == that.isResizeable
+ && supportsMultiWindow == that.supportsMultiWindow
&& Objects.equals(positionInParent, that.positionInParent)
&& Objects.equals(pictureInPictureParams, that.pictureInPictureParams)
&& getWindowingMode() == that.getWindowingMode()
@@ -375,6 +383,7 @@
taskDescription = source.readTypedObject(ActivityManager.TaskDescription.CREATOR);
supportsSplitScreenMultiWindow = source.readBoolean();
+ supportsMultiWindow = source.readBoolean();
resizeMode = source.readInt();
configuration.readFromParcel(source);
token = WindowContainerToken.CREATOR.createFromParcel(source);
@@ -412,6 +421,7 @@
dest.writeTypedObject(taskDescription, flags);
dest.writeBoolean(supportsSplitScreenMultiWindow);
+ dest.writeBoolean(supportsMultiWindow);
dest.writeInt(resizeMode);
configuration.writeToParcel(dest, flags);
token.writeToParcel(dest, flags);
@@ -440,6 +450,7 @@
+ " numActivities=" + numActivities
+ " lastActiveTime=" + lastActiveTime
+ " supportsSplitScreenMultiWindow=" + supportsSplitScreenMultiWindow
+ + " supportsMultiWindow=" + supportsMultiWindow
+ " resizeMode=" + resizeMode
+ " isResizeable=" + isResizeable
+ " token=" + token
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c93a88f..cbf2d6a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -10368,6 +10368,9 @@
/**
* Called by device owners to set the user's global location setting.
*
+ * <p><b>Note: </b> this call is ignored on
+ * {@link android.content.pm.PackageManager#FEATURE_AUTOMOTIVE automotive builds}.
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with
* @param locationEnabled whether location should be enabled or disabled
* @throws SecurityException if {@code admin} is not a device owner.
diff --git a/core/java/android/app/compat/PackageOverride.java b/core/java/android/app/compat/PackageOverride.java
index 59b3555..fad6cd3 100644
--- a/core/java/android/app/compat/PackageOverride.java
+++ b/core/java/android/app/compat/PackageOverride.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import android.content.pm.PackageInfo;
import android.os.Parcel;
import java.lang.annotation.Retention;
@@ -101,12 +102,20 @@
return VALUE_UNDEFINED;
}
- /** Returns the minimum version code the override applies to. */
+ /**
+ * Returns the minimum APK version code the override applies to.
+ *
+ * @see PackageInfo#getLongVersionCode()
+ */
public long getMinVersionCode() {
return mMinVersionCode;
}
- /** Returns the minimum version code the override applies from. */
+ /**
+ * Returns the maximum APK version code the override applies from.
+ *
+ * @see PackageInfo#getLongVersionCode()
+ */
public long getMaxVersionCode() {
return mMaxVersionCode;
}
@@ -146,9 +155,11 @@
private boolean mEnabled;
/**
- * Sets the minimum version code the override should apply from.
+ * Sets the minimum APK version code the override should apply from.
*
* default value: {@code Long.MIN_VALUE}.
+ *
+ * @see PackageInfo#getLongVersionCode()
*/
@NonNull
public Builder setMinVersionCode(long minVersionCode) {
@@ -157,9 +168,11 @@
}
/**
- * Sets the maximum version code the override should apply to.
+ * Sets the maximum APK version code the override should apply to.
*
* default value: {@code Long.MAX_VALUE}.
+ *
+ * @see PackageInfo#getLongVersionCode()
*/
@NonNull
public Builder setMaxVersionCode(long maxVersionCode) {
diff --git a/core/java/android/app/search/Query.java b/core/java/android/app/search/Query.java
index 3ab20bb..34ace48 100644
--- a/core/java/android/app/search/Query.java
+++ b/core/java/android/app/search/Query.java
@@ -16,63 +16,108 @@
package android.app.search;
import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
/**
+ * Query object is sent over from client to the service.
+ *
+ * Inside the query object, there is a timestamp that trackes when the query string was typed.
+ *
+ * If this object was created for the {@link SearchSession#query},
+ * the client expects first consumer to be returned
+ * within {@link #getTimestampMillis()} + {@link SearchContext#getTimeoutMillis()}
+ * Base of the timestamp should be SystemClock.elasedRealTime()
+ *
* @hide
*/
@SystemApi
public final class Query implements Parcelable {
/**
- * Query string typed from the client.
+ * string typed from the client.
*/
@NonNull
private final String mInput;
- /**
- * The timestamp that the query string was typed. If this object was created for the
- * {@link SearchSession#query}, the client expects first consumer to be returned
- * within mTimestamp + {@link SearchContext#mTimeoutMillis}
- */
- private final long mTimestamp;
+ private final long mTimestampMillis;
- @Nullable
+ /**
+ * Contains other client UI constraints related data
+ */
+ @NonNull
private final Bundle mExtras;
+ /**
+ * Query object used to pass search box input from client to service.
+ *
+ * @param input string typed from the client
+ * @param timestampMillis timestamp that query string was typed.
+ * @param extras bundle that contains other client UI constraints data
+ */
public Query(@NonNull String input,
- long timestamp,
- @SuppressLint("NullableCollection")
- @Nullable Bundle extras) {
+ long timestampMillis,
+ @NonNull Bundle extras) {
mInput = input;
- mTimestamp = timestamp;
- mExtras = extras;
+ mTimestampMillis = timestampMillis;
+ mExtras = extras == null ? extras : new Bundle();
+ }
+
+ /**
+ * Query object used to pass search box input from client to service.
+ *
+ * @param input string typed from the client
+ * @param timestampMillis timestamp that query string was typed
+ */
+ public Query(@NonNull String input, long timestampMillis) {
+ this(input, timestampMillis, new Bundle());
}
private Query(Parcel parcel) {
mInput = parcel.readString();
- mTimestamp = parcel.readLong();
+ mTimestampMillis = parcel.readLong();
mExtras = parcel.readBundle();
}
+ /**
+ * @return string typed from the client
+ */
@NonNull
public String getInput() {
return mInput;
}
+ /**
+ * @deprecated Will be replaced by {@link #getTimestampMillis()} as soon as
+ * new SDK is adopted.
+ *
+ * @removed
+ */
+ @Deprecated
@NonNull
public long getTimestamp() {
- return mTimestamp;
+ return mTimestampMillis;
}
- @Nullable
- @SuppressLint("NullableCollection")
+ /**
+ * Base of the timestamp should be SystemClock.elasedRealTime()
+ *
+ * @return timestamp that query string was typed
+ */
+ public long getTimestampMillis() {
+ return mTimestampMillis;
+ }
+
+ /**
+ * @return bundle that contains other client constraints related to the query
+ */
+ @NonNull
public Bundle getExtras() {
+ if (mExtras == null) {
+ return new Bundle();
+ }
return mExtras;
}
@@ -84,7 +129,7 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mInput);
- dest.writeLong(mTimestamp);
+ dest.writeLong(mTimestampMillis);
dest.writeBundle(mExtras);
}
diff --git a/core/java/android/app/search/SearchContext.java b/core/java/android/app/search/SearchContext.java
index 548b7da..3e345fa 100644
--- a/core/java/android/app/search/SearchContext.java
+++ b/core/java/android/app/search/SearchContext.java
@@ -17,13 +17,20 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
/**
+ * When {@link SearchSession} is created, {@link SearchContext} object is created
+ * to pass the result types from the {@link SearchSession#query(Query, Executor, Consumer)}
+ * method that the client wants.
+ *
* @hide
*/
@SystemApi
@@ -51,12 +58,25 @@
@Nullable
private String mPackageName;
+ /**
+ * @param resultTypes {@link SearchTarget.SearchResultType}s combined using bit OR operation
+ * @param timeoutMillis timeout before client renders its own fallback result
+ */
+ public SearchContext(int resultTypes, int timeoutMillis) {
+ this(resultTypes, timeoutMillis, new Bundle());
+ }
+
+ /**
+ * @param resultTypes {@link SearchTarget.SearchResultType}s combined using bit OR operation
+ * @param timeoutMillis timeout before client renders its own fallback result
+ * @param extras other client constraints (e.g., height of the search surface)
+ */
public SearchContext(int resultTypes,
- int queryTimeoutMillis,
- @SuppressLint("NullableCollection") @Nullable Bundle extras) {
+ int timeoutMillis,
+ @NonNull Bundle extras) {
mResultTypes = resultTypes;
- mTimeoutMillis = queryTimeoutMillis;
- mExtras = extras;
+ mTimeoutMillis = timeoutMillis;
+ mExtras = Objects.requireNonNull(extras);
}
private SearchContext(Parcel parcel) {
@@ -74,7 +94,7 @@
/**
* @hide
*/
- public void setPackageName(@Nullable String packageName) {
+ void setPackageName(@Nullable String packageName) {
mPackageName = packageName;
}
@@ -83,8 +103,7 @@
return mTimeoutMillis;
}
- @Nullable
- @SuppressLint("NullableCollection")
+ @NonNull
public Bundle getExtras() {
return mExtras;
}
diff --git a/core/java/android/app/search/SearchSession.java b/core/java/android/app/search/SearchSession.java
index 7bd88d9..a5425a2 100644
--- a/core/java/android/app/search/SearchSession.java
+++ b/core/java/android/app/search/SearchSession.java
@@ -37,7 +37,9 @@
import java.util.function.Consumer;
/**
- * Client API to share information about the search UI state and execute query.
+ * Client needs to create {@link SearchSession} object from in order to execute
+ * {@link #query(Query, Executor, Consumer)} method and share client side signals
+ * back to the service using {@link #notifyEvent(Query, SearchTargetEvent)}.
*
* <p>
* Usage: <pre> {@code
@@ -60,7 +62,7 @@
* }
*
* void onDestroy() {
- * mSearchSession.destroy();
+ * mSearchSession.close();
* }
*
* }</pre>
@@ -108,7 +110,10 @@
}
/**
- * Notifies the search service of an search target event.
+ * Notifies the search service of an search target event (e.g., user interaction
+ * and lifecycle event of the search surface).
+ *
+ * {@see SearchTargetEvent}
*
* @param query input object associated with the event.
* @param event The {@link SearchTargetEvent} that represents the search target event.
@@ -153,7 +158,11 @@
/**
* Destroys the client and unregisters the callback. Any method on this class after this call
* will throw {@link IllegalStateException}.
+ *
+ * @deprecated
+ * @removed
*/
+ @Deprecated
public void destroy() {
if (!mIsClosed.getAndSet(true)) {
mCloseGuard.close();
@@ -188,6 +197,11 @@
}
}
+ /**
+ * Destroys the client and unregisters the callback. Any method on this class after this call
+ * will throw {@link IllegalStateException}.
+ *
+ */
@Override
public void close() {
try {
diff --git a/core/java/android/app/search/SearchTarget.java b/core/java/android/app/search/SearchTarget.java
index 6a80f8b..56c5ddf 100644
--- a/core/java/android/app/search/SearchTarget.java
+++ b/core/java/android/app/search/SearchTarget.java
@@ -15,34 +15,73 @@
*/
package android.app.search;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.app.slice.SliceManager;
import android.appwidget.AppWidgetProviderInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
- * A representation of a searchable item info.
+ * A representation of a search result. Search result can be expressed in one of the following:
+ * app icon, shortcut, slice, widget, or a custom object using {@link SearchAction}. While
+ * app icon ({@link PackageManager}, shortcut {@link ShortcutManager}, slice {@link SliceManager},
+ * or widget (@link AppWidgetManager} are published content backed by the system service,
+ * {@link SearchAction} is a custom object that the service can use to send search result to the
+ * client.
+ *
+ * These various types of Android primitives could be defined as {@link SearchResultType}. Some
+ * times, the result type can define the layout type that that this object can be rendered in.
+ * (e.g., app widget). Most times, {@link #getLayoutType()} assigned by the service
+ * can recommend which layout this target should be rendered in.
+ *
+ * The service can also use fields such as {@link #getScore()} to indicate
+ * how confidence the search result is and {@link #shouldHide()} to indicate
+ * whether it is recommended to be shown by default.
+ *
+ * Finally, {@link #getId()} is the unique identifier of this search target and a single
+ * search target is defined by being able to express a single launcheable item. In case the
+ * service want to recommend how to combine multiple search target objects to render in a group
+ * (e.g., same row), {@link #getParentId()} can be assigned on the sub targets of the group
+ * using the primary search target's identifier.
*
* @hide
*/
@SystemApi
public final class SearchTarget implements Parcelable {
-
- @NonNull
+ public static final int RESULT_TYPE_APPLICATION = 1 << 0;
+ public static final int RESULT_TYPE_SHORTCUT = 1 << 1;
+ public static final int RESULT_TYPE_SLICE = 1 << 2;
+ public static final int RESULT_TYPE_WIDGETS = 1 << 3;
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"RESULT_TYPE_"}, value = {
+ RESULT_TYPE_APPLICATION,
+ RESULT_TYPE_SHORTCUT,
+ RESULT_TYPE_SLICE,
+ RESULT_TYPE_WIDGETS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SearchResultType {}
private final int mResultType;
/**
- * Constant to express how the group of {@link SearchTarget} should be laid out.
+ * Constant to express how the group of {@link SearchTarget} should be rendered on
+ * the client side. (e.g., "icon", "icon_row", "short_icon_row")
*/
@NonNull
private final String mLayoutType;
@@ -69,13 +108,13 @@
private final AppWidgetProviderInfo mAppWidgetProviderInfo;
@Nullable
private final Uri mSliceUri;
- @Nullable
+
+ @NonNull
private final Bundle mExtras;
private SearchTarget(Parcel parcel) {
mResultType = parcel.readInt();
mLayoutType = parcel.readString();
-
mId = parcel.readString();
mParentId = parcel.readString();
mScore = parcel.readFloat();
@@ -102,7 +141,7 @@
@Nullable ShortcutInfo shortcutInfo,
@Nullable Uri sliceUri,
@Nullable AppWidgetProviderInfo appWidgetProviderInfo,
- @Nullable Bundle extras) {
+ @NonNull Bundle extras) {
mResultType = resultType;
mLayoutType = Objects.requireNonNull(layoutType);
mId = Objects.requireNonNull(id);
@@ -129,9 +168,9 @@
}
/**
- * Retrieves the result type.
+ * Retrieves the result type {@see SearchResultType}.
*/
- public int getResultType() {
+ public @SearchResultType int getResultType() {
return mResultType;
}
@@ -167,7 +206,7 @@
}
/**
- * TODO: add comment
+ * Indicates whether this object should be hidden and shown only on demand.
*/
public boolean shouldHide() {
return mShouldHide;
@@ -198,7 +237,7 @@
}
/**
- * Return widget provider info.
+ * Return a widget provider info.
*/
@Nullable
public AppWidgetProviderInfo getAppWidgetProviderInfo() {
@@ -206,7 +245,7 @@
}
/**
- * Return slice uri.
+ * Returns a slice uri.
*/
@Nullable
public Uri getSliceUri() {
@@ -214,7 +253,7 @@
}
/**
- * Return search action.
+ * Returns a search action.
*/
@Nullable
public SearchAction getSearchAction() {
@@ -224,8 +263,7 @@
/**
* Return extra bundle.
*/
- @Nullable
- @SuppressLint("NullableCollection")
+ @NonNull
public Bundle getExtras() {
return mExtras;
}
@@ -295,10 +333,10 @@
private Uri mSliceUri;
@Nullable
private AppWidgetProviderInfo mAppWidgetProviderInfo;
- @Nullable
+ @NonNull
private Bundle mExtras;
- public Builder(int resultType,
+ public Builder(@SearchResultType int resultType,
@NonNull String layoutType,
@NonNull String id) {
mId = id;
@@ -369,32 +407,30 @@
*/
@NonNull
public Builder setSliceUri(@NonNull Uri sliceUri) {
- // TODO: add packageName check
mSliceUri = sliceUri;
return this;
}
/**
- * TODO: add comment
+ * Set the {@link SearchAction} object to this target.
*/
@NonNull
- public Builder setSearchAction(@Nullable SearchAction remoteAction) {
- // TODO: add packageName check
- mSearchAction = remoteAction;
+ public Builder setSearchAction(@Nullable SearchAction searchAction) {
+ mSearchAction = searchAction;
return this;
}
/**
- * TODO: add comment
+ * Set any extra information that needs to be shared between service and the client.
*/
@NonNull
- public Builder setExtras(@SuppressLint("NullableCollection") @Nullable Bundle extras) {
- mExtras = extras;
+ public Builder setExtras(@NonNull Bundle extras) {
+ mExtras = Objects.requireNonNull(extras);
return this;
}
/**
- * TODO: add comment
+ * Sets the score of the object.
*/
@NonNull
public Builder setScore(float score) {
@@ -403,7 +439,7 @@
}
/**
- * TODO: add comment
+ * Sets whether the result should be hidden by default inside client.
*/
@NonNull
public Builder setShouldHide(boolean shouldHide) {
diff --git a/core/java/android/app/search/SearchTargetEvent.java b/core/java/android/app/search/SearchTargetEvent.java
index f478dc3..d4915af 100644
--- a/core/java/android/app/search/SearchTargetEvent.java
+++ b/core/java/android/app/search/SearchTargetEvent.java
@@ -29,7 +29,11 @@
import java.util.Objects;
/**
- * A representation of an app target event.
+ * A representation of an search target event.
+ *
+ * There are two types of events. First type of event correspends to the user interaction
+ * that happens on the search surface. (e.g., {@link #ACTION_TAP}. Second type of events
+ * correspends to the lifecycle event of the search surface {@link #ACTION_SURFACE_VISIBLE}.
*
* @hide
*/
diff --git a/core/java/android/app/search/SearchUiManager.java b/core/java/android/app/search/SearchUiManager.java
index 636bfe1..ce6d8b2 100644
--- a/core/java/android/app/search/SearchUiManager.java
+++ b/core/java/android/app/search/SearchUiManager.java
@@ -25,6 +25,12 @@
/**
* Class that provides methods to create search ui session clients.
*
+ * Usage: <pre> {@code
+ * mSearchUiManager = context.getSystemService(SearchUiManager.class);
+ * mSearchSession.createSearchSession(searchContext)
+ *
+ * }</pre>
+ *
* @hide
*/
@SystemApi
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 9dc2d8e..3bf517c 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -31,6 +31,7 @@
import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -365,6 +366,15 @@
mAdapter = adapter;
mAttributionSource = adapter.getAttributionSource();
+ // Preserve legacy compatibility where apps were depending on
+ // registerStateChangeCallback() performing a permissions check which
+ // has been relaxed in modern platform versions
+ if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R
+ && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Need BLUETOOTH permission");
+ }
+
IBluetoothManager mgr = mAdapter.getBluetoothManager();
if (mgr != null) {
try {
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index 2600029..8ce01a3 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -30,6 +30,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
@@ -136,6 +137,16 @@
mServiceListener = l;
mAdapter = adapter;
mAttributionSource = adapter.getAttributionSource();
+
+ // Preserve legacy compatibility where apps were depending on
+ // registerStateChangeCallback() performing a permissions check which
+ // has been relaxed in modern platform versions
+ if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R
+ && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Need BLUETOOTH permission");
+ }
+
IBluetoothManager mgr = mAdapter.getBluetoothManager();
if (mgr != null) {
try {
diff --git a/core/java/android/bluetooth/BluetoothProfileConnector.java b/core/java/android/bluetooth/BluetoothProfileConnector.java
index b20ab75..beff841 100644
--- a/core/java/android/bluetooth/BluetoothProfileConnector.java
+++ b/core/java/android/bluetooth/BluetoothProfileConnector.java
@@ -21,6 +21,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -123,6 +125,16 @@
mContext = context;
mServiceListener = listener;
IBluetoothManager mgr = BluetoothAdapter.getDefaultAdapter().getBluetoothManager();
+
+ // Preserve legacy compatibility where apps were depending on
+ // registerStateChangeCallback() performing a permissions check which
+ // has been relaxed in modern platform versions
+ if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.R
+ && context.checkSelfPermission(android.Manifest.permission.BLUETOOTH)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Need BLUETOOTH permission");
+ }
+
if (mgr != null) {
try {
mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 7e1df1b..114ad87 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -1885,9 +1885,9 @@
* in {@link android.provider.MediaStore.MediaColumns}.</p>
*
* @param uri The URI whose file is to be opened.
- * @param mode Access mode for the file. May be "r" for read-only access,
- * "rw" for read and write access, or "rwt" for read and write access
- * that truncates any existing file.
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
*
* @return Returns a new ParcelFileDescriptor which you can use to access
* the file.
@@ -1948,10 +1948,9 @@
* in {@link android.provider.MediaStore.MediaColumns}.</p>
*
* @param uri The URI whose file is to be opened.
- * @param mode Access mode for the file. May be "r" for read-only access,
- * "w" for write-only access, "rw" for read and write access, or
- * "rwt" for read and write access that truncates any existing
- * file.
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
* @param signal A signal to cancel the operation in progress, or
* {@code null} if none. For example, if you are downloading a
* file from the network to service a "rw" mode request, you
@@ -2011,11 +2010,9 @@
* containing at least the columns specified by {@link android.provider.OpenableColumns}.</p>
*
* @param uri The URI whose file is to be opened.
- * @param mode Access mode for the file. May be "r" for read-only access,
- * "w" for write-only access (erasing whatever data is currently in
- * the file), "wa" for write-only access to append to any existing data,
- * "rw" for read and write access on any existing data, and "rwt" for read
- * and write access that truncates any existing file.
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
*
* @return Returns a new AssetFileDescriptor which you can use to access
* the file.
@@ -2068,11 +2065,9 @@
* containing at least the columns specified by {@link android.provider.OpenableColumns}.</p>
*
* @param uri The URI whose file is to be opened.
- * @param mode Access mode for the file. May be "r" for read-only access,
- * "w" for write-only access (erasing whatever data is currently in
- * the file), "wa" for write-only access to append to any existing data,
- * "rw" for read and write access on any existing data, and "rwt" for read
- * and write access that truncates any existing file.
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
* @param signal A signal to cancel the operation in progress, or
* {@code null} if none. For example, if you are downloading a
* file from the network to service a "rw" mode request, you
@@ -2103,11 +2098,9 @@
* by looking up a column named "_data" at the given URI.
*
* @param uri The URI to be opened.
- * @param mode The file mode. May be "r" for read-only access,
- * "w" for write-only access (erasing whatever data is currently in
- * the file), "wa" for write-only access to append to any existing data,
- * "rw" for read and write access on any existing data, and "rwt" for read
- * and write access that truncates any existing file.
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
*
* @return Returns a new ParcelFileDescriptor that can be used by the
* client to access the file.
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index aec39da..1132991 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -63,7 +63,9 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.system.ErrnoException;
import android.system.Int64Ref;
+import android.system.Os;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
@@ -76,8 +78,10 @@
import dalvik.system.CloseGuard;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -864,6 +868,20 @@
return wrap((ContentInterface) wrapped);
}
+ /**
+ * Offer to locally truncate the given file when opened using the write-only
+ * mode. This is typically used to preserve legacy compatibility behavior.
+ */
+ private static void maybeTruncate(FileDescriptor fd, String mode) throws FileNotFoundException {
+ if ("w".equals(mode)) {
+ try {
+ Os.ftruncate(fd, 0);
+ } catch (ErrnoException e) {
+ throw new FileNotFoundException("Failed to truncate: " + e.getMessage());
+ }
+ }
+ }
+
/** @hide */
@SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
@@ -1525,8 +1543,20 @@
}
/**
- * Synonym for {@link #openOutputStream(Uri, String)
- * openOutputStream(uri, "w")}.
+ * Open a stream on to the content associated with a content URI. If there
+ * is no data associated with the URI, FileNotFoundException is thrown.
+ *
+ * <h5>Accepts the following URI schemes:</h5>
+ * <ul>
+ * <li>content ({@link #SCHEME_CONTENT})</li>
+ * <li>file ({@link #SCHEME_FILE})</li>
+ * </ul>
+ *
+ * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
+ * on these schemes.
+ *
+ * <p>This method behaves like {@link FileOutputStream} and automatically
+ * truncates any existing contents.
*
* @param uri The desired URI.
* @return an OutputStream or {@code null} if the provider recently crashed.
@@ -1534,7 +1564,16 @@
*/
public final @Nullable OutputStream openOutputStream(@NonNull Uri uri)
throws FileNotFoundException {
- return openOutputStream(uri, "w");
+ AssetFileDescriptor fd = openAssetFileDescriptor(uri, "w", null);
+ if (fd == null) return null;
+ try {
+ final FileOutputStream res = fd.createOutputStream();
+ // Unconditionally truncate to mirror FileOutputStream behavior
+ maybeTruncate(res.getFD(), "w");
+ return res;
+ } catch (IOException e) {
+ throw new FileNotFoundException("Unable to create stream");
+ }
}
/**
@@ -1551,7 +1590,9 @@
* on these schemes.
*
* @param uri The desired URI.
- * @param mode May be "w", "wa", "rw", or "rwt".
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
* @return an OutputStream or {@code null} if the provider recently crashed.
* @throws FileNotFoundException if the provided URI could not be opened.
* @see #openAssetFileDescriptor(Uri, String)
@@ -1559,8 +1600,14 @@
public final @Nullable OutputStream openOutputStream(@NonNull Uri uri, @NonNull String mode)
throws FileNotFoundException {
AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode, null);
+ if (fd == null) return null;
try {
- return fd != null ? fd.createOutputStream() : null;
+ final FileOutputStream res = fd.createOutputStream();
+ // Preserve legacy behavior by offering to truncate
+ if (mTargetSdkVersion < Build.VERSION_CODES.Q) {
+ maybeTruncate(res.getFD(), mode);
+ }
+ return res;
} catch (IOException e) {
throw new FileNotFoundException("Unable to create stream");
}
@@ -1607,8 +1654,9 @@
* provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
*
* @param uri The desired URI to open.
- * @param mode The file mode to use, as per {@link ContentProvider#openFile
- * ContentProvider.openFile}.
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
* @return Returns a new ParcelFileDescriptor pointing to the file or {@code null} if the
* provider recently crashed. You own this descriptor and are responsible for closing it
* when done.
@@ -1650,8 +1698,9 @@
* provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
*
* @param uri The desired URI to open.
- * @param mode The file mode to use, as per {@link ContentProvider#openFile
- * ContentProvider.openFile}.
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
* @param cancellationSignal A signal to cancel the operation in progress,
* or null if none. If the operation is canceled, then
* {@link OperationCanceledException} will be thrown.
@@ -1744,8 +1793,9 @@
* from any built-in data conversion that a provider implements.
*
* @param uri The desired URI to open.
- * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
- * ContentProvider.openAssetFile}.
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
* @return Returns a new ParcelFileDescriptor pointing to the file or {@code null} if the
* provider recently crashed. You own this descriptor and are responsible for closing it
* when done.
@@ -1798,8 +1848,9 @@
* from any built-in data conversion that a provider implements.
*
* @param uri The desired URI to open.
- * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
- * ContentProvider.openAssetFile}.
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt". See
+ * {@link ParcelFileDescriptor#parseMode} for more details.
* @param cancellationSignal A signal to cancel the operation in progress, or null if
* none. If the operation is canceled, then
* {@link OperationCanceledException} will be thrown.
@@ -1835,6 +1886,10 @@
} else if (SCHEME_FILE.equals(scheme)) {
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
new File(uri.getPath()), ParcelFileDescriptor.parseMode(mode));
+ // Preserve legacy behavior by offering to truncate
+ if (mTargetSdkVersion < Build.VERSION_CODES.Q) {
+ maybeTruncate(pfd.getFileDescriptor(), mode);
+ }
return new AssetFileDescriptor(pfd, 0, -1);
} else {
if ("r".equals(mode)) {
@@ -1892,6 +1947,11 @@
// ParcelFileDescriptorInner do that when it is closed.
stableProvider = null;
+ // Preserve legacy behavior by offering to truncate
+ if (mTargetSdkVersion < Build.VERSION_CODES.Q) {
+ maybeTruncate(pfd.getFileDescriptor(), mode);
+ }
+
return new AssetFileDescriptor(pfd, fd.getStartOffset(),
fd.getDeclaredLength());
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index b498325..7c7cfdb 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6397,7 +6397,9 @@
public static final int FLAG_ACTIVITY_NO_HISTORY = 0x40000000;
/**
* If set, the activity will not be launched if it is already running
- * at the top of the history stack.
+ * at the top of the history stack. See
+ * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html#TaskLaunchModes">
+ * Tasks and Back Stack</a> for more information.
*/
public static final int FLAG_ACTIVITY_SINGLE_TOP = 0x20000000;
/**
@@ -6537,8 +6539,7 @@
public static final int FLAG_ACTIVITY_RESET_TASK_IF_NEEDED = 0x00200000;
/**
* This flag is not normally set by application code, but set for you by
- * the system if this activity is being launched from history
- * (longpress home key).
+ * the system if this activity is being launched from history.
*/
public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 0x00100000;
/**
@@ -6571,7 +6572,9 @@
* equivalent of the Activity manifest specifying {@link
* android.R.attr#documentLaunchMode}="intoExisting". When used with
* FLAG_ACTIVITY_MULTIPLE_TASK it is the equivalent of the Activity manifest specifying
- * {@link android.R.attr#documentLaunchMode}="always".
+ * {@link android.R.attr#documentLaunchMode}="always". The flag is ignored even in
+ * conjunction with {@link #FLAG_ACTIVITY_MULTIPLE_TASK} when the Activity manifest specifies
+ * {@link android.R.attr#documentLaunchMode}="never".
*
* Refer to {@link android.R.attr#documentLaunchMode} for more information.
*
diff --git a/core/java/android/content/pm/OWNERS b/core/java/android/content/pm/OWNERS
index f0def805..4e674f6 100644
--- a/core/java/android/content/pm/OWNERS
+++ b/core/java/android/content/pm/OWNERS
@@ -4,7 +4,8 @@
toddke@google.com
patb@google.com
-per-file PackageParser.java = chiuwinson@google.com
+per-file PackageParser.java = set noparent
+per-file PackageParser.java = chiuwinson@google.com,patb@google.com,toddke@google.com
per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
per-file AppSearchPerson.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS
per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 86a8a9d..4ff2624 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -214,7 +214,6 @@
public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
public static final String METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY =
"android.activity_window_layout_affinity";
- public static final String METADATA_ACTIVITY_LAUNCH_MODE = "android.activity.launch_mode";
/**
* Bit mask of all the valid bits that can be set in recreateOnConfigChanges.
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 0fc6b2b..e3aca97 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -183,6 +183,7 @@
public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
public static final String METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY =
"android.activity_window_layout_affinity";
+ public static final String METADATA_ACTIVITY_LAUNCH_MODE = "android.activity.launch_mode";
public static final int SDK_VERSION = Build.VERSION.SDK_INT;
public static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index ff6aaad..aa740bd 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -24,7 +24,6 @@
import android.app.ActivityTaskManager;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.content.pm.PackageParser;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.ParsingUtils;
@@ -414,9 +413,9 @@
if (!isAlias && activity.launchMode != LAUNCH_SINGLE_INSTANCE_PER_TASK
&& activity.metaData != null && activity.metaData.containsKey(
- PackageParser.METADATA_ACTIVITY_LAUNCH_MODE)) {
+ ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE)) {
final String launchMode = activity.metaData.getString(
- PackageParser.METADATA_ACTIVITY_LAUNCH_MODE);
+ ParsingPackageUtils.METADATA_ACTIVITY_LAUNCH_MODE);
if (launchMode != null && launchMode.equals("singleInstancePerTask")) {
activity.launchMode = LAUNCH_SINGLE_INSTANCE_PER_TASK;
}
diff --git a/core/java/android/hardware/Battery.java b/core/java/android/hardware/BatteryState.java
similarity index 91%
rename from core/java/android/hardware/Battery.java
rename to core/java/android/hardware/BatteryState.java
index 24c8d76..aa75359 100644
--- a/core/java/android/hardware/Battery.java
+++ b/core/java/android/hardware/BatteryState.java
@@ -24,9 +24,9 @@
import java.lang.annotation.RetentionPolicy;
/**
- * The Battery class is a representation of a single battery on a device.
+ * The BatteryState class is a representation of a single battery on a device.
*/
-public abstract class Battery {
+public abstract class BatteryState {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "STATUS_" }, value = {
@@ -55,7 +55,7 @@
*
* @return True if the hardware has a battery, else false.
*/
- public abstract boolean hasBattery();
+ public abstract boolean isPresent();
/**
* Get the battery status.
@@ -66,7 +66,7 @@
/**
* Get remaining battery capacity as float percentage [0.0f, 1.0f] of total capacity
- * Returns -1 when battery capacity can't be read.
+ * Returns NaN when battery capacity can't be read.
*
* @return the battery capacity.
*/
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 948da3f..f3a8342 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -34,6 +34,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.util.Slog;
@@ -557,7 +558,7 @@
* @hide
*/
public long[] getAuthenticatorIds() {
- return getAuthenticatorIds(UserHandle.getCallingUserId());
+ return getAuthenticatorIds(UserHandle.myUserId());
}
/**
diff --git a/core/java/android/hardware/input/InputDeviceBattery.java b/core/java/android/hardware/input/InputDeviceBatteryState.java
similarity index 84%
rename from core/java/android/hardware/input/InputDeviceBattery.java
rename to core/java/android/hardware/input/InputDeviceBatteryState.java
index 0fe124e..d069eb0 100644
--- a/core/java/android/hardware/input/InputDeviceBattery.java
+++ b/core/java/android/hardware/input/InputDeviceBatteryState.java
@@ -19,28 +19,28 @@
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
import static android.os.IInputConstants.INVALID_BATTERY_CAPACITY;
-import android.hardware.Battery;
+import android.hardware.BatteryState;
/**
* Battery implementation for input devices.
*
* @hide
*/
-public final class InputDeviceBattery extends Battery {
- private static final float NULL_BATTERY_CAPACITY = -1.0f;
+public final class InputDeviceBatteryState extends BatteryState {
+ private static final float NULL_BATTERY_CAPACITY = Float.NaN;
private final InputManager mInputManager;
private final int mDeviceId;
private final boolean mHasBattery;
- InputDeviceBattery(InputManager inputManager, int deviceId, boolean hasBattery) {
+ InputDeviceBatteryState(InputManager inputManager, int deviceId, boolean hasBattery) {
mInputManager = inputManager;
mDeviceId = deviceId;
mHasBattery = hasBattery;
}
@Override
- public boolean hasBattery() {
+ public boolean isPresent() {
return mHasBattery;
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 648fda7..51d196d 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1571,12 +1571,12 @@
}
/**
- * Gets a battery object associated with an input device, assuming it has one.
+ * Gets a battery state object associated with an input device, assuming it has one.
* @return The battery, never null.
* @hide
*/
- public InputDeviceBattery getInputDeviceBattery(int deviceId, boolean hasBattery) {
- return new InputDeviceBattery(this, deviceId, hasBattery);
+ public InputDeviceBatteryState getInputDeviceBatteryState(int deviceId, boolean hasBattery) {
+ return new InputDeviceBatteryState(this, deviceId, hasBattery);
}
/**
diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java
index 2c78fcb..2812868 100644
--- a/core/java/android/hardware/lights/Light.java
+++ b/core/java/android/hardware/lights/Light.java
@@ -141,6 +141,11 @@
return mId;
}
+ @Override
+ public String toString() {
+ return "[Name=" + mName + " Id=" + mId + " Type=" + mType + " Ordinal=" + mOrdinal + "]";
+ }
+
/**
* Returns the id of the light.
*
diff --git a/core/java/android/hardware/lights/LightsManager.java b/core/java/android/hardware/lights/LightsManager.java
index cbcef86..2d9bc0e 100644
--- a/core/java/android/hardware/lights/LightsManager.java
+++ b/core/java/android/hardware/lights/LightsManager.java
@@ -113,6 +113,12 @@
private final IBinder mToken = new Binder();
/**
+ * @hide to prevent subclassing from outside of the framework
+ */
+ public LightsSession() {
+ }
+
+ /**
* Sends a request to modify the states of multiple lights.
*
* @param request the settings for lights that should change
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 4defc55..d7b96df 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1947,12 +1947,12 @@
public void showStatusIcon(@DrawableRes int iconResId) {
mStatusIcon = iconResId;
- mPrivOps.updateStatusIcon(getPackageName(), iconResId);
+ mPrivOps.updateStatusIconAsync(getPackageName(), iconResId);
}
public void hideStatusIcon() {
mStatusIcon = 0;
- mPrivOps.updateStatusIcon(null, 0);
+ mPrivOps.updateStatusIconAsync(null, 0);
}
/**
@@ -2312,7 +2312,7 @@
if (setVisible) {
cancelImeSurfaceRemoval();
}
- mPrivOps.applyImeVisibility(setVisible
+ mPrivOps.applyImeVisibilityAsync(setVisible
? mCurShowInputToken : mCurHideInputToken, setVisible);
}
@@ -3319,7 +3319,7 @@
if (mNotifyUserActionSent) {
return;
}
- mPrivOps.notifyUserAction();
+ mPrivOps.notifyUserActionAsync();
mNotifyUserActionSent = true;
}
}
diff --git a/core/java/android/net/UnderlyingNetworkInfo.java b/core/java/android/net/UnderlyingNetworkInfo.java
index 459fdac..33f9375 100644
--- a/core/java/android/net/UnderlyingNetworkInfo.java
+++ b/core/java/android/net/UnderlyingNetworkInfo.java
@@ -71,13 +71,13 @@
/** Get the interface name of this network. */
@NonNull
- public String getIface() {
+ public String getInterface() {
return mIface;
}
/** Get the names of the interfaces underlying this network. */
@NonNull
- public List<String> getUnderlyingIfaces() {
+ public List<String> getUnderlyingInterfaces() {
return mUnderlyingIfaces;
}
@@ -124,8 +124,8 @@
if (!(o instanceof UnderlyingNetworkInfo)) return false;
final UnderlyingNetworkInfo that = (UnderlyingNetworkInfo) o;
return mOwnerUid == that.getOwnerUid()
- && Objects.equals(mIface, that.getIface())
- && Objects.equals(mUnderlyingIfaces, that.getUnderlyingIfaces());
+ && Objects.equals(mIface, that.getInterface())
+ && Objects.equals(mUnderlyingIfaces, that.getUnderlyingInterfaces());
}
@Override
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index eedeeb5..f02346b 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -141,7 +141,7 @@
* <p>To ensure the device is not constantly being woken up, this retry interval MUST be greater
* than this value.
*
- * @see {@link Builder#setRetryIntervalsMs()}
+ * @see {@link Builder#setRetryIntervalsMillis()}
*/
private static final long MINIMUM_REPEATING_RETRY_INTERVAL_MS = TimeUnit.MINUTES.toMillis(15);
@@ -342,10 +342,10 @@
/**
* Retrieves the configured retry intervals.
*
- * @see Builder#setRetryIntervalsMs(long[])
+ * @see Builder#setRetryIntervalsMillis(long[])
*/
@NonNull
- public long[] getRetryIntervalsMs() {
+ public long[] getRetryIntervalsMillis() {
return Arrays.copyOf(mRetryIntervalsMs, mRetryIntervalsMs.length);
}
@@ -555,7 +555,7 @@
* @see VcnManager for additional discussion on fail-safe mode
*/
@NonNull
- public Builder setRetryIntervalsMs(@NonNull long[] retryIntervalsMs) {
+ public Builder setRetryIntervalsMillis(@NonNull long[] retryIntervalsMs) {
validateRetryInterval(retryIntervalsMs);
mRetryIntervalsMs = retryIntervalsMs;
diff --git a/core/java/android/net/vcn/VcnTransportInfo.java b/core/java/android/net/vcn/VcnTransportInfo.java
index 4d8cf91..0e9ccf1 100644
--- a/core/java/android/net/vcn/VcnTransportInfo.java
+++ b/core/java/android/net/vcn/VcnTransportInfo.java
@@ -16,14 +16,23 @@
package android.net.vcn;
+import static android.net.NetworkCapabilities.REDACT_ALL;
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.net.NetworkCapabilities;
import android.net.TransportInfo;
import android.net.wifi.WifiInfo;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.SubscriptionManager;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.Objects;
/**
@@ -37,28 +46,41 @@
* SubscriptionManager#INVALID_SUBSCRIPTION_ID}. If the underlying Network is Cellular, the WifiInfo
* will be {@code null}.
*
+ * <p>Receipt of a VcnTransportInfo requires the NETWORK_SETTINGS permission; else the entire
+ * VcnTransportInfo instance will be redacted.
+ *
* @hide
*/
public class VcnTransportInfo implements TransportInfo, Parcelable {
@Nullable private final WifiInfo mWifiInfo;
private final int mSubId;
+ /**
+ * The redaction scheme to use when parcelling.
+ *
+ * <p>The TransportInfo/NetworkCapabilities redaction mechanisms rely on redaction being
+ * performed at parcelling time. This means that the redaction scheme must be stored for later
+ * use.
+ *
+ * <p>Since the redaction scheme itself is not parcelled, this field is listed as a transient.
+ *
+ * <p>Defaults to REDACT_ALL when constructed using public constructors, or creating from
+ * parcels.
+ */
+ private final transient long mRedactions;
+
public VcnTransportInfo(@NonNull WifiInfo wifiInfo) {
- this(wifiInfo, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ this(wifiInfo, INVALID_SUBSCRIPTION_ID, REDACT_ALL);
}
public VcnTransportInfo(int subId) {
- this(null /* wifiInfo */, subId);
+ this(null /* wifiInfo */, subId, REDACT_ALL);
}
- private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId) {
- if (wifiInfo == null && subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- throw new IllegalArgumentException(
- "VcnTransportInfo requires either non-null WifiInfo or valid subId");
- }
-
+ private VcnTransportInfo(@Nullable WifiInfo wifiInfo, int subId, long redactions) {
mWifiInfo = wifiInfo;
mSubId = subId;
+ mRedactions = redactions;
}
/**
@@ -86,8 +108,19 @@
return mSubId;
}
+ /**
+ * Gets the redaction scheme
+ *
+ * @hide
+ */
+ @VisibleForTesting(visibility = PRIVATE)
+ public long getRedaction() {
+ return mRedactions;
+ }
+
@Override
public int hashCode() {
+ // mRedactions not hashed, as it is a transient, for control of parcelling
return Objects.hash(mWifiInfo, mSubId);
}
@@ -96,6 +129,7 @@
if (!(o instanceof VcnTransportInfo)) return false;
final VcnTransportInfo that = (VcnTransportInfo) o;
+ // mRedactions not compared, as it is a transient, for control of parcelling
return Objects.equals(mWifiInfo, that.mWifiInfo) && mSubId == that.mSubId;
}
@@ -105,17 +139,59 @@
return 0;
}
+ @Override
+ @NonNull
+ public TransportInfo makeCopy(long redactions) {
+ return new VcnTransportInfo(
+ mWifiInfo == null ? null : mWifiInfo.makeCopy(redactions), mSubId, redactions);
+ }
+
+ @Override
+ public long getApplicableRedactions() {
+ long redactions = REDACT_FOR_NETWORK_SETTINGS;
+
+ // Add additional wifi redactions if necessary
+ if (mWifiInfo != null) {
+ redactions |= mWifiInfo.getApplicableRedactions();
+ }
+
+ return redactions;
+ }
+
+ private boolean shouldParcelNetworkSettingsFields() {
+ return (mRedactions & NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS) == 0;
+ }
+
/** {@inheritDoc} */
@Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {}
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(shouldParcelNetworkSettingsFields() ? mSubId : INVALID_SUBSCRIPTION_ID);
+ dest.writeParcelable(
+ shouldParcelNetworkSettingsFields() ? (Parcelable) mWifiInfo : null, flags);
+ }
+
+ @Override
+ public String toString() {
+ return "VcnTransportInfo { mWifiInfo = " + mWifiInfo + ", mSubId = " + mSubId + " }";
+ }
/** Implement the Parcelable interface */
public static final @NonNull Creator<VcnTransportInfo> CREATOR =
new Creator<VcnTransportInfo>() {
public VcnTransportInfo createFromParcel(Parcel in) {
- // return null instead of a default VcnTransportInfo to avoid leaking
- // information about this being a VCN Network (instead of macro cellular, etc)
- return null;
+ final int subId = in.readInt();
+ final WifiInfo wifiInfo = in.readParcelable(null);
+
+ // If all fields are their null values, return null TransportInfo to avoid
+ // leaking information about this being a VCN Network (instead of macro
+ // cellular, etc)
+ if (wifiInfo == null && subId == INVALID_SUBSCRIPTION_ID) {
+ return null;
+ }
+
+ // Prevent further forwarding by redacting everything in future parcels from
+ // this VcnTransportInfo
+ return new VcnTransportInfo(wifiInfo, subId, REDACT_ALL);
}
public VcnTransportInfo[] newArray(int size) {
diff --git a/core/java/android/nfc/NfcControllerAlwaysOnListener.java b/core/java/android/nfc/NfcControllerAlwaysOnListener.java
index 96707bb..6ae58fd 100644
--- a/core/java/android/nfc/NfcControllerAlwaysOnListener.java
+++ b/core/java/android/nfc/NfcControllerAlwaysOnListener.java
@@ -52,6 +52,14 @@
*/
public void register(@NonNull Executor executor,
@NonNull ControllerAlwaysOnListener listener) {
+ try {
+ if (!mAdapter.isControllerAlwaysOnSupported()) {
+ return;
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to register");
+ return;
+ }
synchronized (this) {
if (mListenerMap.containsKey(listener)) {
return;
@@ -75,6 +83,14 @@
* @param listener user implementation of the {@link ControllerAlwaysOnListener}
*/
public void unregister(@NonNull ControllerAlwaysOnListener listener) {
+ try {
+ if (!mAdapter.isControllerAlwaysOnSupported()) {
+ return;
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to unregister");
+ return;
+ }
synchronized (this) {
if (!mListenerMap.containsKey(listener)) {
return;
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index c084787..0c9ad1b 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -31,6 +31,7 @@
import static android.system.OsConstants.S_ISREG;
import static android.system.OsConstants.S_IWOTH;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -63,6 +64,8 @@
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.UncheckedIOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.net.DatagramSocket;
import java.net.Socket;
import java.nio.ByteOrder;
@@ -110,6 +113,20 @@
private final CloseGuard mGuard = CloseGuard.get();
+ /** @hide */
+ @IntDef(prefix = {"MODE_"}, value = {
+ MODE_WORLD_READABLE,
+ MODE_WORLD_WRITEABLE,
+ MODE_READ_ONLY,
+ MODE_WRITE_ONLY,
+ MODE_READ_WRITE,
+ MODE_CREATE,
+ MODE_TRUNCATE,
+ MODE_APPEND,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Mode { }
+
/**
* For use with {@link #open}: if {@link #MODE_CREATE} has been supplied and
* this file doesn't already exist, then create the file with permissions
@@ -227,7 +244,8 @@
* be opened with the requested mode.
* @see #parseMode(String)
*/
- public static ParcelFileDescriptor open(File file, int mode) throws FileNotFoundException {
+ public static ParcelFileDescriptor open(File file, @Mode int mode)
+ throws FileNotFoundException {
final FileDescriptor fd = openInternal(file, mode);
if (fd == null) return null;
@@ -259,7 +277,7 @@
// We can't accept a generic Executor here, since we need to use
// MessageQueue.addOnFileDescriptorEventListener()
@SuppressLint("ExecutorRegistration")
- public static ParcelFileDescriptor open(File file, int mode, Handler handler,
+ public static ParcelFileDescriptor open(File file, @Mode int mode, Handler handler,
final OnCloseListener listener) throws IOException {
if (handler == null) {
throw new IllegalArgumentException("Handler must not be null");
@@ -330,7 +348,8 @@
return pfd;
}
- private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException {
+ private static FileDescriptor openInternal(File file, @Mode int mode)
+ throws FileNotFoundException {
final int flags = FileUtils.translateModePfdToPosix(mode) | ifAtLeastQ(O_CLOEXEC);
int realMode = S_IRWXU | S_IRWXG;
@@ -623,15 +642,36 @@
}
/**
- * Converts a string representing a file mode, such as "rw", into a bitmask suitable for use
- * with {@link #open}.
+ * Converts a string representing a file mode, such as "rw", into a bitmask
+ * suitable for use with {@link #open}.
* <p>
- * @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt".
+ * The argument must define at least one of the following base access modes:
+ * <ul>
+ * <li>"r" indicates the file should be opened in read-only mode, equivalent
+ * to {@link OsConstants#O_RDONLY}.
+ * <li>"w" indicates the file should be opened in write-only mode,
+ * equivalent to {@link OsConstants#O_WRONLY}.
+ * <li>"rw" indicates the file should be opened in read-write mode,
+ * equivalent to {@link OsConstants#O_RDWR}.
+ * </ul>
+ * In addition to a base access mode, the following additional modes may
+ * requested:
+ * <ul>
+ * <li>"a" indicates the file should be opened in append mode, equivalent to
+ * {@link OsConstants#O_APPEND}. Before each write, the file offset is
+ * positioned at the end of the file.
+ * <li>"t" indicates the file should be opened in truncate mode, equivalent
+ * to {@link OsConstants#O_TRUNC}. If the file already exists and is a
+ * regular file and is opened for writing, it will be truncated to length 0.
+ * </ul>
+ *
+ * @param mode The string representation of the file mode. Can be "r", "w",
+ * "wt", "wa", "rw" or "rwt".
* @return A bitmask representing the given file mode.
- * @throws IllegalArgumentException if the given string does not match a known file mode.
+ * @throws IllegalArgumentException if the given string does not match a
+ * known file mode.
*/
- public static int parseMode(String mode) {
+ public static @Mode int parseMode(String mode) {
return FileUtils.translateModePosixToPfd(FileUtils.translateModeStringToPosix(mode));
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 98abd96..c8e5e42 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8515,6 +8515,12 @@
"swipe_bottom_to_notification_enabled";
/**
+ * Controls whether One-Handed mode is currently activated.
+ * @hide
+ */
+ public static final String ONE_HANDED_MODE_ACTIVATED = "one_handed_mode_activated";
+
+ /**
* For user preference if One-Handed Mode enabled.
* @hide
*/
diff --git a/core/java/android/service/displayhash/DisplayHashParams.java b/core/java/android/service/displayhash/DisplayHashParams.java
index 2ec9d5d..123c209 100644
--- a/core/java/android/service/displayhash/DisplayHashParams.java
+++ b/core/java/android/service/displayhash/DisplayHashParams.java
@@ -48,7 +48,7 @@
/**
* Whether the content will be captured in grayscale or color.
*/
- private final boolean mUseGrayscale;
+ private final boolean mGrayscaleBuffer;
/**
* A builder for {@link DisplayHashParams}
@@ -56,7 +56,7 @@
public static final class Builder {
@Nullable
private Size mBufferSize;
- private boolean mUseGrayscale;
+ private boolean mGrayscaleBuffer;
/**
* Creates a new Builder.
@@ -77,15 +77,15 @@
* Whether the content will be captured in grayscale or color.
*/
@NonNull
- public Builder setUseGrayscale(boolean useGrayscale) {
- mUseGrayscale = useGrayscale;
+ public Builder setGrayscaleBuffer(boolean grayscaleBuffer) {
+ mGrayscaleBuffer = grayscaleBuffer;
return this;
}
/** Builds the instance. This builder should not be touched after calling this! */
@NonNull
public DisplayHashParams build() {
- return new DisplayHashParams(mBufferSize, mUseGrayscale);
+ return new DisplayHashParams(mBufferSize, mGrayscaleBuffer);
}
}
@@ -112,16 +112,16 @@
* buffer given to the {@link DisplayHashingService#onGenerateDisplayHash(byte[],
* HardwareBuffer, Rect, String, DisplayHashResultCallback)} will be stretched based on the
* value set here. If {@code null}, the buffer size will not be changed.
- * @param useGrayscale
+ * @param grayscaleBuffer
* Whether the content will be captured in grayscale or color.
* @hide
*/
@DataClass.Generated.Member
public DisplayHashParams(
@Nullable Size bufferSize,
- boolean useGrayscale) {
+ boolean grayscaleBuffer) {
this.mBufferSize = bufferSize;
- this.mUseGrayscale = useGrayscale;
+ this.mGrayscaleBuffer = grayscaleBuffer;
// onConstructed(); // You can define this method to get a callback
}
@@ -141,8 +141,8 @@
* Whether the content will be captured in grayscale or color.
*/
@DataClass.Generated.Member
- public boolean isUseGrayscale() {
- return mUseGrayscale;
+ public boolean isGrayscaleBuffer() {
+ return mGrayscaleBuffer;
}
@Override
@@ -153,7 +153,7 @@
return "DisplayHashParams { " +
"bufferSize = " + mBufferSize + ", " +
- "useGrayscale = " + mUseGrayscale +
+ "grayscaleBuffer = " + mGrayscaleBuffer +
" }";
}
@@ -164,7 +164,7 @@
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
- if (mUseGrayscale) flg |= 0x2;
+ if (mGrayscaleBuffer) flg |= 0x2;
if (mBufferSize != null) flg |= 0x1;
dest.writeByte(flg);
if (mBufferSize != null) dest.writeSize(mBufferSize);
@@ -182,11 +182,11 @@
// static FieldType unparcelFieldName(Parcel in) { ... }
byte flg = in.readByte();
- boolean useGrayscale = (flg & 0x2) != 0;
+ boolean grayscaleBuffer = (flg & 0x2) != 0;
Size bufferSize = (flg & 0x1) == 0 ? null : (Size) in.readSize();
this.mBufferSize = bufferSize;
- this.mUseGrayscale = useGrayscale;
+ this.mGrayscaleBuffer = grayscaleBuffer;
// onConstructed(); // You can define this method to get a callback
}
@@ -206,10 +206,10 @@
};
@DataClass.Generated(
- time = 1618436855096L,
+ time = 1619638159453L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/displayhash/DisplayHashParams.java",
- inputSignatures = "private final @android.annotation.Nullable android.util.Size mBufferSize\nprivate final boolean mUseGrayscale\nclass DisplayHashParams extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.Nullable android.util.Size mBufferSize\nprivate boolean mUseGrayscale\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setBufferSize(int,int)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setUseGrayscale(boolean)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genAidl=true, genToString=true, genParcelable=true, genHiddenConstructor=true)")
+ inputSignatures = "private final @android.annotation.Nullable android.util.Size mBufferSize\nprivate final boolean mGrayscaleBuffer\nclass DisplayHashParams extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.Nullable android.util.Size mBufferSize\nprivate boolean mGrayscaleBuffer\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setBufferSize(int,int)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams.Builder setGrayscaleBuffer(boolean)\npublic @android.annotation.NonNull android.service.displayhash.DisplayHashParams build()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genAidl=true, genToString=true, genParcelable=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/rotationresolver/RotationResolverService.java b/core/java/android/service/rotationresolver/RotationResolverService.java
index 604dd0a..0932901 100644
--- a/core/java/android/service/rotationresolver/RotationResolverService.java
+++ b/core/java/android/service/rotationresolver/RotationResolverService.java
@@ -136,9 +136,6 @@
@MainThread
private void resolveRotation(IRotationResolverCallback callback,
RotationResolutionRequest request, ICancellationSignal transport) {
- // TODO(b/175334753): The current behavior of preempted failures is based on the design we
- // had in Smart OS exploration. We should revisit it once we have implemented the whole
- // feature and tested on devices.
if (mPendingCallback != null
&& (mCancellationSignal == null || !mCancellationSignal.isCanceled())) {
reportFailures(callback, ROTATION_RESULT_FAILURE_PREEMPTED);
diff --git a/core/java/android/service/search/SearchUiService.java b/core/java/android/service/search/SearchUiService.java
index d0e4195..c04dd9c 100644
--- a/core/java/android/service/search/SearchUiService.java
+++ b/core/java/android/service/search/SearchUiService.java
@@ -45,6 +45,9 @@
* A service used to share the lifecycle of search UI (open, close, interaction)
* and also to return search result on a query.
*
+ * To understand the lifecycle of search session and how a query get issued,
+ * {@see SearchSession}
+ *
* @hide
*/
@SystemApi
@@ -71,6 +74,10 @@
@Override
public void onCreateSearchSession(SearchContext context, SearchSessionId sessionId) {
mHandler.sendMessage(
+ obtainMessage(SearchUiService::onSearchSessionCreated,
+ SearchUiService.this, context, sessionId));
+ // to be removed
+ mHandler.sendMessage(
obtainMessage(SearchUiService::onCreateSearchSession,
SearchUiService.this, context, sessionId));
}
@@ -119,11 +126,24 @@
/**
* Creates a new search session.
+ *
+ * @deprecated this is method will be removed as soon as
+ * {@link #onSearchSessionCreated(SearchContext, SearchSessionId)}
+ * is adopted by the service.
+ *
+ * @removed
*/
+ @Deprecated
public void onCreateSearchSession(@NonNull SearchContext context,
@NonNull SearchSessionId sessionId) {}
/**
+ * A new search session is created.
+ */
+ public void onSearchSessionCreated(@NonNull SearchContext context,
+ @NonNull SearchSessionId sessionId) {}
+
+ /**
* Called by the client to request search results using a query string.
*/
@MainThread
@@ -132,7 +152,10 @@
@NonNull Consumer<List<SearchTarget>> callback);
/**
- * Called by a client to indicate an interaction (tap, long press, drag, etc) on target(s).
+ * Called by a client to indicate an interaction (tap, long press, drag, etc) on target(s)
+ * and lifecycle event on the search surface (e.g., visibility change).
+ *
+ * {@see SearchTargetEvent}
*/
@MainThread
public abstract void onNotifyEvent(@NonNull SearchSessionId sessionId,
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 3b1c8ec..4f1354d 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -22,7 +22,7 @@
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
-import android.hardware.Battery;
+import android.hardware.BatteryState;
import android.hardware.SensorManager;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
@@ -88,7 +88,7 @@
private SensorManager mSensorManager;
@GuardedBy("mMotionRanges")
- private Battery mBattery;
+ private BatteryState mBatteryState;
@GuardedBy("mMotionRanges")
private LightsManager mLightsManager;
@@ -848,19 +848,19 @@
}
/**
- * Gets the battery object associated with the device, if there is one.
+ * Gets the battery state object associated with the device, if there is one.
* Even if the device does not have a battery, the result is never null.
- * Use {@link Battery#hasBattery} to determine whether a battery is
+ * Use {@link BatteryState#isPresent} to determine whether a battery is
* present.
*
* @return The battery object associated with the device, never null.
*/
@NonNull
- public Battery getBattery() {
- if (mBattery == null) {
- mBattery = InputManager.getInstance().getInputDeviceBattery(mId, mHasBattery);
+ public BatteryState getBatteryState() {
+ if (mBatteryState == null) {
+ mBatteryState = InputManager.getInstance().getInputDeviceBatteryState(mId, mHasBattery);
}
- return mBattery;
+ return mBatteryState;
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5a738eb..0958f3f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5191,6 +5191,17 @@
@Override
public void handleMessage(Message msg) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, getMessageName(msg));
+ }
+ try {
+ handleMessageImpl(msg);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ }
+
+ private void handleMessageImpl(Message msg) {
switch (msg.what) {
case MSG_INVALIDATE:
((View) msg.obj).invalidate();
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index b4c1dd2..20a0b0b 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1406,7 +1406,8 @@
*/
@Deprecated
public void showStatusIcon(IBinder imeToken, String packageName, @DrawableRes int iconId) {
- InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIcon(packageName, iconId);
+ InputMethodPrivilegedOperationsRegistry.get(
+ imeToken).updateStatusIconAsync(packageName, iconId);
}
/**
@@ -1416,7 +1417,7 @@
*/
@Deprecated
public void hideStatusIcon(IBinder imeToken) {
- InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIcon(null, 0);
+ InputMethodPrivilegedOperationsRegistry.get(imeToken).updateStatusIconAsync(null, 0);
}
/**
diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java
index b0d95b3..8dbce1b 100644
--- a/core/java/android/view/translation/Translator.java
+++ b/core/java/android/view/translation/Translator.java
@@ -254,6 +254,11 @@
}
/** @hide */
+ public TranslationContext getTranslationContext() {
+ return mTranslationContext;
+ }
+
+ /** @hide */
public int getTranslatorId() {
return mId;
}
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 0425572..bdf217d 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -451,13 +451,14 @@
int[] supportedFormats = getSupportedFormatsLocked();
ArrayList<ViewRootImpl> roots =
WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
+ TranslationCapability capability =
+ getTranslationCapability(translator.getTranslationContext());
mActivity.runOnUiThread(() -> {
// traverse the hierarchy to collect ViewTranslationRequests
for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
View rootView = roots.get(rootNum).getView();
- // TODO(b/183589662): call getTranslationCapabilities() for capability
- rootView.dispatchRequestTranslation(viewIds, supportedFormats, /* capability */
- null, requests);
+ rootView.dispatchRequestTranslation(viewIds, supportedFormats, capability,
+ requests);
}
mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
UiTranslationController::sendTranslationRequest,
@@ -487,6 +488,15 @@
return new int[] {TranslationSpec.DATA_FORMAT_TEXT};
}
+ private TranslationCapability getTranslationCapability(TranslationContext translationContext) {
+ // We only support text to text capability now, we will query real status from service when
+ // we support more translation capabilities.
+ return new TranslationCapability(TranslationCapability.STATE_ON_DEVICE,
+ translationContext.getSourceSpec(),
+ translationContext.getTargetSpec(), /* uiTranslationEnabled= */ true,
+ /* supportedTranslationFlags= */ 0);
+ }
+
private void findViewsTraversalByAutofillIds(IntArray sourceViewIds) {
final ArrayList<ViewRootImpl> roots =
WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
diff --git a/core/java/android/webkit/WebViewDelegate.java b/core/java/android/webkit/WebViewDelegate.java
index c7eac6c..6b49569 100644
--- a/core/java/android/webkit/WebViewDelegate.java
+++ b/core/java/android/webkit/WebViewDelegate.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -226,6 +227,7 @@
* WebViewChromiumFactoryProvider#create method was invoked.
*/
@NonNull
+ @ElapsedRealtimeLong
public long[] getTimestamps() {
return WebViewFactory.getTimestamps();
}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index bc2b221..5fc5b29 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -83,16 +83,27 @@
public @interface Timestamp {
}
+ /** When the overall WebView provider load began. */
public static final int WEBVIEW_LOAD_START = 0;
+ /** Before creating the WebView APK Context. */
public static final int CREATE_CONTEXT_START = 1;
+ /** After creating the WebView APK Context. */
public static final int CREATE_CONTEXT_END = 2;
+ /** Before adding WebView assets to AssetManager. */
public static final int ADD_ASSETS_START = 3;
+ /** After adding WebView assets to AssetManager. */
public static final int ADD_ASSETS_END = 4;
+ /** Before creating the WebView ClassLoader. */
public static final int GET_CLASS_LOADER_START = 5;
+ /** After creating the WebView ClassLoader. */
public static final int GET_CLASS_LOADER_END = 6;
+ /** Before preloading the WebView native library. */
public static final int NATIVE_LOAD_START = 7;
+ /** After preloading the WebView native library. */
public static final int NATIVE_LOAD_END = 8;
+ /** Before looking up the WebView provider class. */
public static final int PROVIDER_CLASS_FOR_NAME_START = 9;
+ /** After looking up the WebView provider class. */
public static final int PROVIDER_CLASS_FOR_NAME_END = 10;
private static final int TIMESTAMPS_SIZE = 11;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 166411e..e06d5f0 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -259,6 +259,20 @@
// Used to highlight a word when it is corrected by the IME
private CorrectionHighlighter mCorrectionHighlighter;
+ /**
+ * {@code true} when {@link TextView#setText(CharSequence, TextView.BufferType, boolean, int)}
+ * is being executed and {@link InputMethodManager#restartInput(View)} is scheduled to be
+ * called.
+ *
+ * <p>This is also used to avoid an unnecessary invocation of
+ * {@link InputMethodManager#updateSelection(View, int, int, int, int)} when
+ * {@link InputMethodManager#restartInput(View)} is scheduled to be called already
+ * See bug 186582769 for details.</p>
+ *
+ * <p>TODO(186582769): Come up with better way.</p>
+ */
+ private boolean mHasPendingRestartInputForSetText = false;
+
InputContentType mInputContentType;
InputMethodState mInputMethodState;
@@ -1825,6 +1839,29 @@
}
}
+ /**
+ * Called from {@link TextView#setText(CharSequence, TextView.BufferType, boolean, int)} to
+ * schedule {@link InputMethodManager#restartInput(View)}.
+ */
+ void scheduleRestartInputForSetText() {
+ mHasPendingRestartInputForSetText = true;
+ }
+
+ /**
+ * Called from {@link TextView#setText(CharSequence, TextView.BufferType, boolean, int)} to
+ * actually call {@link InputMethodManager#restartInput(View)} if it's scheduled. Does nothing
+ * otherwise.
+ */
+ void maybeFireScheduledRestartInputForSetText() {
+ if (mHasPendingRestartInputForSetText) {
+ final InputMethodManager imm = getInputMethodManager();
+ if (imm != null) {
+ imm.restartInput(mTextView);
+ }
+ mHasPendingRestartInputForSetText = false;
+ }
+ }
+
static final int EXTRACT_NOTHING = -2;
static final int EXTRACT_UNKNOWN = -1;
@@ -1958,7 +1995,8 @@
}
private void sendUpdateSelection() {
- if (null != mInputMethodState && mInputMethodState.mBatchEditNesting <= 0) {
+ if (null != mInputMethodState && mInputMethodState.mBatchEditNesting <= 0
+ && !mHasPendingRestartInputForSetText) {
final InputMethodManager imm = getInputMethodManager();
if (null != imm) {
final int selectionStart = mTextView.getSelectionStart();
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 105c714..b5a5848 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -706,8 +706,7 @@
if (getChildCount() == 0) {
return false;
}
- if ((mIsBeingDragged = !mScroller.isFinished() || !mEdgeGlowRight.isFinished()
- || !mEdgeGlowLeft.isFinished())) {
+ if (!mScroller.isFinished()) {
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 65f3da7..2dd7f02 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -763,8 +763,7 @@
if (getChildCount() == 0) {
return false;
}
- if ((mIsBeingDragged = !mScroller.isFinished() || !mEdgeGlowTop.isFinished()
- || !mEdgeGlowBottom.isFinished())) {
+ if (!mScroller.isFinished()) {
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 07a9a5fa..1a37b59 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6285,11 +6285,10 @@
|| needEditableForNotification) {
createEditorIfNeeded();
mEditor.forgetUndoRedo();
+ mEditor.scheduleRestartInputForSetText();
Editable t = mEditableFactory.newEditable(text);
text = t;
setFilters(t, mFilters);
- InputMethodManager imm = getInputMethodManager();
- if (imm != null) imm.restartInput(this);
} else if (precomputed != null) {
if (mTextDir == null) {
mTextDir = getTextDirectionHeuristic();
@@ -6408,8 +6407,12 @@
notifyListeningManagersAfterTextChanged();
}
- // SelectionModifierCursorController depends on textCanBeSelected, which depends on text
- if (mEditor != null) mEditor.prepareCursorControllers();
+ if (mEditor != null) {
+ // SelectionModifierCursorController depends on textCanBeSelected, which depends on text
+ mEditor.prepareCursorControllers();
+
+ mEditor.maybeFireScheduledRestartInputForSetText();
+ }
}
/**
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
index 9d06bb9..f2d91ba 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -203,34 +203,33 @@
final InvisibleToggleAllowListingFeatureTarget magnification =
new InvisibleToggleAllowListingFeatureTarget(context,
- shortcutType,
- isShortcutContained(context, shortcutType, MAGNIFICATION_CONTROLLER_NAME),
- MAGNIFICATION_CONTROLLER_NAME,
- context.getString(R.string.accessibility_magnification_chooser_text),
- context.getDrawable(R.drawable.ic_accessibility_magnification),
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
+ shortcutType,
+ isShortcutContained(context, shortcutType, MAGNIFICATION_CONTROLLER_NAME),
+ MAGNIFICATION_CONTROLLER_NAME,
+ context.getString(R.string.accessibility_magnification_chooser_text),
+ context.getDrawable(R.drawable.ic_accessibility_magnification),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
final ToggleAllowListingFeatureTarget daltonizer =
new ToggleAllowListingFeatureTarget(context,
- shortcutType,
- isShortcutContained(context, shortcutType,
- DALTONIZER_COMPONENT_NAME.flattenToString()),
- DALTONIZER_COMPONENT_NAME.flattenToString(),
- context.getString(R.string.color_correction_feature_name),
- context.getDrawable(R.drawable.ic_accessibility_color_correction),
- Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
+ shortcutType,
+ isShortcutContained(context, shortcutType,
+ DALTONIZER_COMPONENT_NAME.flattenToString()),
+ DALTONIZER_COMPONENT_NAME.flattenToString(),
+ context.getString(R.string.color_correction_feature_name),
+ context.getDrawable(R.drawable.ic_accessibility_color_correction),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
final ToggleAllowListingFeatureTarget colorInversion =
new ToggleAllowListingFeatureTarget(context,
- shortcutType,
- isShortcutContained(context, shortcutType,
- COLOR_INVERSION_COMPONENT_NAME.flattenToString()),
- COLOR_INVERSION_COMPONENT_NAME.flattenToString(),
- context.getString(R.string.color_inversion_feature_name),
- context.getDrawable(R.drawable.ic_accessibility_color_inversion),
- Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
+ shortcutType,
+ isShortcutContained(context, shortcutType,
+ COLOR_INVERSION_COMPONENT_NAME.flattenToString()),
+ COLOR_INVERSION_COMPONENT_NAME.flattenToString(),
+ context.getString(R.string.color_inversion_feature_name),
+ context.getDrawable(R.drawable.ic_accessibility_color_inversion),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
- // TODO: Update with shortcut icon
final ToggleAllowListingFeatureTarget reduceBrightColors =
new ToggleAllowListingFeatureTarget(context,
shortcutType,
@@ -238,7 +237,7 @@
REDUCE_BRIGHT_COLORS_COMPONENT_NAME.flattenToString()),
REDUCE_BRIGHT_COLORS_COMPONENT_NAME.flattenToString(),
context.getString(R.string.reduce_bright_colors_feature_name),
- null,
+ context.getDrawable(R.drawable.ic_accessibility_reduce_bright_colors),
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED);
targets.add(magnification);
diff --git a/core/java/com/android/internal/graphics/OWNERS b/core/java/com/android/internal/graphics/OWNERS
new file mode 100644
index 0000000..5851cbb
--- /dev/null
+++ b/core/java/com/android/internal/graphics/OWNERS
@@ -0,0 +1 @@
+include /graphics/java/android/graphics/OWNERS
\ No newline at end of file
diff --git a/core/java/com/android/internal/graphics/palette/LABCentroid.java b/core/java/com/android/internal/graphics/palette/LABCentroid.java
index 98d5d26..408cf1f 100644
--- a/core/java/com/android/internal/graphics/palette/LABCentroid.java
+++ b/core/java/com/android/internal/graphics/palette/LABCentroid.java
@@ -62,6 +62,6 @@
double dL = a[0] - b[0];
double dA = a[1] - b[1];
double dB = a[2] - b[2];
- return (float) (Math.pow(dL, 2) + Math.pow(dA, 2) + Math.pow(dB, 2));
+ return (float) (dL * dL + dA * dA + dB * dB);
}
}
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
index fcb5d3b..11df5a8 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -39,11 +39,10 @@
in IVoidResultCallback resultCallback);
void hideMySoftInput(int flags, in IVoidResultCallback resultCallback);
void showMySoftInput(int flags, in IVoidResultCallback resultCallback);
- void updateStatusIcon(String packageName, int iconId, in IVoidResultCallback resultCallback);
+ void updateStatusIconAsync(String packageName, int iconId);
void switchToPreviousInputMethod(in IBooleanResultCallback resultCallback);
void switchToNextInputMethod(boolean onlyCurrentIme, in IBooleanResultCallback resultCallback);
void shouldOfferSwitchingToNextInputMethod(in IBooleanResultCallback resultCallback);
- void notifyUserAction(in IVoidResultCallback resultCallback);
- void applyImeVisibility(IBinder showOrHideInputToken, boolean setVisible,
- in IVoidResultCallback resultCallback);
+ void notifyUserActionAsync();
+ void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible);
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 1691e13f..ed1fe1a 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -187,22 +187,19 @@
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#updateStatusIcon(String, int,
- * IVoidResultCallback)}.
+ * Calls {@link IInputMethodPrivilegedOperations#updateStatusIconAsync(String, int)}.
*
* @param packageName package name from which the status icon should be loaded
* @param iconResId resource ID of the icon to be loaded
*/
@AnyThread
- public void updateStatusIcon(String packageName, @DrawableRes int iconResId) {
+ public void updateStatusIconAsync(String packageName, @DrawableRes int iconResId) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
return;
}
try {
- final Completable.Void value = Completable.createVoid();
- ops.updateStatusIcon(packageName, iconResId, ResultCallbacks.of(value));
- Completable.getResult(value);
+ ops.updateStatusIconAsync(packageName, iconResId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -362,26 +359,23 @@
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#notifyUserAction(IVoidResultCallback)}
+ * Calls {@link IInputMethodPrivilegedOperations#notifyUserActionAsync()}
*/
@AnyThread
- public void notifyUserAction() {
+ public void notifyUserActionAsync() {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
return;
}
try {
- final Completable.Void value = Completable.createVoid();
- ops.notifyUserAction(ResultCallbacks.of(value));
- Completable.getResult(value);
+ ops.notifyUserActionAsync();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibility(IBinder, boolean,
- * IVoidResultCallback)}.
+ * Calls {@link IInputMethodPrivilegedOperations#applyImeVisibilityAsync(IBinder, boolean)}.
*
* @param showOrHideInputToken placeholder token that maps to window requesting
* {@link android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} or
@@ -390,15 +384,13 @@
* @param setVisible {@code true} to set IME visible, else hidden.
*/
@AnyThread
- public void applyImeVisibility(IBinder showOrHideInputToken, boolean setVisible) {
+ public void applyImeVisibilityAsync(IBinder showOrHideInputToken, boolean setVisible) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
return;
}
try {
- final Completable.Void value = Completable.createVoid();
- ops.applyImeVisibility(showOrHideInputToken, setVisible, ResultCallbacks.of(value));
- Completable.getResult(value);
+ ops.applyImeVisibilityAsync(showOrHideInputToken, setVisible);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 7ecf69b..1004e17 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -16,18 +16,25 @@
package com.android.internal.os;
+import static com.android.internal.os.BinderLatencyProto.Dims.SYSTEM_SERVER;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.Settings;
import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
+import android.util.KeyValueListParser;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -157,15 +164,20 @@
return new Handler(Looper.getMainLooper());
}
- public BinderLatencyObserver getLatencyObserver() {
- return new BinderLatencyObserver(new BinderLatencyObserver.Injector());
+ /** Create a latency observer for the specified process. */
+ public BinderLatencyObserver getLatencyObserver(int processSource) {
+ return new BinderLatencyObserver(new BinderLatencyObserver.Injector(), processSource);
}
}
public BinderCallsStats(Injector injector) {
+ this(injector, SYSTEM_SERVER);
+ }
+
+ public BinderCallsStats(Injector injector, int processSource) {
this.mRandom = injector.getRandomGenerator();
this.mCallStatsObserverHandler = injector.getHandler();
- this.mLatencyObserver = injector.getLatencyObserver();
+ this.mLatencyObserver = injector.getLatencyObserver(processSource);
}
public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
@@ -1074,4 +1086,120 @@
? result
: Integer.compare(a.transactionCode, b.transactionCode);
}
+
+
+ /**
+ * Settings observer for other processes (not system_server).
+ *
+ * We do not want to collect cpu data from other processes so only latency collection should be
+ * possible to enable.
+ */
+ public static class SettingsObserver extends ContentObserver {
+ // Settings for BinderCallsStats.
+ public static final String SETTINGS_ENABLED_KEY = "enabled";
+ public static final String SETTINGS_DETAILED_TRACKING_KEY = "detailed_tracking";
+ public static final String SETTINGS_UPLOAD_DATA_KEY = "upload_data";
+ public static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
+ public static final String SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY = "track_screen_state";
+ public static final String SETTINGS_TRACK_DIRECT_CALLING_UID_KEY = "track_calling_uid";
+ public static final String SETTINGS_MAX_CALL_STATS_KEY = "max_call_stats_count";
+ public static final String SETTINGS_IGNORE_BATTERY_STATUS_KEY = "ignore_battery_status";
+ // Settings for BinderLatencyObserver.
+ public static final String SETTINGS_COLLECT_LATENCY_DATA_KEY = "collect_Latency_data";
+ public static final String SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY =
+ "latency_observer_sampling_interval";
+ public static final String SETTINGS_LATENCY_OBSERVER_PUSH_INTERVAL_MINUTES_KEY =
+ "latency_observer_push_interval_minutes";
+ public static final String SETTINGS_LATENCY_HISTOGRAM_BUCKET_COUNT_KEY =
+ "latency_histogram_bucket_count";
+ public static final String SETTINGS_LATENCY_HISTOGRAM_FIRST_BUCKET_SIZE_KEY =
+ "latency_histogram_first_bucket_size";
+ public static final String SETTINGS_LATENCY_HISTOGRAM_BUCKET_SCALE_FACTOR_KEY =
+ "latency_histogram_bucket_scale_factor";
+
+ private boolean mEnabled;
+ private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS);
+ private final Context mContext;
+ private final KeyValueListParser mParser = new KeyValueListParser(',');
+ private final BinderCallsStats mBinderCallsStats;
+ private final int mProcessSource;
+
+ public SettingsObserver(Context context, BinderCallsStats binderCallsStats,
+ int processSource, int userHandle) {
+ super(BackgroundThread.getHandler());
+ mContext = context;
+ context.getContentResolver().registerContentObserver(mUri, false, this,
+ userHandle);
+ mBinderCallsStats = binderCallsStats;
+ mProcessSource = processSource;
+ // Always kick once to ensure that we match current state
+ onChange();
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri, int userId) {
+ if (mUri.equals(uri)) {
+ onChange();
+ }
+ }
+
+ void onChange() {
+ try {
+ mParser.setString(Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.BINDER_CALLS_STATS));
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Bad binder call stats settings", e);
+ }
+
+ // Cpu data collection should always be disabled for other processes.
+ mBinderCallsStats.setDetailedTracking(false);
+ mBinderCallsStats.setTrackScreenInteractive(false);
+ mBinderCallsStats.setTrackDirectCallerUid(false);
+
+ mBinderCallsStats.setIgnoreBatteryStatus(
+ mParser.getBoolean(SETTINGS_IGNORE_BATTERY_STATUS_KEY,
+ BinderCallsStats.DEFAULT_IGNORE_BATTERY_STATUS));
+ mBinderCallsStats.setCollectLatencyData(
+ mParser.getBoolean(SETTINGS_COLLECT_LATENCY_DATA_KEY,
+ BinderCallsStats.DEFAULT_COLLECT_LATENCY_DATA));
+
+ // Binder latency observer settings.
+ configureLatencyObserver(mParser, mBinderCallsStats.getLatencyObserver());
+
+ final boolean enabled =
+ mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT);
+ if (mEnabled != enabled) {
+ if (enabled) {
+ Binder.setObserver(mBinderCallsStats);
+ } else {
+ Binder.setObserver(null);
+ }
+ mEnabled = enabled;
+ mBinderCallsStats.reset();
+ mBinderCallsStats.setAddDebugEntries(enabled);
+ mBinderCallsStats.getLatencyObserver().reset();
+ }
+ }
+
+ /** Configures the binder latency observer related settings. */
+ public static void configureLatencyObserver(
+ KeyValueListParser mParser, BinderLatencyObserver binderLatencyObserver) {
+ binderLatencyObserver.setSamplingInterval(mParser.getInt(
+ SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY,
+ BinderLatencyObserver.PERIODIC_SAMPLING_INTERVAL_DEFAULT));
+ binderLatencyObserver.setHistogramBucketsParams(
+ mParser.getInt(
+ SETTINGS_LATENCY_HISTOGRAM_BUCKET_COUNT_KEY,
+ BinderLatencyObserver.BUCKET_COUNT_DEFAULT),
+ mParser.getInt(
+ SETTINGS_LATENCY_HISTOGRAM_FIRST_BUCKET_SIZE_KEY,
+ BinderLatencyObserver.FIRST_BUCKET_SIZE_DEFAULT),
+ mParser.getFloat(
+ SETTINGS_LATENCY_HISTOGRAM_BUCKET_SCALE_FACTOR_KEY,
+ BinderLatencyObserver.BUCKET_SCALE_FACTOR_DEFAULT));
+ binderLatencyObserver.setPushInterval(mParser.getInt(
+ SETTINGS_LATENCY_OBSERVER_PUSH_INTERVAL_MINUTES_KEY,
+ BinderLatencyObserver.STATSD_PUSH_INTERVAL_MINUTES_DEFAULT));
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/BinderLatencyObserver.java b/core/java/com/android/internal/os/BinderLatencyObserver.java
index 0079801..5fa96bd 100644
--- a/core/java/com/android/internal/os/BinderLatencyObserver.java
+++ b/core/java/com/android/internal/os/BinderLatencyObserver.java
@@ -16,8 +16,6 @@
package com.android.internal.os;
-import static com.android.internal.os.BinderLatencyProto.Dims.SYSTEM_SERVER;
-
import android.annotation.Nullable;
import android.os.Binder;
import android.os.Handler;
@@ -68,9 +66,10 @@
private int mStatsdPushIntervalMinutes = STATSD_PUSH_INTERVAL_MINUTES_DEFAULT;
private final Random mRandom;
- private BinderLatencyBuckets mLatencyBuckets;
-
private final Handler mLatencyObserverHandler;
+ private final int mProcessSource;
+
+ private BinderLatencyBuckets mLatencyBuckets;
private Runnable mLatencyObserverRunnable = new Runnable() {
@Override
@@ -134,7 +133,7 @@
// Write the dims.
long dimsToken = proto.start(ApiStats.DIMS);
- proto.write(Dims.PROCESS_SOURCE, SYSTEM_SERVER);
+ proto.write(Dims.PROCESS_SOURCE, mProcessSource);
proto.write(Dims.SERVICE_CLASS_NAME, dims.getBinderClass().getName());
proto.write(Dims.SERVICE_METHOD_NAME, transactionName);
proto.end(dimsToken);
@@ -180,11 +179,12 @@
}
}
- public BinderLatencyObserver(Injector injector) {
+ public BinderLatencyObserver(Injector injector, int processSource) {
mRandom = injector.getRandomGenerator();
mLatencyObserverHandler = injector.getHandler();
mLatencyBuckets = new BinderLatencyBuckets(
mBucketCount, mFirstBucketSize, mBucketScaleFactor);
+ mProcessSource = processSource;
noteLatencyDelayed();
}
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 04528e9..c32951a 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 <mutex>
+
#include "signal.h"
#include "android-base/logging.h"
@@ -26,6 +28,7 @@
#include "utils/misc.h"
#include "utils/Trace.h"
+#include "android_content_res_ApkAssets.h"
#include "android_util_AssetManager_private.h"
#include "core_jni_helpers.h"
#include "jni.h"
@@ -72,6 +75,19 @@
FORMAT_DIRECTORY = 3,
};
+Guarded<std::unique_ptr<const ApkAssets>>& ApkAssetsFromLong(jlong ptr) {
+ return *reinterpret_cast<Guarded<std::unique_ptr<const ApkAssets>>*>(ptr);
+}
+
+static jlong CreateGuardedApkAssets(std::unique_ptr<const ApkAssets> assets) {
+ auto guarded_assets = new Guarded<std::unique_ptr<const ApkAssets>>(std::move(assets));
+ return reinterpret_cast<jlong>(guarded_assets);
+}
+
+static void DeleteGuardedApkAssets(Guarded<std::unique_ptr<const ApkAssets>>& apk_assets) {
+ delete &apk_assets;
+}
+
class LoaderAssetsProvider : public AssetsProvider {
public:
static std::unique_ptr<AssetsProvider> Create(JNIEnv* env, jobject assets_provider) {
@@ -227,7 +243,7 @@
jniThrowException(env, "java/io/IOException", error_msg.c_str());
return 0;
}
- return reinterpret_cast<jlong>(apk_assets.release());
+ return CreateGuardedApkAssets(std::move(apk_assets));
}
static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
@@ -279,7 +295,7 @@
jniThrowException(env, "java/io/IOException", error_msg.c_str());
return 0;
}
- return reinterpret_cast<jlong>(apk_assets.release());
+ return CreateGuardedApkAssets(std::move(apk_assets));
}
static jlong NativeLoadFromFdOffset(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
@@ -347,12 +363,12 @@
jniThrowException(env, "java/io/IOException", error_msg.c_str());
return 0;
}
- return reinterpret_cast<jlong>(apk_assets.release());
+ return CreateGuardedApkAssets(std::move(apk_assets));
}
static jlong NativeLoadEmpty(JNIEnv* env, jclass /*clazz*/, jint flags, jobject assets_provider) {
auto apk_assets = ApkAssets::Load(LoaderAssetsProvider::Create(env, assets_provider), flags);
- return reinterpret_cast<jlong>(apk_assets.release());
+ return CreateGuardedApkAssets(std::move(apk_assets));
}
// STOPSHIP (b/159041693): Revert signal handler when reason for issue is found.
@@ -383,54 +399,60 @@
}
static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong 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 << "]";
+ {
+ auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+ auto apk_assets = scoped_apk_assets->get();
+ 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 << "}";
}
- destroy_info << "}";
- }
- destroy_info << "}";
- delete apk_assets;
+ DeleteGuardedApkAssets(ApkAssetsFromLong(ptr));
- // Deleting the apk assets did not lead to a crash.
- destroy_info.str("");
- destroy_info.clear();
+ // 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) {
- auto apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
- if (auto path = apk_assets->GetPath()) {
- return env->NewStringUTF(path->data());
+ auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+ auto apk_assets = scoped_apk_assets->get();
+ if (auto path = apk_assets->GetPath()) {
+ return env->NewStringUTF(path->data());
}
return nullptr;
}
static jstring NativeGetDebugName(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
- auto apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
- return env->NewStringUTF(apk_assets->GetDebugName().c_str());
+ auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+ auto apk_assets = scoped_apk_assets->get();
+ return env->NewStringUTF(apk_assets->GetDebugName().c_str());
}
static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
- const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
- return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool());
+ auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+ auto apk_assets = scoped_apk_assets->get();
+ return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool());
}
static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
- const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
- return apk_assets->IsUpToDate() ? JNI_TRUE : JNI_FALSE;
+ auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+ auto apk_assets = scoped_apk_assets->get();
+ return apk_assets->IsUpToDate() ? JNI_TRUE : JNI_FALSE;
}
static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) {
@@ -439,7 +461,8 @@
return 0;
}
- const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+ auto apk_assets = scoped_apk_assets->get();
std::unique_ptr<Asset> asset = apk_assets->GetAssetsProvider()->Open(
path_utf8.c_str(),Asset::AccessMode::ACCESS_RANDOM);
if (asset == nullptr) {
@@ -467,12 +490,13 @@
static jobject NativeGetOverlayableInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
jstring overlayable_name) {
- const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+ auto apk_assets = scoped_apk_assets->get();
- const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
- if (packages.empty()) {
- jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
- return 0;
+ const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
+ if (packages.empty()) {
+ jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
+ return 0;
}
// TODO(b/119899133): Convert this to a search for the info rather than assuming it's at index 0
@@ -502,13 +526,14 @@
}
static jboolean NativeDefinesOverlayable(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
- const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
+ auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr));
+ auto apk_assets = scoped_apk_assets->get();
- const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
- if (packages.empty()) {
- // Must throw to prevent bypass by returning false
- jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
- return 0;
+ const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
+ if (packages.empty()) {
+ // Must throw to prevent bypass by returning false
+ jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
+ return 0;
}
const auto& overlayable_infos = packages[0]->GetOverlayableMap();
diff --git a/core/jni/android_content_res_ApkAssets.h b/core/jni/android_content_res_ApkAssets.h
new file mode 100644
index 0000000..7e525dc
--- /dev/null
+++ b/core/jni/android_content_res_ApkAssets.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_CONTENT_RES_APKASSETS_H
+#define ANDROID_CONTENT_RES_APKASSETS_H
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/MutexGuard.h"
+
+#include "jni.h"
+
+namespace android {
+
+Guarded<std::unique_ptr<const ApkAssets>>& ApkAssetsFromLong(jlong ptr);
+
+} // namespace android
+
+#endif // ANDROID_CONTENT_RES_APKASSETS_H
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index b2c69a0..24f9abf 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -42,6 +42,7 @@
#include "androidfw/ResourceTypes.h"
#include "androidfw/ResourceUtils.h"
+#include "android_content_res_ApkAssets.h"
#include "android_util_AssetManager_private.h"
#include "core_jni_helpers.h"
#include "jni.h"
@@ -50,9 +51,9 @@
#include "nativehelper/ScopedStringChars.h"
#include "nativehelper/ScopedUtfChars.h"
#include "utils/Log.h"
-#include "utils/misc.h"
#include "utils/String8.h"
#include "utils/Trace.h"
+#include "utils/misc.h"
extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap);
extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
@@ -307,7 +308,9 @@
if (env->ExceptionCheck()) {
return;
}
- apk_assets.push_back(reinterpret_cast<const ApkAssets*>(apk_assets_native_ptr));
+
+ auto scoped_assets = ScopedLock(ApkAssetsFromLong(apk_assets_native_ptr));
+ apk_assets.push_back(scoped_assets->get());
}
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
diff --git a/core/jni/include/android_runtime/AndroidRuntime.h b/core/jni/include/android_runtime/AndroidRuntime.h
index d86d934..bf2ba77 100644
--- a/core/jni/include/android_runtime/AndroidRuntime.h
+++ b/core/jni/include/android_runtime/AndroidRuntime.h
@@ -19,7 +19,6 @@
#ifndef _RUNTIME_ANDROID_RUNTIME_H
#define _RUNTIME_ANDROID_RUNTIME_H
-#include <binder/IBinder.h>
#include <jni.h>
#include <pthread.h>
#include <utils/Errors.h>
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index 7d68a0d..4c84944 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -642,7 +642,7 @@
message ReasonCount {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
- optional android.app.job.StopReasonEnum name = 1;
+ optional android.app.job.InternalStopReasonEnum name = 1;
optional int32 count = 2;
}
repeated ReasonCount reason_count = 2;
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index ae6dcfd..1bba12f 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -430,6 +430,7 @@
optional SettingProto one_handed_mode_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto one_handed_mode_timeout = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto taps_app_to_exit = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto one_handed_mode_activated = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional OneHanded onehanded = 80;
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 36d48e2..c4ff49c 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -51,7 +51,7 @@
message JobRestriction {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional .android.app.job.StopReasonEnum reason = 1;
+ optional .android.app.job.InternalStopReasonEnum reason = 1;
optional bool is_restricting = 2;
}
@@ -856,7 +856,7 @@
message StopReasonCount {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional .android.app.job.StopReasonEnum reason = 1;
+ optional .android.app.job.InternalStopReasonEnum reason = 1;
optional int32 count = 2;
}
repeated StopReasonCount stop_reasons = 9;
@@ -907,7 +907,7 @@
optional int32 job_id = 4;
optional string tag = 5;
// Only valid for STOP_JOB or STOP_PERIODIC_JOB Events.
- optional .android.app.job.StopReasonEnum stop_reason = 6;
+ optional .android.app.job.InternalStopReasonEnum stop_reason = 6;
}
repeated HistoryEvent history_event = 1;
}
diff --git a/core/res/res/drawable/ic_accessibility_color_correction.xml b/core/res/res/drawable/ic_accessibility_color_correction.xml
index ed09d57..f1dbfb2 100644
--- a/core/res/res/drawable/ic_accessibility_color_correction.xml
+++ b/core/res/res/drawable/ic_accessibility_color_correction.xml
@@ -1,5 +1,5 @@
<!--
- Copyright (C) 2020 The Android Open Source Project
+ Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,40 +13,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector android:height="24dp" android:viewportHeight="192"
- android:viewportWidth="192" android:width="24dp"
- xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#00BCD4" android:pathData="M37.14,173.74L8.61,83.77c-1.72,-5.75 0.26,-11.97 4.97,-15.63L87.15,11c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c4.63,3.7 6.53,9.88 4.8,15.57l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84C44.53,184 38.97,179.83 37.14,173.74z"/>
- <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
- android:pathData="M13.58,69.14L87.15,12c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c3.36,2.68 5.27,6.67 5.41,10.82c0.15,-4.51 -1.79,-8.93 -5.41,-11.82l-73.81,-58.94C99.61,7.01 92.35,6.96 87.15,11L13.58,68.14c-3.71,2.88 -5.72,7.36 -5.56,11.94C8.17,75.85 10.14,71.81 13.58,69.14z" android:strokeAlpha="0.2"/>
- <path android:fillAlpha="0.2" android:fillColor="#263238"
- android:pathData="M183.35,84.63l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84c-6.31,0 -11.87,-4.17 -13.7,-10.26L8.61,82.77c-0.36,-1.22 -0.55,-2.46 -0.59,-3.69c-0.06,1.56 0.13,3.14 0.59,4.69l28.53,89.97c1.83,6.09 7.39,10.26 13.7,10.26h90.37c6.28,0 11.82,-4.13 13.67,-10.19l28.48,-88.18c0.48,-1.57 0.67,-3.17 0.61,-4.75C183.92,82.13 183.73,83.39 183.35,84.63z" android:strokeAlpha="0.2"/>
- <path android:pathData="M60.01,135L60.01,135H60l48.04,49h33.17c6.28,0 11.82,-4.13 13.67,-10.19l18.68,-57.84L129.51,72.2l-0.01,0c0.27,0.27 0.52,0.5 0.77,0.77l0.55,0.55c1.57,1.56 1.57,4.08 -0.04,5.68l-12.56,12.49l6.36,6.3l1.38,1.37l-5.67,5.64l-5.71,-5.68L79.15,135H60.42H60.01z">
- <aapt:attr name="android:fillColor">
- <gradient android:endX="155.9627" android:endY="165.1493"
- android:startX="98.4649" android:startY="107.6516" android:type="linear">
- <item android:color="#19263238" android:offset="0"/>
- <item android:color="#00212121" android:offset="1"/>
- </gradient>
- </aapt:attr>
- </path>
- <path android:fillColor="#0097A7" android:pathData="M68.55,120.35l32.173,-32.173l7.658,7.658l-32.173,32.173z"/>
- <path android:fillColor="#FFFFFF" android:pathData="M130.83,73.52l-9.42,-9.36c-1.57,-1.56 -4.1,-1.56 -5.67,0l-12.56,12.48L95.42,69l-5.67,5.64l5.71,5.68L60,116.97V135h19.15l35.42,-35.68l5.71,5.68l5.67,-5.64l-7.73,-7.68l12.56,-12.48C132.4,77.6 132.4,75.08 130.83,73.52zM74.98,126.77l-6.43,-6.43l32.17,-32.17l6.43,6.43L74.98,126.77z"/>
- <path android:fillAlpha="0.1" android:fillColor="#263238"
- android:pathData="M120.28,105l-5.71,-5.68l-35.42,35.68l-19.15,0l1,1l19.15,0l35.42,-35.68l5.71,5.68l5.68,-5.64l-1,-1z" android:strokeAlpha="0.1"/>
- <path android:fillAlpha="0.1" android:fillColor="#263238"
- android:pathData="M90.75,75.64l-0.01,0l4.71,4.68l0.01,0z" android:strokeAlpha="0.1"/>
- <path android:fillAlpha="0.1" android:fillColor="#263238"
- android:pathData="M131.83,74.52l-0.97,-0.97c1.54,1.56 1.53,4.06 -0.07,5.65l-12.56,12.48l1,1l12.55,-12.48C133.4,78.6 133.4,76.08 131.83,74.52z" android:strokeAlpha="0.1"/>
- <path android:fillAlpha="0.1" android:fillColor="#263238"
- android:pathData="M101.72,89.17l6.67,6.66l0,0l-7.67,-7.66l-32.17,32.17l1,1z" android:strokeAlpha="0.1"/>
- <path android:pathData="M37.13,173.7L8.62,83.69c-1.7,-5.8 0.3,-12 5,-15.6l73.52,-57.1c5.2,-4 12.5,-4 17.6,0.1l73.82,58.9c4.6,3.7 6.5,9.9 4.8,15.6l-28.51,88.21c-1.8,6.1 -7.4,10.2 -13.7,10.2H50.83C44.53,184 38.93,179.8 37.13,173.7z">
- <aapt:attr name="android:fillColor">
- <gradient android:centerX="21.977" android:centerY="23.8809"
- android:gradientRadius="158.0384" android:type="radial">
- <item android:color="#19FFFFFF" android:offset="0"/>
- <item android:color="#00FFFFFF" android:offset="1"/>
- </gradient>
- </aapt:attr>
- </path>
-</vector>
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@color/accessibility_daltonizer_background" />
+ <foreground>
+ <inset
+ android:drawable="@drawable/ic_accessibility_color_correction_foreground"
+ android:inset="@dimen/accessibility_icon_foreground_padding_ratio" />
+ </foreground>
+</adaptive-icon>
diff --git a/core/res/res/drawable/ic_accessibility_color_correction_foreground.xml b/core/res/res/drawable/ic_accessibility_color_correction_foreground.xml
new file mode 100644
index 0000000..ecb5065
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_color_correction_foreground.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="108dp"
+ android:height="108dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"
+ android:pathData="M14.8417,6.6662C14.9562,6.5573 15.0995,6.5 15.2484,6.5C15.3917,6.5 15.5349,6.5573 15.6495,6.6662L16.9901,8.0068C17.2135,8.2302 17.2135,8.5912 16.9844,8.8203L15.1969,10.6078L16.2969,11.7078L15.4891,12.5156L14.6755,11.7021L9.5651,16.8125H6.8438V14.0911L11.9542,8.9807L11.1406,8.1672L11.9484,7.3594L13.0542,8.4537L14.8417,6.6662ZM7.9896,15.6667H9.0896L13.7073,11.049L12.6073,9.949L7.9896,14.5667V15.6667Z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_accessibility_color_inversion.xml b/core/res/res/drawable/ic_accessibility_color_inversion.xml
index d69a169..f855880 100644
--- a/core/res/res/drawable/ic_accessibility_color_inversion.xml
+++ b/core/res/res/drawable/ic_accessibility_color_inversion.xml
@@ -1,5 +1,5 @@
<!--
- Copyright (C) 2020 The Android Open Source Project
+ Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,50 +13,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector android:height="24dp" android:viewportHeight="192"
- android:viewportWidth="192" android:width="24dp"
- xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#546E7A" android:pathData="M37.14,173.74L8.61,83.77c-1.72,-5.75 0.26,-11.97 4.97,-15.63L87.15,11c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c4.63,3.7 6.53,9.88 4.8,15.57l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84C44.53,184 38.97,179.83 37.14,173.74z"/>
- <path android:fillAlpha="0.2" android:fillColor="#263238"
- android:pathData="M183.37,84.63l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84c-6.31,0 -11.87,-4.17 -13.7,-10.26L8.61,82.77c-0.36,-1.22 -0.55,-2.46 -0.59,-3.69c-0.06,1.56 0.13,3.14 0.59,4.69l28.53,89.97c1.83,6.09 7.39,10.26 13.7,10.26h90.38c6.28,0 11.82,-4.13 13.67,-10.19l28.48,-88.18c0.48,-1.57 0.67,-3.17 0.61,-4.75C183.94,82.13 183.74,83.39 183.37,84.63z" android:strokeAlpha="0.2"/>
- <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
- android:pathData="M13.58,69.14L87.15,12c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c3.36,2.68 5.27,6.67 5.41,10.82c0.15,-4.51 -1.79,-8.93 -5.41,-11.82l-73.81,-58.94C99.61,7.01 92.35,6.96 87.15,11L13.58,68.14c-3.71,2.88 -5.72,7.36 -5.56,11.94C8.17,75.85 10.14,71.81 13.58,69.14z" android:strokeAlpha="0.2"/>
- <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
- android:pathData="M53,130.05l5.03,-4.79L44.16,112c-0.05,0.78 -0.14,1.52 -0.15,2.34C43.62,136.61 61.27,154.56 84,156v-5.31C70.13,149.64 58.56,141.54 53,130.05z" android:strokeAlpha="0.2"/>
- <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
- android:pathData="M109,52v5.31c13.65,1.05 24.67,9.15 30.11,20.64l-4.92,4.79L147.81,96c0.09,-0.78 0.17,-1.53 0.19,-2.34C148.38,71.39 131.36,53.44 109,52z" android:strokeAlpha="0.2"/>
- <path android:pathData="M154.89,173.81l13.57,-42.02l-46.53,-46.56C125.75,90.5 128,96.98 128,104c0,17.7 -14.3,32 -32,32c-8.64,0 -16.47,-3.42 -22.22,-8.97l0.9,0.91L130.73,184h10.49C147.5,184 153.04,179.87 154.89,173.81z">
- <aapt:attr name="android:fillColor">
- <gradient android:endX="153.3523" android:endY="161.6371"
- android:startX="90.6075" android:startY="98.8923" android:type="linear">
- <item android:color="#33263238" android:offset="0"/>
- <item android:color="#05263238" android:offset="1"/>
- </gradient>
- </aapt:attr>
- </path>
- <path android:pathData="M96,129.6V78.4c-14.11,0 -25.6,11.49 -25.6,25.6S81.89,129.6 96,129.6z">
- <aapt:attr name="android:fillColor">
- <gradient android:endX="150.8492" android:endY="164.1401"
- android:startX="88.1044" android:startY="101.3954" android:type="linear">
- <item android:color="#33263238" android:offset="0"/>
- <item android:color="#05263238" android:offset="1"/>
- </gradient>
- </aapt:attr>
- </path>
- <path android:fillAlpha="0.2" android:fillColor="#263238"
- android:pathData="M96,136c-17.53,0 -31.72,-14.04 -31.99,-31.5c0,0.17 -0.01,0.33 -0.01,0.5c0,17.7 14.3,32 32,32s32,-14.3 32,-32c0,-0.17 -0.01,-0.33 -0.01,-0.5C127.72,121.96 113.53,136 96,136z" android:strokeAlpha="0.2"/>
- <path android:fillAlpha="0.2" android:fillColor="#263238"
- android:pathData="M70.4,104c0,0.17 0.01,0.33 0.01,0.5C70.68,90.62 82.06,79.4 96,79.4v-1C81.89,78.4 70.4,89.88 70.4,104z" android:strokeAlpha="0.2"/>
- <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
- android:pathData="M96,72c-17.7,0 -32,14.3 -32,32s14.3,32 32,32s32,-14.3 32,-32S113.7,72 96,72zM70.4,104c0,-14.11 11.49,-25.6 25.6,-25.6v51.2C81.89,129.6 70.4,118.11 70.4,104z" android:strokeAlpha="0.2"/>
- <path android:fillColor="#FFFFFF" android:pathData="M96,72c-17.7,0 -32,14.3 -32,32s14.3,32 32,32s32,-14.3 32,-32S113.7,72 96,72zM70.4,104c0,-14.11 11.49,-25.6 25.6,-25.6v51.2C81.89,129.6 70.4,118.11 70.4,104z"/>
- <path android:pathData="M37.14,173.74L8.61,83.77c-1.72,-5.75 0.26,-11.97 4.97,-15.63L87.15,11c5.2,-4.04 12.46,-3.99 17.61,0.12l73.81,58.94c4.63,3.7 6.53,9.88 4.8,15.57l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84C44.53,184 38.97,179.83 37.14,173.74z">
- <aapt:attr name="android:fillColor">
- <gradient android:endX="156.2451" android:endY="171.4516"
- android:startX="37.0633" android:startY="52.269802" android:type="linear">
- <item android:color="#19FFFFFF" android:offset="0"/>
- <item android:color="#00FFFFFF" android:offset="1"/>
- </gradient>
- </aapt:attr>
- </path>
-</vector>
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@color/accessibility_color_inversion_background" />
+ <foreground>
+ <inset
+ android:drawable="@drawable/ic_accessibility_color_inversion_foreground"
+ android:inset="@dimen/accessibility_icon_foreground_padding_ratio" />
+ </foreground>
+</adaptive-icon>
diff --git a/core/res/res/drawable/ic_accessibility_color_inversion_foreground.xml b/core/res/res/drawable/ic_accessibility_color_inversion_foreground.xml
new file mode 100644
index 0000000..7fc3aa6
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_color_inversion_foreground.xml
@@ -0,0 +1,54 @@
+<!--
+ 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="108dp"
+ android:height="108dp"
+ android:viewportWidth="192"
+ android:viewportHeight="192">
+ <path
+ android:fillAlpha="0.2"
+ android:fillColor="#212121"
+ android:pathData="M96,183L96,183c-48.23,0 -87.72,-39.33 -87.99,-87.5C8.01,95.67 8,95.83 8,96v0c0,48.4 39.6,88 88,88h0c48.4,0 88,-39.6 88,-88v0c0,-0.17 -0.01,-0.33 -0.01,-0.5C183.72,143.67 144.23,183 96,183z" />
+ <path
+ android:fillAlpha="0.2"
+ android:fillColor="#FFFFFF"
+ android:pathData="M184,96c0,-48.4 -39.6,-88 -88,-88h0C47.6,8 8,47.6 8,96v0c0,0.17 0.01,0.33 0.01,0.5C8.28,48.33 47.77,9 96,9h0c48.23,0 87.72,39.33 87.99,87.5C183.99,96.33 184,96.17 184,96L184,96z"
+ android:strokeAlpha="0.2" />
+ <path
+ android:fillAlpha="0.2"
+ android:fillColor="#FFFFFF"
+ android:pathData="M53,122.2l5.03,-4.79l-13.88,-13.26c-0.05,0.78 -0.14,1.52 -0.15,2.34c-0.39,22.27 17.27,40.22 39.99,41.66v-5.31C70.13,141.78 58.56,133.69 53,122.2z"
+ android:strokeAlpha="0.2" />
+ <path
+ android:fillAlpha="0.2"
+ android:fillColor="#FFFFFF"
+ android:pathData="M109,44.15v5.31c13.65,1.05 24.67,9.15 30.11,20.64l-4.92,4.79l13.62,13.26c0.09,-0.78 0.17,-1.53 0.19,-2.34C148.38,63.53 131.36,45.59 109,44.15z"
+ android:strokeAlpha="0.2" />
+ <path
+ android:fillAlpha="0.2"
+ android:fillColor="#263238"
+ android:pathData="M70.4,96.18c0,0.17 0.01,0.33 0.01,0.5c0.27,-13.88 11.64,-25.1 25.59,-25.1v-1C81.89,70.57 70.4,82.06 70.4,96.18z"
+ android:strokeAlpha="0.2" />
+ <path
+ android:fillAlpha="0.2"
+ android:fillColor="#263238"
+ android:pathData="M128,96.67c-0.27,17.47 -14.46,31.5 -31.99,31.5c-8.79,0 -16.75,-3.53 -22.53,-9.26l-0.04,0l5.03,5.04c5.03,3.3 11.05,5.22 17.53,5.22c17.7,0 32,-14.31 32,-32C128.01,97.01 128,96.84 128,96.67z"
+ android:strokeAlpha="0.2" />
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M96,64.16c-17.7,0 -32,14.3 -32,32c0,17.7 14.3,32 32,32c17.7,0 32,-14.3 32,-32C128,78.47 113.7,64.16 96,64.16zM70.4,96.16c0,-14.11 11.49,-25.6 25.6,-25.6v51.2C81.89,121.77 70.4,110.28 70.4,96.16z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_accessibility_magnification.xml b/core/res/res/drawable/ic_accessibility_magnification.xml
index 5dab479..f3b2887 100644
--- a/core/res/res/drawable/ic_accessibility_magnification.xml
+++ b/core/res/res/drawable/ic_accessibility_magnification.xml
@@ -1,5 +1,5 @@
<!--
- Copyright (C) 2020 The Android Open Source Project
+ Copyright (C) 2021 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,102 +13,13 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:aapt="http://schemas.android.com/aapt"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="192"
- android:viewportHeight="192">
- <path
- android:pathData="M96,104m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"
- android:fillColor="#F50057"/>
- <path
- android:pathData="M178.57,70.06l-73.81,-58.94C99.6,7.01 92.35,6.96 87.15,11L13.59,68.14C8.87,71.8 6.9,78.02 8.61,83.77l28.53,89.97c1.83,6.09 7.39,10.26 13.7,10.26h90.38c6.28,0 11.82,-4.13 13.67,-10.19l0.69,-2.13l-34.94,-34.88v-4.7l-0.96,-0.99c-6.33,5.54 -14.61,8.9 -23.68,8.9c-19.89,0 -36.02,-16.12 -36.02,-36.01S76.11,68 96,68s36.01,16.12 36.01,36.01c0,8.68 -3.08,16.65 -8.2,22.87l1.05,1.01h4.7l30.34,30.39l23.47,-72.65C185.1,79.94 183.2,73.76 178.57,70.06z"
- android:fillColor="#F50057"/>
- <path
- android:pathData="M65.25,73c0,0 -16.75,31.96 -9.25,45.1s21.02,29.15 40.01,32.65s32.99,10.5 39.99,14s19.58,6.93 19.58,6.93s-5.34,-9.49 4.32,-13.4c0.73,-3.53 -11.9,-42.35 -11.9,-42.35L127.92,73L81.79,62.25L65.25,73z"
- android:fillColor="#F50057"/>
- <path
- android:pathData="M155.33,171.43l-0.44,1.37c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84c-6.31,0 -11.87,-4.17 -13.7,-10.26L8.61,82.77c-0.36,-1.22 -0.55,-2.46 -0.59,-3.69c-0.06,1.56 0.13,3.14 0.59,4.69l28.53,89.97c1.83,6.09 7.39,10.26 13.7,10.26h90.38c6.28,0 11.82,-4.13 13.67,-10.19l0.69,-2.13L155.33,171.43z"
- android:strokeAlpha="0.2"
- android:fillColor="#3E2723"
- android:fillAlpha="0.2"/>
- <path
- android:pathData="M183.37,84.63l-23.71,73.41l0.24,0.24l23.47,-72.66c0.48,-1.57 0.67,-3.17 0.61,-4.75C183.94,82.13 183.74,83.39 183.37,84.63z"
- android:strokeAlpha="0.2"
- android:fillColor="#3E2723"
- android:fillAlpha="0.2"/>
- <path
- android:pathData="M155.57,171.67l-34.93,-34.87v-4.7l-0.96,-0.99c-6.33,5.54 -14.61,8.9 -23.68,8.9c-9.81,0 -18.71,-3.93 -25.2,-10.29L125.07,184h16.14c6.28,0 11.81,-4.13 13.67,-10.19L155.57,171.67z">
- <aapt:attr name="android:fillColor">
- <gradient
- android:startY="104.215"
- android:startX="69.035"
- android:endY="173.8946"
- android:endX="138.7146"
- android:type="linear">
- <item android:offset="0" android:color="#333E2723"/>
- <item android:offset="1" android:color="#003E2723"/>
- </gradient>
- </aapt:attr>
- </path>
- <path
- android:pathData="M132.01,104.01c0,8.68 -3.08,16.65 -8.2,22.87l1.05,1.01h4.7l30.34,30.39L170,127l-49,-49.03l-0.19,-0.04C127.71,84.49 132.01,93.74 132.01,104.01z">
- <aapt:attr name="android:fillColor">
- <gradient
- android:startY="83.635"
- android:startX="103.615"
- android:endY="137.0219"
- android:endX="157.0018"
- android:type="linear">
- <item android:offset="0" android:color="#333E2723"/>
- <item android:offset="1" android:color="#003E2723"/>
- </gradient>
- </aapt:attr>
- </path>
- <path
- android:pathData="M124.27,127.32c4.85,-6.13 7.75,-13.88 7.75,-22.3c0,-0.16 -0.01,-0.31 -0.01,-0.47c-0.12,8.47 -3.17,16.24 -8.19,22.34L124.27,127.32z"
- android:strokeAlpha="0.2"
- android:fillColor="#3E2723"
- android:fillAlpha="0.2"/>
- <path
- android:pathData="M96.01,80.01c-13.25,0 -24,10.75 -24,24c0,0.17 0.01,0.33 0.01,0.5c0.27,-13.02 10.91,-23.5 23.99,-23.5s23.72,10.48 23.99,23.5c0,-0.17 0.01,-0.33 0.01,-0.5C120.01,90.76 109.26,80.01 96.01,80.01z"
- android:strokeAlpha="0.2"
- android:fillColor="#3E2723"
- android:fillAlpha="0.2"/>
- <path
- android:pathData="M155.58,171.68l-34.93,-34.87l0,1l34.68,34.62z"
- android:strokeAlpha="0.2"
- android:fillColor="#3E2723"
- android:fillAlpha="0.2"/>
- <path
- android:pathData="M119.69,131.12c-6.33,5.54 -14.61,8.9 -23.68,8.9c-9.97,0 -19,-4.06 -25.52,-10.61h-0.01l5.59,5.59c5.71,3.8 12.57,6.03 19.94,6.03c9.07,0 17.35,-3.36 23.68,-8.9l0.96,0.99v-1L119.69,131.12z"
- android:strokeAlpha="0.2"
- android:fillColor="#3E2723"
- android:fillAlpha="0.2"/>
- <path
- android:pathData="M13.59,69.14L87.15,12c5.2,-4.04 12.45,-3.99 17.61,0.12l73.81,58.94c3.36,2.68 5.27,6.67 5.41,10.82c0.15,-4.51 -1.79,-8.93 -5.41,-11.82l-73.81,-58.94C99.6,7.01 92.35,6.96 87.15,11L13.59,68.14c-3.72,2.88 -5.72,7.36 -5.57,11.94C8.17,75.85 10.14,71.81 13.59,69.14z"
- android:strokeAlpha="0.2"
- android:fillColor="#FFFFFF"
- android:fillAlpha="0.2"/>
- <path
- android:pathData="M112,108h-12v12h-8v-12H80v-8h12V88h8v12h12V108z"
- android:fillColor="#F8BBD0"/>
- <path
- android:pathData="M129.57,127.9h-4.7l-1.05,-1.01c5.12,-6.22 8.2,-14.19 8.2,-22.87c0,-19.89 -16.12,-36.01 -36.01,-36.01s-36.02,16.11 -36.02,36s16.13,36.01 36.02,36.01c9.07,0 17.35,-3.36 23.68,-8.9l0.96,0.99v4.7l34.93,34.87l4.33,-13.39L129.57,127.9zM96.01,128.01c-13.25,0 -24,-10.75 -24,-24s10.75,-24 24,-24s24,10.75 24,24S109.26,128.01 96.01,128.01z"
- android:fillColor="#FFFFFF"/>
- <path
- android:pathData="M37.14,173.74L8.61,83.77C6.9,78.02 8.87,71.8 13.59,68.14L87.15,11c5.2,-4.04 12.45,-3.99 17.61,0.12l73.81,58.94c4.63,3.7 6.53,9.88 4.8,15.57l-28.48,88.18c-1.85,6.06 -7.39,10.19 -13.67,10.19H50.84C44.53,184 38.97,179.83 37.14,173.74z">
- <aapt:attr name="android:fillColor">
- <gradient
- android:startY="53.3534"
- android:startX="38.1466"
- android:endY="178.712"
- android:endX="163.5051"
- android:type="linear">
- <item android:offset="0" android:color="#19FFFFFF"/>
- <item android:offset="1" android:color="#00FFFFFF"/>
- </gradient>
- </aapt:attr>
- </path>
-</vector>
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@color/accessibility_magnification_background" />
+ <foreground>
+ <inset
+ android:drawable="@drawable/ic_accessibility_magnification_foreground"
+ android:inset="@dimen/accessibility_icon_foreground_padding_ratio" />
+ </foreground>
+
+</adaptive-icon>
diff --git a/core/res/res/drawable/ic_accessibility_magnification_foreground.xml b/core/res/res/drawable/ic_accessibility_magnification_foreground.xml
new file mode 100644
index 0000000..7ce1880
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_magnification_foreground.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="108dp"
+ android:height="108dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"
+ android:pathData="M17.6942,16.7552L14.0831,13.1441C14.5684,12.4698 14.8646,11.6568 14.8646,10.7682C14.8646,8.5058 13.0307,6.6719 10.7682,6.6719C8.5058,6.6719 6.6719,8.5058 6.6719,10.7682C6.6719,13.0307 8.5058,14.8646 10.7682,14.8646C11.6568,14.8646 12.4698,14.5684 13.1441,14.0831L16.7552,17.6942L17.6942,16.7552ZM10.7682,13.6042C9.199,13.6042 7.9323,12.3374 7.9323,10.7682C7.9323,9.199 9.199,7.9323 10.7682,7.9323C12.3374,7.9323 13.6042,9.199 13.6042,10.7682C13.6042,12.3374 12.3374,13.6042 10.7682,13.6042ZM12.6589,10.138H11.3984V8.8776H10.138V10.138H8.8776V11.3984H10.138V12.6589H11.3984V11.3984H12.6589V10.138Z" />
+</vector>
diff --git a/core/res/res/drawable/ic_accessibility_reduce_bright_colors.xml b/core/res/res/drawable/ic_accessibility_reduce_bright_colors.xml
new file mode 100644
index 0000000..4eab97b
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_reduce_bright_colors.xml
@@ -0,0 +1,24 @@
+<!--
+ 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.
+-->
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@color/accessibility_feature_background" />
+ <foreground>
+ <inset
+ android:drawable="@drawable/ic_accessibility_reduce_bright_colors_foreground"
+ android:inset="@dimen/accessibility_icon_foreground_padding_ratio" />
+ </foreground>
+</adaptive-icon>
diff --git a/core/res/res/drawable/ic_accessibility_reduce_bright_colors_foreground.xml b/core/res/res/drawable/ic_accessibility_reduce_bright_colors_foreground.xml
new file mode 100644
index 0000000..58609da
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_reduce_bright_colors_foreground.xml
@@ -0,0 +1,54 @@
+<!--
+ 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="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <group android:scaleX="2.97"
+ android:scaleY="2.97"
+ android:translateX="18.36"
+ android:translateY="18.36">
+ <path
+ android:pathData="M17,12.1L15.59,10.69L13.05,13.22V7.05H11.05V13.22L8.51,10.69L7.1,12.1L12.05,17.05L17,12.1Z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M2.05,13.05H4.05C4.6,13.05 5.05,12.6 5.05,12.05C5.05,11.5 4.6,11.05 4.05,11.05H2.05C1.5,11.05 1.05,11.5 1.05,12.05C1.05,12.6 1.5,13.05 2.05,13.05Z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M20.05,13.05H22.05C22.6,13.05 23.05,12.6 23.05,12.05C23.05,11.5 22.6,11.05 22.05,11.05H20.05C19.5,11.05 19.05,11.5 19.05,12.05C19.05,12.6 19.5,13.05 20.05,13.05Z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M11.05,2.05V4.05C11.05,4.6 11.5,5.05 12.05,5.05C12.6,5.05 13.05,4.6 13.05,4.05V2.05C13.05,1.5 12.6,1.05 12.05,1.05C11.5,1.05 11.05,1.5 11.05,2.05Z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M11.05,20.05V22.05C11.05,22.6 11.5,23.05 12.05,23.05C12.6,23.05 13.05,22.6 13.05,22.05V20.05C13.05,19.5 12.6,19.05 12.05,19.05C11.5,19.05 11.05,19.5 11.05,20.05Z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M6.04,4.63C5.65,4.24 5.01,4.24 4.63,4.63C4.24,5.02 4.24,5.66 4.63,6.04L5.69,7.1C6.08,7.49 6.72,7.49 7.1,7.1C7.49,6.71 7.49,6.07 7.1,5.69L6.04,4.63Z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M18.41,17C18.02,16.61 17.38,16.61 17,17C16.61,17.39 16.61,18.03 17,18.41L18.06,19.47C18.45,19.86 19.09,19.86 19.47,19.47C19.86,19.08 19.86,18.44 19.47,18.06L18.41,17Z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M19.47,6.04C19.86,5.65 19.86,5.01 19.47,4.63C19.08,4.24 18.44,4.24 18.06,4.63L17,5.69C16.61,6.08 16.61,6.72 17,7.1C17.39,7.49 18.03,7.49 18.41,7.1L19.47,6.04Z"
+ android:fillColor="#ffffff"/>
+ <path
+ android:pathData="M7.1,18.41C7.49,18.02 7.49,17.38 7.1,17C6.71,16.61 6.07,16.61 5.69,17L4.63,18.06C4.24,18.45 4.24,19.09 4.63,19.47C5.02,19.86 5.66,19.86 6.04,19.47L7.1,18.41Z"
+ android:fillColor="#ffffff"/>
+ </group>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/layout/work_widget_mask_view.xml b/core/res/res/layout/work_widget_mask_view.xml
index 9e1692f..e7174cc 100644
--- a/core/res/res/layout/work_widget_mask_view.xml
+++ b/core/res/res/layout/work_widget_mask_view.xml
@@ -18,7 +18,7 @@
android:id="@+id/work_widget_mask_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="#F3374248"
+ android:background="?android:attr/colorSurfaceVariant"
android:importantForAccessibility="noHideDescendants"
android:clickable="true">
@@ -33,8 +33,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
- android:layout_marginBottom="4dp"
- android:layout_marginRight="4dp"
+ android:layout_marginBottom="12dp"
+ android:layout_marginRight="12dp"
android:src="@drawable/ic_corp_badge_off"
android:clickable="false" />
</FrameLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f097009..d4fa285 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9576,8 +9576,9 @@
<attr name="lStar" format="float"/>
<declare-styleable name="DisplayHashingService">
- <!-- The throttle duration for display hash requests
+ <!-- The interval required between display hash requests. Requests made faster than this
+ delay will be throttled."
@hide @SystemApi -->
- <attr name="throttleDurationMillis" format="integer" />
+ <attr name="durationBetweenRequestsMillis" format="integer" />
</declare-styleable>
</resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 0213c60..36168e7 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -427,4 +427,10 @@
<!-- Darkest shade of the secondary neutral color used by the system. Black.
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_1000">#000000</color>
+
+ <!-- Accessibility shortcut icon background color -->
+ <color name="accessibility_feature_background">#5F6368</color> <!-- Google grey 700 -->
+ <color name="accessibility_magnification_background">#F50D60</color>
+ <color name="accessibility_daltonizer_background">#00BCD4</color>
+ <color name="accessibility_color_inversion_background">#546E7A</color>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fbc9678..d3ea52e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4892,4 +4892,7 @@
<bool name="config_supportsMicToggle">false</bool>
<!-- Whether this device is supporting the camera toggle -->
<bool name="config_supportsCamToggle">false</bool>
+
+ <!-- List containing the allowed install sources for accessibility service. -->
+ <string-array name="config_accessibility_allowed_install_source" translatable="false"/>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index fc3ed63..6be6167 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -37,7 +37,7 @@
<!-- Text size of the message within a toast -->
<dimen name="toast_text_size">14sp</dimen>
<!-- Elevation of toast view -->
- <dimen name="toast_elevation">6dp</dimen>
+ <dimen name="toast_elevation">2dp</dimen>
<!-- Height of the status bar -->
<dimen name="status_bar_height">@dimen/status_bar_height_portrait</dimen>
@@ -576,6 +576,9 @@
<!-- Width of the outline stroke used by the accessibility focus rectangle -->
<dimen name="accessibility_focus_highlight_stroke_width">4dp</dimen>
+ <!-- The padding ratio of the Accessibility icon foreground drawable -->
+ <item name="accessibility_icon_foreground_padding_ratio" type="dimen">21.88%</item>
+
<!-- Margin around the various security views -->
<dimen name="keyguard_muliuser_selector_margin">8dp</dimen>
diff --git a/core/res/res/values/locale_config.xml b/core/res/res/values/locale_config.xml
index 3c65caf..e9b42d3 100644
--- a/core/res/res/values/locale_config.xml
+++ b/core/res/res/values/locale_config.xml
@@ -74,7 +74,7 @@
<item>ar-TD-u-nu-latn</item> <!-- Arabic (Chad, Western Digits) -->
<item>ar-TN</item> <!-- Arabic (Tunisia) -->
<item>ar-TN-u-nu-arab</item> <!-- Arabic (Tunisia, Arabic-Indic Digits) -->
- <item>ar-XB</item> <!-- Right-to-left pseudolocale -->
+ <item>ar-XB</item> <!-- Arabic (Pseudo-Bidi) -->
<item>ar-YE</item> <!-- Arabic (Yemen) -->
<item>ar-YE-u-nu-latn</item> <!-- Arabic (Yemen, Western Digits) -->
<item>as-IN</item> <!-- Assamese (India) -->
@@ -104,6 +104,7 @@
<item>ca-FR</item> <!-- Catalan (France) -->
<item>ca-IT</item> <!-- Catalan (Italy) -->
<item>ce-RU</item> <!-- Chechen (Russia) -->
+ <item>ceb-PH</item> <!-- Cebuano (Philippines) -->
<item>cgg-UG</item> <!-- Chiga (Uganda) -->
<item>chr-US</item> <!-- Cherokee (United States) -->
<item>cs-CZ</item> <!-- Czech (Czechia) -->
@@ -119,15 +120,18 @@
<item>de-LI</item> <!-- German (Liechtenstein) -->
<item>de-LU</item> <!-- German (Luxembourg) -->
<item>dje-NE</item> <!-- Zarma (Niger) -->
+ <item>doi-IN</item> <!-- Dogri (India) -->
<item>dsb-DE</item> <!-- Lower Sorbian (Germany) -->
<item>dua-CM</item> <!-- Duala (Cameroon) -->
<item>dyo-SN</item> <!-- Jola-Fonyi (Senegal) -->
<item>dz-BT</item> <!-- Dzongkha (Bhutan) -->
+ <item>dz-BT-u-nu-latn</item> <!-- Dzongkha (Bhutan, Western Digits) -->
<item>ebu-KE</item> <!-- Embu (Kenya) -->
<item>ee-GH</item> <!-- Ewe (Ghana) -->
<item>ee-TG</item> <!-- Ewe (Togo) -->
<item>el-CY</item> <!-- Greek (Cyprus) -->
<item>el-GR</item> <!-- Greek (Greece) -->
+ <item>en-AE</item> <!-- English (United Arab Emirates) -->
<item>en-AG</item> <!-- English (Antigua & Barbuda) -->
<item>en-AI</item> <!-- English (Anguilla) -->
<item>en-AS</item> <!-- English (American Samoa) -->
@@ -181,7 +185,7 @@
<item>en-LS</item> <!-- English (Lesotho) -->
<item>en-MG</item> <!-- English (Madagascar) -->
<item>en-MH</item> <!-- English (Marshall Islands) -->
- <item>en-MO</item> <!-- English (Macau) -->
+ <item>en-MO</item> <!-- English (Macao) -->
<item>en-MP</item> <!-- English (Northern Mariana Islands) -->
<item>en-MS</item> <!-- English (Montserrat) -->
<item>en-MT</item> <!-- English (Malta) -->
@@ -212,7 +216,7 @@
<item>en-SL</item> <!-- English (Sierra Leone) -->
<item>en-SS</item> <!-- English (South Sudan) -->
<item>en-SX</item> <!-- English (Sint Maarten) -->
- <item>en-SZ</item> <!-- English (Swaziland) -->
+ <item>en-SZ</item> <!-- English (Eswatini) -->
<item>en-TC</item> <!-- English (Turks & Caicos Islands) -->
<item>en-TK</item> <!-- English (Tokelau) -->
<item>en-TO</item> <!-- English (Tonga) -->
@@ -227,7 +231,7 @@
<item>en-VI</item> <!-- English (U.S. Virgin Islands) -->
<item>en-VU</item> <!-- English (Vanuatu) -->
<item>en-WS</item> <!-- English (Samoa) -->
- <item>en-XA</item> <!-- Left-to-right pseudolocale -->
+ <item>en-XA</item> <!-- English (Pseudo-Accents) -->
<item>en-ZA</item> <!-- English (South Africa) -->
<item>en-ZM</item> <!-- English (Zambia) -->
<item>en-ZW</item> <!-- English (Zimbabwe) -->
@@ -265,10 +269,42 @@
<item>fa-AF-u-nu-latn</item> <!-- Persian (Afghanistan, Western Digits) -->
<item>fa-IR</item> <!-- Persian (Iran) -->
<item>fa-IR-u-nu-latn</item> <!-- Persian (Iran, Western Digits) -->
- <item>ff-CM</item> <!-- Fulah (Cameroon) -->
- <item>ff-GN</item> <!-- Fulah (Guinea) -->
- <item>ff-MR</item> <!-- Fulah (Mauritania) -->
- <item>ff-SN</item> <!-- Fulah (Senegal) -->
+ <item>ff-Adlm-BF</item> <!-- Fulah (Adlam, Burkina Faso) -->
+ <item>ff-Adlm-BF-u-nu-latn</item> <!-- Fulah (Adlam, Burkina Faso, Western Digits) -->
+ <item>ff-Adlm-CM</item> <!-- Fulah (Adlam, Cameroon) -->
+ <item>ff-Adlm-CM-u-nu-latn</item> <!-- Fulah (Adlam, Cameroon, Western Digits) -->
+ <item>ff-Adlm-GH</item> <!-- Fulah (Adlam, Ghana) -->
+ <item>ff-Adlm-GH-u-nu-latn</item> <!-- Fulah (Adlam, Ghana, Western Digits) -->
+ <item>ff-Adlm-GM</item> <!-- Fulah (Adlam, Gambia) -->
+ <item>ff-Adlm-GM-u-nu-latn</item> <!-- Fulah (Adlam, Gambia, Western Digits) -->
+ <item>ff-Adlm-GN</item> <!-- Fulah (Adlam, Guinea) -->
+ <item>ff-Adlm-GN-u-nu-latn</item> <!-- Fulah (Adlam, Guinea, Western Digits) -->
+ <item>ff-Adlm-GW</item> <!-- Fulah (Adlam, Guinea-Bissau) -->
+ <item>ff-Adlm-GW-u-nu-latn</item> <!-- Fulah (Adlam, Guinea-Bissau, Western Digits) -->
+ <item>ff-Adlm-LR</item> <!-- Fulah (Adlam, Liberia) -->
+ <item>ff-Adlm-LR-u-nu-latn</item> <!-- Fulah (Adlam, Liberia, Western Digits) -->
+ <item>ff-Adlm-MR</item> <!-- Fulah (Adlam, Mauritania) -->
+ <item>ff-Adlm-MR-u-nu-latn</item> <!-- Fulah (Adlam, Mauritania, Western Digits) -->
+ <item>ff-Adlm-NE</item> <!-- Fulah (Adlam, Niger) -->
+ <item>ff-Adlm-NE-u-nu-latn</item> <!-- Fulah (Adlam, Niger, Western Digits) -->
+ <item>ff-Adlm-NG</item> <!-- Fulah (Adlam, Nigeria) -->
+ <item>ff-Adlm-NG-u-nu-latn</item> <!-- Fulah (Adlam, Nigeria, Western Digits) -->
+ <item>ff-Adlm-SL</item> <!-- Fulah (Adlam, Sierra Leone) -->
+ <item>ff-Adlm-SL-u-nu-latn</item> <!-- Fulah (Adlam, Sierra Leone, Western Digits) -->
+ <item>ff-Adlm-SN</item> <!-- Fulah (Adlam, Senegal) -->
+ <item>ff-Adlm-SN-u-nu-latn</item> <!-- Fulah (Adlam, Senegal, Western Digits) -->
+ <item>ff-Latn-BF</item> <!-- Fulah (Latin, Burkina Faso) -->
+ <item>ff-Latn-CM</item> <!-- Fulah (Latin, Cameroon) -->
+ <item>ff-Latn-GH</item> <!-- Fulah (Latin, Ghana) -->
+ <item>ff-Latn-GM</item> <!-- Fulah (Latin, Gambia) -->
+ <item>ff-Latn-GN</item> <!-- Fulah (Latin, Guinea) -->
+ <item>ff-Latn-GW</item> <!-- Fulah (Latin, Guinea-Bissau) -->
+ <item>ff-Latn-LR</item> <!-- Fulah (Latin, Liberia) -->
+ <item>ff-Latn-MR</item> <!-- Fulah (Latin, Mauritania) -->
+ <item>ff-Latn-NE</item> <!-- Fulah (Latin, Niger) -->
+ <item>ff-Latn-NG</item> <!-- Fulah (Latin, Nigeria) -->
+ <item>ff-Latn-SL</item> <!-- Fulah (Latin, Sierra Leone) -->
+ <item>ff-Latn-SN</item> <!-- Fulah (Latin, Senegal) -->
<item>fi-FI</item> <!-- Finnish (Finland) -->
<item>fil-PH</item> <!-- Filipino (Philippines) -->
<item>fo-DK</item> <!-- Faroese (Denmark) -->
@@ -321,6 +357,7 @@
<item>fr-YT</item> <!-- French (Mayotte) -->
<item>fur-IT</item> <!-- Friulian (Italy) -->
<item>fy-NL</item> <!-- Western Frisian (Netherlands) -->
+ <item>ga-GB</item> <!-- Irish (United Kingdom) -->
<item>ga-IE</item> <!-- Irish (Ireland) -->
<item>gd-GB</item> <!-- Scottish Gaelic (United Kingdom) -->
<item>gl-ES</item> <!-- Galician (Spain) -->
@@ -340,6 +377,7 @@
<item>hsb-DE</item> <!-- Upper Sorbian (Germany) -->
<item>hu-HU</item> <!-- Hungarian (Hungary) -->
<item>hy-AM</item> <!-- Armenian (Armenia) -->
+ <item>ia-001</item> <!-- Interlingua (World) -->
<item>ig-NG</item> <!-- Igbo (Nigeria) -->
<item>ii-CN</item> <!-- Sichuan Yi (China) -->
<item>in-ID</item> <!-- Indonesian (Indonesia) -->
@@ -347,10 +385,12 @@
<item>it-CH</item> <!-- Italian (Switzerland) -->
<item>it-IT</item> <!-- Italian (Italy) -->
<item>it-SM</item> <!-- Italian (San Marino) -->
+ <item>it-VA</item> <!-- Italian (Vatican City) -->
<item>iw-IL</item> <!-- Hebrew (Israel) -->
<item>ja-JP</item> <!-- Japanese (Japan) -->
<item>jgo-CM</item> <!-- Ngomba (Cameroon) -->
<item>jmc-TZ</item> <!-- Machame (Tanzania) -->
+ <item>jv-ID</item> <!-- Javanese (Indonesia) -->
<item>ka-GE</item> <!-- Georgian (Georgia) -->
<item>kab-DZ</item> <!-- Kabyle (Algeria) -->
<item>kam-KE</item> <!-- Kamba (Kenya) -->
@@ -386,6 +426,7 @@
<item>luo-KE</item> <!-- Luo (Kenya) -->
<item>luy-KE</item> <!-- Luyia (Kenya) -->
<item>lv-LV</item> <!-- Latvian (Latvia) -->
+ <item>mai-IN</item> <!-- Maithili (India) -->
<item>mas-KE</item> <!-- Masai (Kenya) -->
<item>mas-TZ</item> <!-- Masai (Tanzania) -->
<item>mer-KE</item> <!-- Meru (Kenya) -->
@@ -393,23 +434,31 @@
<item>mg-MG</item> <!-- Malagasy (Madagascar) -->
<item>mgh-MZ</item> <!-- Makhuwa-Meetto (Mozambique) -->
<item>mgo-CM</item> <!-- Metaʼ (Cameroon) -->
- <item>mk-MK</item> <!-- Macedonian (Macedonia (FYROM)) -->
+ <item>mi-NZ</item> <!-- Maori (New Zealand) -->
+ <item>mk-MK</item> <!-- Macedonian (North Macedonia) -->
<item>ml-IN</item> <!-- Malayalam (India) -->
<item>mn-MN</item> <!-- Mongolian (Mongolia) -->
+ <item>mni-IN</item> <!-- Manipuri (India) -->
+ <item>mni-IN-u-nu-latn</item> <!-- Manipuri (India, Western Digits) -->
<item>mr-IN</item> <!-- Marathi (India) -->
+ <item>mr-IN-u-nu-latn</item> <!-- Marathi (India, Western Digits) -->
<item>ms-BN</item> <!-- Malay (Brunei) -->
+ <item>ms-ID</item> <!-- Malay (Indonesia) -->
<item>ms-MY</item> <!-- Malay (Malaysia) -->
<item>ms-SG</item> <!-- Malay (Singapore) -->
<item>mt-MT</item> <!-- Maltese (Malta) -->
<item>my-MM</item> <!-- Burmese (Myanmar (Burma)) -->
<item>my-MM-u-nu-latn</item> <!-- Burmese (Myanmar (Burma), Western Digits) -->
<item>mzn-IR</item> <!-- Mazanderani (Iran) -->
+ <item>mzn-IR-u-nu-latn</item> <!-- Mazanderani (Iran, Western Digits) -->
<item>naq-NA</item> <!-- Nama (Namibia) -->
<item>nb-NO</item> <!-- Norwegian Bokmål (Norway) -->
<item>nb-SJ</item> <!-- Norwegian Bokmål (Svalbard & Jan Mayen) -->
<item>nd-ZW</item> <!-- North Ndebele (Zimbabwe) -->
<item>ne-IN</item> <!-- Nepali (India) -->
+ <item>ne-IN-u-nu-latn</item> <!-- Nepali (India, Western Digits) -->
<item>ne-NP</item> <!-- Nepali (Nepal) -->
+ <item>ne-NP-u-nu-latn</item> <!-- Nepali (Nepal, Western Digits) -->
<item>nl-AW</item> <!-- Dutch (Aruba) -->
<item>nl-BE</item> <!-- Dutch (Belgium) -->
<item>nl-BQ</item> <!-- Dutch (Caribbean Netherlands) -->
@@ -427,14 +476,22 @@
<item>os-GE</item> <!-- Ossetic (Georgia) -->
<item>os-RU</item> <!-- Ossetic (Russia) -->
<item>pa-Arab-PK</item> <!-- Punjabi (Arabic, Pakistan) -->
+ <item>pa-Arab-PK-u-nu-latn</item> <!-- Punjabi (Arabic, Pakistan, Western Digits) -->
<item>pa-Guru-IN</item> <!-- Punjabi (Gurmukhi, India) -->
+ <item>pcm-NG</item> <!-- Nigerian Pidgin (Nigeria) -->
<item>pl-PL</item> <!-- Polish (Poland) -->
<item>ps-AF</item> <!-- Pashto (Afghanistan) -->
+ <item>ps-AF-u-nu-latn</item> <!-- Pashto (Afghanistan, Western Digits) -->
+ <item>ps-PK</item> <!-- Pashto (Pakistan) -->
+ <item>ps-PK-u-nu-latn</item> <!-- Pashto (Pakistan, Western Digits) -->
<item>pt-AO</item> <!-- Portuguese (Angola) -->
<item>pt-BR</item> <!-- Portuguese (Brazil) -->
+ <item>pt-CH</item> <!-- Portuguese (Switzerland) -->
<item>pt-CV</item> <!-- Portuguese (Cape Verde) -->
+ <item>pt-GQ</item> <!-- Portuguese (Equatorial Guinea) -->
<item>pt-GW</item> <!-- Portuguese (Guinea-Bissau) -->
- <item>pt-MO</item> <!-- Portuguese (Macau) -->
+ <item>pt-LU</item> <!-- Portuguese (Luxembourg) -->
+ <item>pt-MO</item> <!-- Portuguese (Macao) -->
<item>pt-MZ</item> <!-- Portuguese (Mozambique) -->
<item>pt-PT</item> <!-- Portuguese (Portugal) -->
<item>pt-ST</item> <!-- Portuguese (São Tomé & Príncipe) -->
@@ -455,9 +512,15 @@
<item>ru-UA</item> <!-- Russian (Ukraine) -->
<item>rw-RW</item> <!-- Kinyarwanda (Rwanda) -->
<item>rwk-TZ</item> <!-- Rwa (Tanzania) -->
+ <item>sa-IN</item> <!-- Sanskrit (India) -->
<item>sah-RU</item> <!-- Sakha (Russia) -->
<item>saq-KE</item> <!-- Samburu (Kenya) -->
+ <item>sat-IN</item> <!-- Santali (India) -->
+ <item>sat-IN-u-nu-latn</item> <!-- Santali (India, Western Digits) -->
<item>sbp-TZ</item> <!-- Sangu (Tanzania) -->
+ <item>sd-Arab-PK</item> <!-- Sindhi (Arabic, Pakistan) -->
+ <item>sd-Arab-PK-u-nu-latn</item> <!-- Sindhi (Arabic, Pakistan, Western Digits) -->
+ <item>sd-Deva-IN</item> <!-- Sindhi (Devanagari, India) -->
<item>se-FI</item> <!-- Northern Sami (Finland) -->
<item>se-NO</item> <!-- Northern Sami (Norway) -->
<item>se-SE</item> <!-- Northern Sami (Sweden) -->
@@ -474,7 +537,7 @@
<item>so-KE</item> <!-- Somali (Kenya) -->
<item>so-SO</item> <!-- Somali (Somalia) -->
<item>sq-AL</item> <!-- Albanian (Albania) -->
- <item>sq-MK</item> <!-- Albanian (Macedonia (FYROM)) -->
+ <item>sq-MK</item> <!-- Albanian (North Macedonia) -->
<item>sq-XK</item> <!-- Albanian (Kosovo) -->
<item>sr-Cyrl-BA</item> <!-- Serbian (Cyrillic, Bosnia & Herzegovina) -->
<item>sr-Cyrl-ME</item> <!-- Serbian (Cyrillic, Montenegro) -->
@@ -484,6 +547,7 @@
<item>sr-Latn-ME</item> <!-- Serbian (Latin, Montenegro) -->
<item>sr-Latn-RS</item> <!-- Serbian (Latin, Serbia) -->
<item>sr-Latn-XK</item> <!-- Serbian (Latin, Kosovo) -->
+ <item>su-ID</item> <!-- Sundanese (Indonesia) -->
<item>sv-AX</item> <!-- Swedish (Åland Islands) -->
<item>sv-FI</item> <!-- Swedish (Finland) -->
<item>sv-SE</item> <!-- Swedish (Sweden) -->
@@ -498,10 +562,13 @@
<item>te-IN</item> <!-- Telugu (India) -->
<item>teo-KE</item> <!-- Teso (Kenya) -->
<item>teo-UG</item> <!-- Teso (Uganda) -->
+ <item>tg-TJ</item> <!-- Tajik (Tajikistan) -->
<item>th-TH</item> <!-- Thai (Thailand) -->
+ <item>tk-TM</item> <!-- Turkmen (Turkmenistan) -->
<item>to-TO</item> <!-- Tongan (Tonga) -->
<item>tr-CY</item> <!-- Turkish (Cyprus) -->
<item>tr-TR</item> <!-- Turkish (Turkey) -->
+ <item>tt-RU</item> <!-- Tatar (Russia) -->
<item>twq-NE</item> <!-- Tasawaq (Niger) -->
<item>tzm-MA</item> <!-- Central Atlas Tamazight (Morocco) -->
<item>ug-CN</item> <!-- Uyghur (China) -->
@@ -511,11 +578,13 @@
<item>ur-PK</item> <!-- Urdu (Pakistan) -->
<item>ur-PK-u-nu-arabext</item> <!-- Urdu (Pakistan, Extended Arabic-Indic Digits) -->
<item>uz-Arab-AF</item> <!-- Uzbek (Arabic, Afghanistan) -->
+ <item>uz-Arab-AF-u-nu-latn</item> <!-- Uzbek (Arabic, Afghanistan, Western Digits) -->
<item>uz-Cyrl-UZ</item> <!-- Uzbek (Cyrillic, Uzbekistan) -->
<item>uz-Latn-UZ</item> <!-- Uzbek (Latin, Uzbekistan) -->
<item>vi-VN</item> <!-- Vietnamese (Vietnam) -->
<item>vun-TZ</item> <!-- Vunjo (Tanzania) -->
<item>wae-CH</item> <!-- Walser (Switzerland) -->
+ <item>wo-SN</item> <!-- Wolof (Senegal) -->
<item>xog-UG</item> <!-- Soga (Uganda) -->
<item>yav-CM</item> <!-- Yangben (Cameroon) -->
<item>yo-BJ</item> <!-- Yoruba (Benin) -->
@@ -525,10 +594,10 @@
<item>zgh-MA</item> <!-- Standard Moroccan Tamazight (Morocco) -->
<item>zh-Hans-CN</item> <!-- Chinese (Simplified, China) -->
<item>zh-Hans-HK</item> <!-- Chinese (Simplified, Hong Kong) -->
- <item>zh-Hans-MO</item> <!-- Chinese (Simplified, Macau) -->
+ <item>zh-Hans-MO</item> <!-- Chinese (Simplified, Macao) -->
<item>zh-Hans-SG</item> <!-- Chinese (Simplified, Singapore) -->
<item>zh-Hant-HK</item> <!-- Chinese (Traditional, Hong Kong) -->
- <item>zh-Hant-MO</item> <!-- Chinese (Traditional, Macau) -->
+ <item>zh-Hant-MO</item> <!-- Chinese (Traditional, Macao) -->
<item>zh-Hant-TW</item> <!-- Chinese (Traditional, Taiwan) -->
<item>zu-ZA</item> <!-- Zulu (South Africa) -->
</string-array>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index c716000..0e9526a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3097,7 +3097,7 @@
<public name="playHomeTransitionSound" />
<public name="lStar" />
<!-- @hide @SystemApi -->
- <public name="throttleDurationMillis" />
+ <public name="durationBetweenRequestsMillis" />
<public name="showInInputMethodPicker" />
<public name="effectColor" />
</staging-public-group>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index fd33ccd..270b98e 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -6006,4 +6006,8 @@
<string name="view_and_control_notification_title">Check access settings</string>
<!-- Notification content to prompt the user that some accessibility service has view and control access. [CHAR LIMIT=none] -->
<string name="view_and_control_notification_content"><xliff:g id="service_name" example="TalkBack">%s</xliff:g> can view and control your screen. Tap to review.</string>
+ <!-- Accessibility message announced when the view text displayed on the screen that has been translated to a different language by the system. [CHAR LIMIT=NONE] -->
+ <string name="ui_translation_accessibility_translated_text"><xliff:g id="message" example="Hello">%1$s</xliff:g> Translated.</string>
+ <!-- Accessibility message announced to notify the user when the system has finished translating the content displayed on the screen to a different language after the user requested translation. [CHAR LIMIT=NONE] -->
+ <string name="ui_translation_accessibility_translation_finished">Message translated from <xliff:g id="from_language" example="English">%1$s</xliff:g> to <xliff:g id="to_language" example="French">%2$s</xliff:g>.</string>
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index fbf67e0..bac9cf2 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -964,7 +964,7 @@
</style>
<style name="TextAppearance.Toast">
- <item name="fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="fontFamily">@*android:string/config_bodyFontFamily</item>
<item name="textSize">14sp</item>
<item name="textColor">?android:attr/textColorPrimary</item>
</style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d62f2bc..b9f1e20 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3404,6 +3404,7 @@
<java-symbol type="drawable" name="ic_accessibility_color_inversion" />
<java-symbol type="drawable" name="ic_accessibility_color_correction" />
<java-symbol type="drawable" name="ic_accessibility_magnification" />
+ <java-symbol type="drawable" name="ic_accessibility_reduce_bright_colors" />
<java-symbol type="string" name="reduce_bright_colors_feature_name" />
@@ -4356,6 +4357,11 @@
<java-symbol type="drawable" name="ic_accessibility_24dp" />
<java-symbol type="string" name="view_and_control_notification_title" />
<java-symbol type="string" name="view_and_control_notification_content" />
+ <java-symbol type="array" name="config_accessibility_allowed_install_source" />
+
+ <!-- Translation -->
+ <java-symbol type="string" name="ui_translation_accessibility_translated_text" />
+ <java-symbol type="string" name="ui_translation_accessibility_translation_finished" />
<java-symbol type="layout" name="notification_expand_button"/>
diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
index 412b367..8b39beb 100644
--- a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
@@ -33,6 +33,7 @@
import android.hardware.lights.Light;
import android.hardware.lights.LightState;
import android.hardware.lights.LightsManager;
+import android.hardware.lights.LightsRequest;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
@@ -224,4 +225,25 @@
session.close();
verify(mIInputManagerMock).closeLightSession(eq(DEVICE_ID), eq(token));
}
+
+ @Test
+ public void testLightsRequest() throws Exception {
+ Light light = new Light(1 /* id */, 0 /* ordinal */, Light.LIGHT_TYPE_INPUT_PLAYER_ID);
+ LightState state = new LightState(0xf1);
+ LightsRequest request = new Builder().addLight(light, state).build();
+
+ // Covers the LightsRequest.getLights
+ assertThat(request.getLights().size()).isEqualTo(1);
+ assertThat(request.getLights().get(0)).isEqualTo(1);
+
+ // Covers the LightsRequest.getLightStates
+ assertThat(request.getLightStates().size()).isEqualTo(1);
+ assertThat(request.getLightStates().get(0)).isEqualTo(state);
+
+ // Covers the LightsRequest.getLightsAndStates
+ assertThat(request.getLightsAndStates().size()).isEqualTo(1);
+ assertThat(request.getLightsAndStates().containsKey(1)).isTrue();
+ assertThat(request.getLightsAndStates().get(1)).isEqualTo(state);
+ }
+
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 8c5e0fc..e2e672e 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -984,8 +984,9 @@
return mHandler;
}
- public BinderLatencyObserver getLatencyObserver() {
- return new BinderLatencyObserverTest.TestBinderLatencyObserver();
+ @Override
+ public BinderLatencyObserver getLatencyObserver(int processSource) {
+ return new BinderLatencyObserverTest.TestBinderLatencyObserver(processSource);
}
});
setSamplingInterval(1);
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
index bf87683..4157f5e 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
@@ -245,18 +245,24 @@
private ArrayList<String> mWrittenAtoms;
TestBinderLatencyObserver() {
- // Make random generator not random.
- super(new Injector() {
- public Random getRandomGenerator() {
- return new Random() {
- int mCallCount = 0;
+ this(SYSTEM_SERVER);
+ }
- public int nextInt() {
- return mCallCount++;
+ TestBinderLatencyObserver(int processSource) {
+ // Make random generator not random.
+ super(
+ new Injector() {
+ public Random getRandomGenerator() {
+ return new Random() {
+ int mCallCount = 0;
+
+ public int nextInt() {
+ return mCallCount++;
+ }
+ };
}
- };
- }
- });
+ },
+ processSource);
setSamplingInterval(1);
mWrittenAtoms = new ArrayList<>();
}
diff --git a/core/tests/nfctests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java b/core/tests/nfctests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java
index 43f9b6f..48f4288 100644
--- a/core/tests/nfctests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java
+++ b/core/tests/nfctests/src/android/nfc/NfcControllerAlwaysOnListenerTest.java
@@ -19,6 +19,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -62,7 +63,36 @@
}
@Test
+ public void testRegister_RegisterUnregisterWhenNotSupported() throws RemoteException {
+ // isControllerAlwaysOnSupported() returns false, not supported.
+ doReturn(false).when(mNfcAdapter).isControllerAlwaysOnSupported();
+ NfcControllerAlwaysOnListener mListener =
+ new NfcControllerAlwaysOnListener(mNfcAdapter);
+ ControllerAlwaysOnListener mockListener1 = mock(ControllerAlwaysOnListener.class);
+ ControllerAlwaysOnListener mockListener2 = mock(ControllerAlwaysOnListener.class);
+
+ // Verify that the state listener will not registered with the NFC Adapter
+ mListener.register(getExecutor(), mockListener1);
+ verify(mNfcAdapter, times(0)).registerControllerAlwaysOnListener(any());
+
+ // Register a second client and no any call to NFC Adapter
+ mListener.register(getExecutor(), mockListener2);
+ verify(mNfcAdapter, times(0)).registerControllerAlwaysOnListener(any());
+
+ // Unregister first listener, and no any call to NFC Adapter
+ mListener.unregister(mockListener1);
+ verify(mNfcAdapter, times(0)).registerControllerAlwaysOnListener(any());
+ verify(mNfcAdapter, times(0)).unregisterControllerAlwaysOnListener(any());
+
+ // Unregister second listener, and no any call to NFC Adapter
+ mListener.unregister(mockListener2);
+ verify(mNfcAdapter, times(0)).registerControllerAlwaysOnListener(any());
+ verify(mNfcAdapter, times(0)).unregisterControllerAlwaysOnListener(any());
+ }
+
+ @Test
public void testRegister_RegisterUnregister() throws RemoteException {
+ doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported();
NfcControllerAlwaysOnListener mListener =
new NfcControllerAlwaysOnListener(mNfcAdapter);
ControllerAlwaysOnListener mockListener1 = mock(ControllerAlwaysOnListener.class);
@@ -89,6 +119,7 @@
@Test
public void testRegister_FirstRegisterFails() throws RemoteException {
+ doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported();
NfcControllerAlwaysOnListener mListener =
new NfcControllerAlwaysOnListener(mNfcAdapter);
ControllerAlwaysOnListener mockListener1 = mock(ControllerAlwaysOnListener.class);
@@ -116,6 +147,7 @@
@Test
public void testRegister_RegisterSameListenerTwice() throws RemoteException {
+ doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported();
NfcControllerAlwaysOnListener mListener =
new NfcControllerAlwaysOnListener(mNfcAdapter);
ControllerAlwaysOnListener mockListener = mock(ControllerAlwaysOnListener.class);
@@ -132,7 +164,7 @@
@Test
public void testNotify_AllListenersNotified() throws RemoteException {
-
+ doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported();
NfcControllerAlwaysOnListener listener = new NfcControllerAlwaysOnListener(mNfcAdapter);
List<ControllerAlwaysOnListener> mockListeners = new ArrayList<>();
for (int i = 0; i < 10; i++) {
@@ -149,7 +181,8 @@
}
@Test
- public void testStateChange_CorrectValue() {
+ public void testStateChange_CorrectValue() throws RemoteException {
+ doReturn(true).when(mNfcAdapter).isControllerAlwaysOnSupported();
runStateChangeValue(true, true);
runStateChangeValue(false, false);
diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java
index bd72d45..00219e7 100644
--- a/keystore/java/android/security/Authorization.java
+++ b/keystore/java/android/security/Authorization.java
@@ -74,16 +74,19 @@
* @param locked - whether it is a lock (true) or unlock (false) event
* @param syntheticPassword - if it is an unlock event with the password, pass the synthetic
* password provided by the LockSettingService
+ * @param unlockingSids - KeyMint secure user IDs that should be permitted to unlock
+ * UNLOCKED_DEVICE_REQUIRED keys.
*
* @return 0 if successful or a {@code ResponseCode}.
*/
public static int onLockScreenEvent(@NonNull boolean locked, @NonNull int userId,
- @Nullable byte[] syntheticPassword) {
+ @Nullable byte[] syntheticPassword, @Nullable long[] unlockingSids) {
try {
if (locked) {
- getService().onLockScreenEvent(LockScreenEvent.LOCK, userId, null);
+ getService().onLockScreenEvent(LockScreenEvent.LOCK, userId, null, unlockingSids);
} else {
- getService().onLockScreenEvent(LockScreenEvent.UNLOCK, userId, syntheticPassword);
+ getService().onLockScreenEvent(
+ LockScreenEvent.UNLOCK, userId, syntheticPassword, unlockingSids);
}
return 0;
} catch (RemoteException | NullPointerException e) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
index b6d408a..eb82c6d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java
@@ -23,7 +23,6 @@
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
import android.graphics.Rect;
import android.view.SurfaceControl;
import android.window.WindowContainerToken;
@@ -89,11 +88,11 @@
ProtoLog.v(WM_SHELL_TASK_ORG, "pair task1=%d task2=%d in AppPair=%s",
task1.taskId, task2.taskId, this);
- if ((!task1.isResizeable || !task2.isResizeable)
- && !ActivityTaskManager.supportsNonResizableMultiWindow()) {
+ if (!task1.supportsMultiWindow || !task2.supportsMultiWindow) {
ProtoLog.e(WM_SHELL_TASK_ORG,
- "Can't pair unresizeable tasks task1.isResizeable=%b task1.isResizeable=%b",
- task1.isResizeable, task2.isResizeable);
+ "Can't pair tasks that doesn't support multi window, "
+ + "task1.supportsMultiWindow=%b, task2.supportsMultiWindow=%b",
+ task1.supportsMultiWindow, task2.supportsMultiWindow);
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 8434d66..6f526ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -575,6 +575,7 @@
Log.d(TAG, "Overflowing: " + bubble);
}
mLogger.logOverflowAdd(bubble, reason);
+ mOverflowBubbles.remove(bubble);
mOverflowBubbles.add(0, bubble);
mStateChange.addedOverflowBubble = bubble;
bubble.stopInflation();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
index 8ee2b40..f7c7285 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
@@ -220,18 +220,25 @@
Log.d(TAG, "remove: " + toRemove);
}
toRemove.cleanupViews();
- final int i = mOverflowBubbles.indexOf(toRemove);
+ final int indexToRemove = mOverflowBubbles.indexOf(toRemove);
mOverflowBubbles.remove(toRemove);
- mAdapter.notifyItemRemoved(i);
+ mAdapter.notifyItemRemoved(indexToRemove);
}
Bubble toAdd = update.addedOverflowBubble;
if (toAdd != null) {
+ final int indexToAdd = mOverflowBubbles.indexOf(toAdd);
if (DEBUG_OVERFLOW) {
- Log.d(TAG, "add: " + toAdd);
+ Log.d(TAG, "add: " + toAdd + " prevIndex: " + indexToAdd);
}
- mOverflowBubbles.add(0, toAdd);
- mAdapter.notifyItemInserted(0);
+ if (indexToAdd > 0) {
+ mOverflowBubbles.remove(toAdd);
+ mOverflowBubbles.add(0, toAdd);
+ mAdapter.notifyItemMoved(indexToAdd, 0);
+ } else {
+ mOverflowBubbles.add(0, toAdd);
+ mAdapter.notifyItemInserted(0);
+ }
}
updateEmptyStateVisibility();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 6719d74..0f5d0ef 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1604,6 +1604,7 @@
// can start fresh.
cancelAllExpandCollapseSwitchAnimations();
}
+ showManageMenu(false /* show */);
// If we're expanded, screenshot the currently expanded bubble (before expanding the newly
// selected bubble) so we can animate it out.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index b9fdaa1..442e7a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -110,6 +110,10 @@
return false;
}
+ if (mDoubleTapDetector.onTouchEvent(event)) {
+ return true;
+ }
+
final int action = event.getAction() & MotionEvent.ACTION_MASK;
final boolean isLandscape = isLandscape();
// Using raw xy to prevent lost track of motion events while moving divider bar.
@@ -136,21 +140,22 @@
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mVelocityTracker.addMovement(event);
+ releaseTouching();
+
+ if (!mMoving) break;
+
mVelocityTracker.computeCurrentVelocity(1000 /* units */);
final float velocity = isLandscape
? mVelocityTracker.getXVelocity()
: mVelocityTracker.getYVelocity();
- releaseTouching();
- mMoving = false;
-
final int position = mSplitLayout.getDividePosition() + touchPos - mStartPos;
final DividerSnapAlgorithm.SnapTarget snapTarget =
mSplitLayout.findSnapTarget(position, velocity, false /* hardDismiss */);
mSplitLayout.snapToTarget(position, snapTarget);
+ mMoving = false;
break;
}
- mDoubleTapDetector.onTouchEvent(event);
return true;
}
@@ -229,7 +234,7 @@
if (mSplitLayout != null) {
mSplitLayout.onDoubleTappedDivider();
}
- return false;
+ return true;
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index b64c796..d318a5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -231,6 +231,7 @@
}
private void flingDividePosition(int from, int to) {
+ if (from == to) return;
ValueAnimator animator = ValueAnimator
.ofInt(from, to)
.setDuration(250);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
index a18d106..60f7ee2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java
@@ -49,7 +49,6 @@
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
-import android.view.ViewRootImpl;
import android.view.ViewTreeObserver.InternalInsetsInfo;
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
import android.view.WindowManager;
@@ -524,9 +523,10 @@
case MotionEvent.ACTION_CANCEL:
mVelocityTracker.addMovement(event);
+ if (!mMoving) break;
+
x = (int) event.getRawX();
y = (int) event.getRawY();
-
mVelocityTracker.computeCurrentVelocity(1000);
int position = calculatePosition(x, y);
stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
index 27c56fd..d9409ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java
@@ -30,7 +30,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
import android.app.WindowConfiguration;
import android.graphics.Rect;
import android.os.IBinder;
@@ -92,11 +91,10 @@
// is nothing behind it.
((type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK)
&& triggerTask.parentTaskId == mListener.mPrimary.taskId)
- // if a non-resizable is launched when it is not supported in multi window,
+ // if an activity that is not supported in multi window mode is launched,
// we also need to leave split-screen.
|| ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)
- && !triggerTask.isResizeable
- && !ActivityTaskManager.supportsNonResizableMultiWindow());
+ && !triggerTask.supportsMultiWindow);
// In both cases, dismiss the primary
if (shouldDismiss) {
WindowManagerProxy.buildDismissSplit(out, mListener,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
index 5a2ef56..1072845 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java
@@ -46,7 +46,6 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.function.BooleanSupplier;
/**
* Proxy to simplify calls into window manager/activity manager
@@ -209,17 +208,10 @@
return false;
}
ActivityManager.RunningTaskInfo topHomeTask = null;
- // One-time lazy wrapper to avoid duplicated IPC in loop. Not store as class variable
- // because the value can be changed at runtime.
- final BooleanSupplier supportsNonResizableMultiWindow =
- createSupportsNonResizableMultiWindowSupplier();
for (int i = rootTasks.size() - 1; i >= 0; --i) {
final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i);
- // Check whether to move resizeable task to split secondary.
- // Also, we have an exception for non-resizable home because we will minimize to show
- // it.
- if (!rootTask.isResizeable && rootTask.topActivityType != ACTIVITY_TYPE_HOME
- && !supportsNonResizableMultiWindow.getAsBoolean()) {
+ // Check whether the task can be moved to split secondary.
+ if (!rootTask.supportsMultiWindow && rootTask.topActivityType != ACTIVITY_TYPE_HOME) {
continue;
}
// Only move fullscreen tasks to split secondary.
@@ -364,21 +356,6 @@
outWct.setFocusable(tiles.mPrimary.token, true /* focusable */);
}
- /** Creates a lazy wrapper to get whether it supports non-resizable in multi window. */
- private static BooleanSupplier createSupportsNonResizableMultiWindowSupplier() {
- return new BooleanSupplier() {
- private Boolean mSupportsNonResizableMultiWindow;
- @Override
- public boolean getAsBoolean() {
- if (mSupportsNonResizableMultiWindow == null) {
- mSupportsNonResizableMultiWindow =
- ActivityTaskManager.supportsNonResizableMultiWindow();
- }
- return mSupportsNonResizableMultiWindow;
- }
- };
- }
-
/**
* Utility to apply a sync transaction serially with other sync transactions.
*
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 6bd9603..4b4d934 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -81,6 +81,14 @@
@Override
public void onOneHandedAnimationStart(
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
+ final boolean isEntering = animator.getTransitionDirection()
+ == TRANSITION_DIRECTION_TRIGGER;
+ if (!mTransitionCallbacks.isEmpty()) {
+ for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
+ final OneHandedTransitionCallback cb = mTransitionCallbacks.get(i);
+ cb.onStartTransition(isEntering);
+ }
+ }
}
@Override
@@ -262,11 +270,12 @@
mLastVisualDisplayBounds.offsetTo(0,
direction == TRANSITION_DIRECTION_TRIGGER ? offset : 0);
for (int i = mTransitionCallbacks.size() - 1; i >= 0; i--) {
- final OneHandedTransitionCallback callback = mTransitionCallbacks.get(i);
+ final OneHandedTransitionCallback cb = mTransitionCallbacks.get(i);
+ cb.onStartTransition(false /* isTransitioning */);
if (direction == TRANSITION_DIRECTION_TRIGGER) {
- callback.onStartFinished(getLastVisualDisplayBounds());
+ cb.onStartFinished(getLastVisualDisplayBounds());
} else {
- callback.onStopFinished(getLastVisualDisplayBounds());
+ cb.onStopFinished(getLastVisualDisplayBounds());
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTransitionCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTransitionCallback.java
index 3af7c4b..e829186 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTransitionCallback.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTransitionCallback.java
@@ -24,6 +24,12 @@
*/
public interface OneHandedTransitionCallback {
/**
+ * Called when one handed mode entering or exiting transition starting
+ */
+ default void onStartTransition(boolean isEntering) {
+ }
+
+ /**
* Called when start one handed transition finished
*/
default void onStartFinished(Rect bounds) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 0a148c4..c2ec1c5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -232,7 +232,8 @@
}
}
- private void onInputEvent(InputEvent ev) {
+ @VisibleForTesting
+ void onInputEvent(InputEvent ev) {
// Don't allow resize when PiP is stashed.
if (mPipBoundsState.isStashed()) {
return;
@@ -366,7 +367,8 @@
return mIsSysUiStateValid;
}
- private void onPinchResize(MotionEvent ev) {
+ @VisibleForTesting
+ void onPinchResize(MotionEvent ev) {
int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
@@ -411,7 +413,7 @@
if (!mThresholdCrossed
&& (distanceBetween(mDownSecondPoint, mLastSecondPoint) > mTouchSlop
|| distanceBetween(mDownPoint, mLastPoint) > mTouchSlop)) {
- mInputMonitor.pilferPointers();
+ pilferPointers();
mThresholdCrossed = true;
// Reset the down to begin resizing from this point
mDownPoint.set(mLastPoint);
@@ -548,6 +550,17 @@
return mUserResizeBounds;
}
+ @VisibleForTesting
+ Rect getLastResizeBounds() {
+ return mLastResizeBounds;
+ }
+
+ @VisibleForTesting
+ void pilferPointers() {
+ mInputMonitor.pilferPointers();
+ }
+
+
@VisibleForTesting public void updateMaxSize(int maxX, int maxY) {
mMaxSize.set(maxX, maxY);
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 134d00b..741773e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -22,6 +22,7 @@
import android.system.helpers.ActivityHelper
import android.util.Log
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.compatibility.common.util.SystemUtil
import com.android.server.wm.flicker.FlickerBuilderProvider
@@ -138,7 +139,7 @@
append("$primaryApp $secondaryApp")
}
- @Presubmit
+ @FlakyTest(bugId = 186510496)
@Test
open fun navBarLayerIsAlwaysVisible() {
testSpec.navBarLayerIsAlwaysVisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
index 58e9204..04f97c8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.provider.Settings
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -44,7 +44,6 @@
*
* To run this test: `atest WMShellFlickerTests:EnterSplitScreenNotSupportNonResizable`
*/
-@Postsubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -94,6 +93,7 @@
prevSupportNonResizableInMultiWindow)
}
+ @Presubmit
@Test
fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
index 91ca7c1..2832bb4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.provider.Settings
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -43,7 +43,6 @@
*
* To run this test: `atest WMShellFlickerTests:EnterSplitScreenSupportNonResizable`
*/
-@Postsubmit
@RequiresDevice
@RunWith(Parameterized::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@@ -91,9 +90,11 @@
prevSupportNonResizableInMultiWindow)
}
+ @Presubmit
@Test
fun dockedStackDividerIsVisible() = testSpec.dockedStackDividerIsVisible()
+ @Presubmit
@Test
fun appWindowIsVisible() {
testSpec.assertWmEnd {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
index 968aff1..32afd19 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.provider.Settings
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -46,7 +46,6 @@
* support non-resizable in multi window, it should trigger exit split screen.
* To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromIntentNotSupportNonResizable`
*/
-@Postsubmit
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -95,25 +94,31 @@
prevSupportNonResizableInMultiWindow)
}
+ @Presubmit
@Test
fun resizableAppLayerBecomesInvisible() =
testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+ @Presubmit
@Test
fun nonResizableAppLayerBecomesVisible() =
testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+ @Presubmit
@Test
fun resizableAppWindowBecomesInvisible() =
testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+ @Presubmit
@Test
fun nonResizableAppWindowBecomesVisible() =
testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+ @Presubmit
@Test
fun dockedStackDividerIsInvisibleAtEnd() = testSpec.dockedStackDividerIsInvisible()
+ @Presubmit
@Test
fun onlyNonResizableAppWindowIsVisibleAtEnd() {
testSpec.assertWmEnd {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
index 8d20673..af30758 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.provider.Settings
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -44,7 +44,6 @@
* non-resizable in multi window, it should show the non-resizable app in split screen.
* To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromIntentSupportNonResizable`
*/
-@Postsubmit
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -93,17 +92,21 @@
prevSupportNonResizableInMultiWindow)
}
+ @Presubmit
@Test
fun nonResizableAppLayerBecomesVisible() =
testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+ @Presubmit
@Test
fun nonResizableAppWindowBecomesVisible() =
testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+ @Presubmit
@Test
fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisible()
+ @Presubmit
@Test
fun bothAppsWindowsAreVisibleAtEnd() {
testSpec.assertWmEnd {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
index 4e291d9..8c62758 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.provider.Settings
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -47,7 +47,6 @@
* not support non-resizable in multi window, it should trigger exit split screen.
* To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromRecentNotSupportNonResizable`
*/
-@Postsubmit
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -96,25 +95,31 @@
prevSupportNonResizableInMultiWindow)
}
+ @Presubmit
@Test
fun resizableAppLayerBecomesInvisible() =
testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
+ @Presubmit
@Test
fun nonResizableAppLayerBecomesVisible() =
testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+ @Presubmit
@Test
fun resizableAppWindowBecomesInvisible() =
testSpec.appWindowBecomesInVisible(splitScreenApp.defaultWindowName)
+ @Presubmit
@Test
fun nonResizableAppWindowBecomesVisible() =
testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+ @Presubmit
@Test
fun dockedStackDividerIsInvisibleAtEnd() = testSpec.dockedStackDividerIsInvisible()
+ @Presubmit
@Test
fun onlyNonResizableAppWindowIsVisibleAtEnd() {
testSpec.assertWmEnd {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
index 880dc55..5b48f8a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt
@@ -16,7 +16,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
-import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
import android.provider.Settings
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -45,7 +45,6 @@
* supports non-resizable in multi window, it should show the non-resizable app in split screen.
* To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromRecentSupportNonResizable`
*/
-@Postsubmit
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -94,17 +93,21 @@
prevSupportNonResizableInMultiWindow)
}
+ @Presubmit
@Test
fun nonResizableAppLayerBecomesVisible() =
testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
+ @Presubmit
@Test
fun nonResizableAppWindowBecomesVisible() =
testSpec.appWindowBecomesVisible(nonResizeableApp.defaultWindowName)
+ @Presubmit
@Test
fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisible()
+ @Presubmit
@Test
fun bothAppsWindowsAreVisibleAtEnd() {
testSpec.assertWmEnd {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index a7e1d0f..95672f4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -65,7 +65,7 @@
@Test
fun pipLayerBecomesVisible() {
testSpec.assertLayers {
- this.isVisible(pipApp.launcherName)
+ this.isVisible(pipApp.windowName)
}
}
@@ -73,9 +73,11 @@
@Test
fun pipWindowBecomesVisible() {
testSpec.assertWm {
- invoke("pipWindowIsNotVisible") { !it.wmState.hasPipWindow() }
- .then()
- .invoke("pipWindowIsVisible") { it.wmState.hasPipWindow() }
+ invoke("pipWindowIsNotVisible") {
+ verify("Has no pip window").that(it.wmState.hasPipWindow()).isTrue()
+ }.then().invoke("pipWindowIsVisible") {
+ verify("Has pip window").that(it.wmState.hasPipWindow()).isTrue()
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index 0d686f5..fb7dac3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -71,7 +71,7 @@
}
}
- @Presubmit
+ @FlakyTest(bugId = 185400889)
@Test
override fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
testSpec.config.endRotation, allStates = false)
@@ -88,7 +88,7 @@
testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
testSpec.config.endRotation)
- @Presubmit
+ @FlakyTest(bugId = 185400889)
@Test
fun appLayerRotates_StartingBounds() {
testSpec.assertLayersStart {
@@ -97,7 +97,7 @@
}
}
- @Presubmit
+ @FlakyTest(bugId = 185400889)
@Test
fun appLayerRotates_EndingBounds() {
testSpec.assertLayersEnd {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
index b0de029..2b5cd60 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
@@ -69,6 +69,7 @@
info.configuration.windowConfiguration.setActivityType(mActivityType);
info.token = mToken;
info.isResizeable = true;
+ info.supportsMultiWindow = true;
return info;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index 9a80a55..2bb7204 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -19,6 +19,8 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static junit.framework.TestCase.assertEquals;
+
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
@@ -799,6 +801,15 @@
assertExpandedChangedTo(false);
}
+ @Test
+ public void test_addToOverflow_doesntAllowDupes() {
+ assertEquals(0, mBubbleData.getOverflowBubbles().size());
+ mBubbleData.overflowBubble(Bubbles.DISMISS_AGED, mBubbleA1);
+ mBubbleData.overflowBubble(Bubbles.DISMISS_AGED, mBubbleA1);
+ mBubbleData.overflowBubble(Bubbles.DISMISS_AGED, mBubbleA1);
+ assertEquals(1, mBubbleData.getOverflowBubbles().size());
+ }
+
private void verifyUpdateReceived() {
verify(mListener).applyUpdate(mUpdateCaptor.capture());
reset(mListener);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
new file mode 100644
index 0000000..dd10aa7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip.phone;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Rect;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.MotionEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.FloatingContentCoordinator;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipSnapAlgorithm;
+import com.android.wm.shell.pip.PipTaskOrganizer;
+import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.PipUiEventLogger;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests against {@link PipResizeGestureHandler}
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class PipResizeGestureHandlerTest extends ShellTestCase {
+ private static final int STEP_SIZE = 40;
+ private final MotionEvent.PointerProperties[] mPp = new MotionEvent.PointerProperties[2];
+
+ @Mock
+ private PhonePipMenuController mPhonePipMenuController;
+
+ @Mock
+ private PipTaskOrganizer mPipTaskOrganizer;
+
+ @Mock
+ private PipDismissTargetHandler mPipDismissTargetHandler;
+
+ @Mock
+ private PipTransitionController mMockPipTransitionController;
+
+ @Mock
+ private FloatingContentCoordinator mFloatingContentCoordinator;
+
+ @Mock
+ private PipUiEventLogger mPipUiEventLogger;
+
+ @Mock
+ private ShellExecutor mMainExecutor;
+
+ private PipResizeGestureHandler mPipResizeGestureHandler;
+
+ private PipBoundsState mPipBoundsState;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mPipBoundsState = new PipBoundsState(mContext);
+ final PipSnapAlgorithm pipSnapAlgorithm = new PipSnapAlgorithm();
+ final PipBoundsAlgorithm pipBoundsAlgorithm = new PipBoundsAlgorithm(mContext,
+ mPipBoundsState, pipSnapAlgorithm);
+ final PipMotionHelper motionHelper = new PipMotionHelper(mContext, mPipBoundsState,
+ mPipTaskOrganizer, mPhonePipMenuController, pipSnapAlgorithm,
+ mMockPipTransitionController, mFloatingContentCoordinator);
+ mPipResizeGestureHandler = new PipResizeGestureHandler(mContext, pipBoundsAlgorithm,
+ mPipBoundsState, motionHelper, mPipTaskOrganizer, mPipDismissTargetHandler,
+ (Rect bounds) -> new Rect(), () -> {}, mPipUiEventLogger, mPhonePipMenuController,
+ mMainExecutor) {
+ @Override
+ public void pilferPointers() {
+ // Overridden just to avoid calling into InputMonitor.
+ }
+ };
+
+ for (int i = 0; i < 2; i++) {
+ MotionEvent.PointerProperties pointerProperty = new MotionEvent.PointerProperties();
+ pointerProperty.id = i;
+ pointerProperty.toolType = MotionEvent.TOOL_TYPE_FINGER;
+ mPp[i] = pointerProperty;
+ }
+
+ mPipResizeGestureHandler.init();
+ mPipResizeGestureHandler.onSystemUiStateChanged(true);
+ }
+
+ @Test
+ public void twoInput_triggersPinchResize_getBigger() {
+ assertTrue(mPipResizeGestureHandler.isUsingPinchToZoom());
+
+ int topLeft = 200;
+ int bottomRight = 500;
+ mPipBoundsState.setBounds(new Rect(topLeft, topLeft, bottomRight, bottomRight));
+
+ // Start inside the PiP bounds first.
+ topLeft += STEP_SIZE;
+ bottomRight -= STEP_SIZE;
+ MotionEvent downEvent =
+ obtainMotionEvent(MotionEvent.ACTION_POINTER_DOWN, topLeft, bottomRight);
+ assertTrue(mPipResizeGestureHandler.willStartResizeGesture(downEvent));
+
+ // Slowly move outward.
+ topLeft -= STEP_SIZE;
+ bottomRight += STEP_SIZE;
+ MotionEvent moveEvent1 = obtainMotionEvent(MotionEvent.ACTION_MOVE, topLeft, bottomRight);
+ mPipResizeGestureHandler.onPinchResize(moveEvent1);
+
+ // Move outward more.
+ topLeft -= STEP_SIZE;
+ bottomRight += STEP_SIZE;
+ MotionEvent moveEvent2 = obtainMotionEvent(MotionEvent.ACTION_MOVE, topLeft, bottomRight);
+ mPipResizeGestureHandler.onPinchResize(moveEvent2);
+
+ verify(mPipTaskOrganizer, times(2))
+ .scheduleUserResizePip(any(), any(), anyFloat(), any());
+
+ MotionEvent upEvent = obtainMotionEvent(MotionEvent.ACTION_UP, topLeft, bottomRight);
+ mPipResizeGestureHandler.onPinchResize(upEvent);
+
+ verify(mPipTaskOrganizer, times(1))
+ .scheduleAnimateResizePip(any(), any(), anyInt(), anyFloat(), any());
+
+ assertTrue("The new size should be bigger than the original PiP size.",
+ mPipResizeGestureHandler.getLastResizeBounds().width()
+ > mPipBoundsState.getBounds().width());
+ }
+
+ @Test
+ public void twoInput_triggersPinchResize_getSmaller() {
+ assertTrue(mPipResizeGestureHandler.isUsingPinchToZoom());
+
+ int topLeft = 200;
+ int bottomRight = 500;
+ mPipBoundsState.setBounds(new Rect(topLeft, topLeft, bottomRight, bottomRight));
+
+
+ topLeft += STEP_SIZE;
+ bottomRight -= STEP_SIZE;
+ MotionEvent downEvent =
+ obtainMotionEvent(MotionEvent.ACTION_POINTER_DOWN, topLeft, bottomRight);
+ assertTrue(mPipResizeGestureHandler.willStartResizeGesture(downEvent));
+
+ topLeft += STEP_SIZE;
+ bottomRight -= STEP_SIZE;
+ MotionEvent moveEvent1 = obtainMotionEvent(MotionEvent.ACTION_MOVE, topLeft, bottomRight);
+ mPipResizeGestureHandler.onPinchResize(moveEvent1);
+
+ topLeft += STEP_SIZE;
+ bottomRight -= STEP_SIZE;
+ MotionEvent moveEvent2 = obtainMotionEvent(MotionEvent.ACTION_MOVE, topLeft, bottomRight);
+ mPipResizeGestureHandler.onPinchResize(moveEvent2);
+
+ verify(mPipTaskOrganizer, times(2))
+ .scheduleUserResizePip(any(), any(), anyFloat(), any());
+
+ MotionEvent upEvent = obtainMotionEvent(MotionEvent.ACTION_UP, topLeft, bottomRight);
+ mPipResizeGestureHandler.onPinchResize(upEvent);
+
+ verify(mPipTaskOrganizer, times(1))
+ .scheduleAnimateResizePip(any(), any(), anyInt(), anyFloat(), any());
+
+ assertTrue("The new size should be smaller than the original PiP size.",
+ mPipResizeGestureHandler.getLastResizeBounds().width()
+ < mPipBoundsState.getBounds().width());
+ }
+
+ private MotionEvent obtainMotionEvent(int action, int topLeft, int bottomRight) {
+ final MotionEvent.PointerCoords[] pc = new MotionEvent.PointerCoords[2];
+ for (int i = 0; i < 2; i++) {
+ MotionEvent.PointerCoords pointerCoord = new MotionEvent.PointerCoords();
+ if (i == 0) {
+ pointerCoord.x = topLeft;
+ pointerCoord.y = topLeft;
+ } else {
+ pointerCoord.x = bottomRight;
+ pointerCoord.y = bottomRight;
+ }
+ pc[i] = pointerCoord;
+ }
+ return MotionEvent.obtain(0 /* downTime */,
+ System.currentTimeMillis(),
+ action,
+ 2 /* pointerCount */,
+ mPp,
+ pc,
+ 0 /* metaState */,
+ 0 /* buttonState */,
+ 0 /* xPrecision */,
+ 0 /* yPrecision */,
+ 0 /* deviceId */,
+ 0 /* edgeFlags */,
+ 0 /* source */,
+ 0 /* flags */);
+ }
+}
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 2a70f0d..cb620cc 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -70,9 +70,6 @@
} // namespace
-LoadedPackage::LoadedPackage() = default;
-LoadedPackage::~LoadedPackage() = default;
-
// Precondition: The header passed in has already been verified, so reading any fields and trusting
// the ResChunk_header is safe.
static bool VerifyResTableType(incfs::map_ptr<ResTable_type> header) {
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 119f531..10666ad 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -94,6 +94,7 @@
};
AssetManager2();
+ explicit AssetManager2(AssetManager2&& other) = default;
// Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets
// are not owned by the AssetManager, and must have a longer lifetime.
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index d9225cd..3b222c5 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -153,8 +153,6 @@
static std::unique_ptr<const LoadedPackage> Load(const Chunk& chunk,
package_property_t property_flags);
- ~LoadedPackage();
-
// Finds the entry with the specified type name and entry name. The names are in UTF-16 because
// the underlying ResStringPool API expects this. For now this is acceptable, but since
// the default policy in AAPT2 is to build UTF-8 string pools, this needs to change.
@@ -275,7 +273,7 @@
private:
DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
- LoadedPackage();
+ LoadedPackage() = default;
ResStringPool type_string_pool_;
ResStringPool key_string_pool_;
diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h
index 64924f4..6fc6d64 100644
--- a/libs/androidfw/include/androidfw/MutexGuard.h
+++ b/libs/androidfw/include/androidfw/MutexGuard.h
@@ -18,6 +18,7 @@
#define ANDROIDFW_MUTEXGUARD_H
#include <mutex>
+#include <optional>
#include <type_traits>
#include "android-base/macros.h"
@@ -47,34 +48,32 @@
static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer");
public:
- explicit Guarded() : guarded_() {
+ Guarded() : guarded_(std::in_place, T()) {
}
- template <typename U = T>
- explicit Guarded(const T& guarded,
- typename std::enable_if<std::is_copy_constructible<U>::value>::type = void())
- : guarded_(guarded) {
+ explicit Guarded(const T& guarded) : guarded_(std::in_place, guarded) {
}
- template <typename U = T>
- explicit Guarded(T&& guarded,
- typename std::enable_if<std::is_move_constructible<U>::value>::type = void())
- : guarded_(std::move(guarded)) {
+ explicit Guarded(T&& guarded) : guarded_(std::in_place, std::forward<T>(guarded)) {
+ }
+
+ ~Guarded() {
+ std::lock_guard<std::mutex> scoped_lock(lock_);
+ guarded_.reset();
}
private:
friend class ScopedLock<T>;
-
DISALLOW_COPY_AND_ASSIGN(Guarded);
std::mutex lock_;
- T guarded_;
+ std::optional<T> guarded_;
};
template <typename T>
class ScopedLock {
public:
- explicit ScopedLock(Guarded<T>& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) {
+ explicit ScopedLock(Guarded<T>& guarded) : lock_(guarded.lock_), guarded_(*guarded.guarded_) {
}
T& operator*() {
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 087837a..7540a14 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -449,7 +449,7 @@
sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
- transaction->setCrop(surfaceControl, sourceRect);
+ transaction->setBufferCrop(surfaceControl, sourceRect);
float dsdx = (destination.right - destination.left) /
static_cast<float>(sourceRect.right - sourceRect.left);
diff --git a/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm b/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
index 2a95cfe..44d5a0c 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_arabic.kcm
@@ -85,14 +85,14 @@
key 9 {
label: '9'
base: '\u0669'
- shift: '('
+ shift: ')'
capslock: '9'
}
key 0 {
label: '0'
base: '\u0660'
- shift: ')'
+ shift: '('
capslock: '0'
}
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index f5641bd..83a838f 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -26,6 +26,7 @@
import androidx.annotation.Nullable;
import com.google.android.material.appbar.CollapsingToolbarLayout;
+import com.google.android.material.resources.TextAppearanceConfig;
/**
* A base Activity that has a collapsing toolbar layout is used for the activities intending to
@@ -39,7 +40,8 @@
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
+ // Force loading font synchronously for collapsing toolbar layout
+ TextAppearanceConfig.setShouldLoadFontSynchronously(true);
super.setContentView(R.layout.collapsing_toolbar_base_layout);
mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
index 0287b1f..4d6e1b7 100644
--- a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
@@ -19,9 +19,10 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:paddingStart="20dp"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingBottom="16dp"
+ android:paddingTop="8dp"
android:background="?android:attr/selectableItemBackground"
android:clipToPadding="false">
@@ -29,8 +30,6 @@
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingBottom="16dp"
- android:paddingTop="16dp"
android:clickable="false"
android:longClickable="false"
android:maxLines="10"
diff --git a/packages/SettingsLib/TopIntroPreference/res/values/styles.xml b/packages/SettingsLib/TopIntroPreference/res/values/styles.xml
index e7eb9f4..65869b5 100644
--- a/packages/SettingsLib/TopIntroPreference/res/values/styles.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/values/styles.xml
@@ -17,7 +17,7 @@
<resources>
<style name="TextAppearance.TopIntroText"
parent="@*android:style/TextAppearance.DeviceDefault">
- <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
<item name="android:textSize">14sp</item>
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index fbb84fd..ccbcb89 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -256,4 +256,7 @@
<!-- Default for Settings.Secure.ACCESSIBILITY_BUTTON_MODE -->
<integer name="def_accessibility_button_mode">1</integer>
+ <!-- Default for Settings.Secure.ONE_HANDED_MODE_ACTIVATED -->
+ <bool name="def_one_handed_mode_activated">false</bool>
+
</resources>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 72fb237..1cfdff8 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -173,6 +173,7 @@
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
+ Settings.Secure.ONE_HANDED_MODE_ACTIVATED,
Settings.Secure.ONE_HANDED_MODE_ENABLED,
Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
Settings.Secure.TAPS_APP_TO_EXIT,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 4872aa4..36f5dba 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -262,6 +262,7 @@
VALIDATORS.put(
Secure.ACCESSIBILITY_BUTTON_TARGETS,
ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
+ VALIDATORS.put(Secure.ONE_HANDED_MODE_ACTIVATED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ONE_HANDED_MODE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ONE_HANDED_MODE_TIMEOUT, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.TAPS_APP_TO_EXIT, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2e90d36..941f47f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3401,7 +3401,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 201;
+ private static final int SETTINGS_VERSION = 202;
private final int mUserId;
@@ -4959,6 +4959,22 @@
currentVersion = 201;
}
+ if (currentVersion == 201) {
+ // Version 201: Set the default value for Secure Settings:
+ final SettingsState secureSettings = getSecureSettingsLocked(userId);
+ final Setting oneHandedModeActivated = secureSettings.getSettingLocked(
+ Secure.ONE_HANDED_MODE_ACTIVATED);
+ if (oneHandedModeActivated.isNull()) {
+ final boolean defOneHandedModeActivated = getContext().getResources()
+ .getBoolean(R.bool.def_one_handed_mode_activated);
+ secureSettings.insertSettingLocked(
+ Secure.ONE_HANDED_MODE_ACTIVATED,
+ defOneHandedModeActivated ? "1" : "0", null, true,
+ SettingsState.SYSTEM_PACKAGE_NAME);
+ }
+ currentVersion = 202;
+ }
+
// vXXX: Add new settings above this point.
if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
index ca13204..2213d1c 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
@@ -46,6 +46,15 @@
* background circle/peripherals. To retrieve only the inner icon, use {@link #getIcon()}.
*/
public abstract View getIconWithBackground();
+
+ /**
+ * Returns the {@link View} containing the icon on the right
+ *
+ * @see com.android.systemui.qs.tileimpl.QSTileViewHorizontal#sideView
+ */
+ public View getSecondaryIcon() {
+ return null;
+ }
public abstract void init(QSTile tile);
public abstract void onStateChanged(State state);
@@ -54,4 +63,8 @@
public View getLabelContainer() {
return null;
}
+
+ public View getSecondaryLabel() {
+ return null;
+ }
}
diff --git a/packages/SystemUI/res/layout/disabled_udfps_view.xml b/packages/SystemUI/res/drawable/qs_media_art_background.xml
similarity index 64%
copy from packages/SystemUI/res/layout/disabled_udfps_view.xml
copy to packages/SystemUI/res/drawable/qs_media_art_background.xml
index 13d3065..95a1870 100644
--- a/packages/SystemUI/res/layout/disabled_udfps_view.xml
+++ b/packages/SystemUI/res/drawable/qs_media_art_background.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2020 The Android Open Source Project
+ ~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -12,11 +12,9 @@
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
- ~ limitations under the License.
+ ~ limitations under the License
-->
-<com.android.keyguard.DisabledUdfpsView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/disabled_udfps_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
-/>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="@dimen/qs_media_album_radius"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/qs_media_button_background.xml b/packages/SystemUI/res/drawable/qs_media_button_background.xml
new file mode 100644
index 0000000..2241abf
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_media_button_background.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <stroke
+ android:color="?androidprv:attr/colorAccentPrimaryVariant"
+ android:width="1dp"/>
+ <corners android:radius="24dp"/>
+ <padding
+ android:left="16dp"
+ android:right="16dp"
+ android:top="8dp"
+ android:bottom="8dp" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/disabled_udfps_view.xml b/packages/SystemUI/res/drawable/qs_media_icon_background.xml
similarity index 64%
rename from packages/SystemUI/res/layout/disabled_udfps_view.xml
rename to packages/SystemUI/res/drawable/qs_media_icon_background.xml
index 13d3065..a3a2986 100644
--- a/packages/SystemUI/res/layout/disabled_udfps_view.xml
+++ b/packages/SystemUI/res/drawable/qs_media_icon_background.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2020 The Android Open Source Project
+ ~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -12,11 +12,12 @@
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
- ~ limitations under the License.
+ ~ limitations under the License
-->
-<com.android.keyguard.DisabledUdfpsView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/disabled_udfps_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
-/>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid android:color="?android:attr/colorBackground" />
+ <size
+ android:width="20dp"
+ android:height="20dp" />
+</shape>
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml b/packages/SystemUI/res/drawable/qs_media_seamless_background.xml
similarity index 85%
rename from packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml
rename to packages/SystemUI/res/drawable/qs_media_seamless_background.xml
index 8e37686..e71c3d3 100644
--- a/packages/SystemUI/res-keyguard/drawable/qs_media_seamless_background.xml
+++ b/packages/SystemUI/res/drawable/qs_media_seamless_background.xml
@@ -20,6 +20,11 @@
<shape android:shape="rectangle">
<solid android:color="@color/media_seamless_border" />
<corners android:radius="24dp"/>
+ <padding
+ android:left="8dp"
+ android:right="8dp"
+ android:top="4dp"
+ android:bottom="4dp" />
</shape>
</item>
</ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
index f4a7434..9210d05 100644
--- a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
+++ b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
@@ -89,30 +89,18 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
- android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
- android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
- android:id="@+id/recommendation_text"
- android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- android:textColor="?android:attr/textColorSecondary"
- android:text="@string/controls_media_title"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintBottom_toTopOf="@id/remove_text"
- app:layout_constraintVertical_chainStyle="spread_inside"/>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
- android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginStart="@dimen/qs_media_padding"
+ android:layout_marginEnd="@dimen/qs_media_padding"
android:id="@+id/remove_text"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:singleLine="true"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/controls_media_close_session"
- app:layout_constraintTop_toBottomOf="@id/recommendation_text"
+ android:gravity="center_horizontal|top"
+ app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/settings"/>
<FrameLayout
@@ -120,8 +108,8 @@
android:background="@drawable/qs_media_light_source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
- android:paddingBottom="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginStart="@dimen/qs_media_padding"
+ android:paddingBottom="@dimen/qs_media_padding"
android:minWidth="48dp"
android:minHeight="48dp"
app:layout_constraintBottom_toBottomOf="parent"
@@ -132,6 +120,7 @@
android:layout_gravity="bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:background="@drawable/qs_media_button_background"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/controls_media_settings_button" />
@@ -142,8 +131,8 @@
android:background="@drawable/qs_media_light_source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
- android:paddingBottom="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginEnd="8dp"
+ android:paddingBottom="@dimen/qs_media_padding"
android:minWidth="48dp"
android:minHeight="48dp"
app:layout_constraintBottom_toBottomOf="parent"
@@ -153,6 +142,7 @@
android:layout_gravity="bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:background="@drawable/qs_media_button_background"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/cancel" />
@@ -163,8 +153,8 @@
android:background="@drawable/qs_media_light_source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
- android:paddingBottom="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginEnd="@dimen/qs_media_padding"
+ android:paddingBottom="@dimen/qs_media_padding"
android:minWidth="48dp"
android:minHeight="48dp"
app:layout_constraintBottom_toBottomOf="parent"
@@ -174,6 +164,7 @@
android:layout_gravity="bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:background="@drawable/qs_media_button_background"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/controls_media_dismiss_button"
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index a4cf5ed..cdced5a 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -18,6 +18,7 @@
<!-- Layout for media controls inside QSPanel carousel -->
<com.android.systemui.util.animation.TransitionLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/qs_media_controls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -32,8 +33,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
- app:layout_constraintGuide_percent="0.5"
- />
+ app:layout_constraintGuide_percent="0.6" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/center_horizontal_guideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_percent="0.5" />
<!-- As per Material Design on Biderectionality, this is forced to LTR in code -->
<FrameLayout
@@ -50,7 +57,7 @@
android:fontFamily="@*android:string/config_bodyFontFamily"
android:textColor="?android:attr/textColorPrimary"
android:gravity="start"
- android:textSize="14sp" />
+ android:textSize="12sp" />
<TextView
android:id="@+id/media_total_time"
@@ -60,11 +67,11 @@
android:fontFamily="@*android:string/config_bodyFontFamily"
android:textColor="?android:attr/textColorPrimary"
android:gravity="end"
- android:textSize="14sp" />
+ android:textSize="12sp" />
</FrameLayout>
<!-- Actions must be ordered left-to-right even in RTL layout. However, they appear in a chain
- with the album art, and must as a group appear at the end of that chain. This is
+ with the artist name, and must as a group appear at the end of that chain. This is
accomplished by having all actions appear in a LTR chain within the parent, and then biasing it
to the right side, then this barrier is used to bound the text views. -->
<androidx.constraintlayout.widget.Barrier
@@ -72,10 +79,10 @@
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
- app:layout_constraintTop_toBottomOf="@id/header_artist"
+ app:layout_constraintTop_toBottomOf="@id/header_title"
app:barrierDirection="start"
app:constraint_referenced_ids="action0,action1,action2,action3,action4"
- app:layout_constraintHorizontal_bias="0" />
+ />
<ImageButton
android:id="@+id/action0"
@@ -92,8 +99,8 @@
<ImageButton
android:id="@+id/action2"
style="@style/MediaPlayer.Button"
- android:layout_width="52dp"
- android:layout_height="52dp" />
+ android:layout_width="48dp"
+ android:layout_height="48dp" />
<ImageButton
android:id="@+id/action3"
@@ -112,23 +119,27 @@
android:id="@+id/album_art"
android:layout_width="@dimen/qs_media_album_size"
android:layout_height="@dimen/qs_media_album_size"
- android:layout_gravity="center_vertical" />
+ android:layout_gravity="center_vertical"
+ android:background="@drawable/qs_media_art_background"
+ android:backgroundTint="?androidprv:attr/colorAccentSecondary"
+ android:clipToOutline="true" />
<!-- Seamless Output Switcher -->
<LinearLayout
android:id="@+id/media_seamless"
android:layout_width="0dp"
- android:layout_height="wrap_content"
+ android:layout_height="48dp"
android:orientation="horizontal"
- android:gravity="center"
+ android:gravity="top|end"
+ android:paddingTop="@dimen/qs_media_padding"
+ android:paddingEnd="@dimen/qs_media_padding"
android:background="@drawable/qs_media_light_source"
android:forceHasOverlappingRendering="false">
<LinearLayout
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_height="@dimen/qs_seamless_height"
android:background="@drawable/qs_media_seamless_background"
android:orientation="horizontal"
- android:padding="6dp"
android:contentDescription="@string/quick_settings_media_device_label">
<ImageView
android:id="@+id/media_seamless_image"
@@ -138,25 +149,28 @@
android:tint="?android:attr/colorPrimary"
android:src="@*android:drawable/ic_media_seamless" />
<TextView
- android:visibility="gone"
android:id="@+id/media_seamless_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:layout_marginStart="8dp"
+ android:layout_marginStart="4dp"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:singleLine="true"
android:text="@*android:string/ext_media_seamless_action"
android:textColor="?android:attr/colorPrimary"
android:textDirection="locale"
- android:textSize="14sp" />
+ android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
<ImageView
android:id="@+id/media_seamless_fallback"
- android:layout_width="@dimen/qs_seamless_icon_size"
- android:layout_height="@dimen/qs_seamless_icon_size"
+ android:layout_width="@dimen/qs_seamless_fallback_icon_size"
+ android:layout_height="@dimen/qs_seamless_fallback_icon_size"
+ android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_media_padding"
+ android:layout_marginStart="@dimen/qs_center_guideline_padding"
+ android:layout_marginEnd="@dimen/qs_seamless_fallback_margin"
android:tint="?android:attr/textColorPrimary"
android:src="@drawable/ic_cast_connected"
android:forceHasOverlappingRendering="false" />
@@ -170,27 +184,18 @@
android:layout_height="wrap_content"
android:clickable="true"
android:maxHeight="@dimen/qs_media_enabled_seekbar_height"
- android:paddingVertical="@dimen/qs_media_enabled_seekbar_vertical_padding"
+ android:paddingTop="@dimen/qs_media_enabled_seekbar_vertical_padding"
+ android:layout_marginTop="-22dp"
+ android:paddingBottom="0dp"
android:thumbTint="?android:attr/textColorPrimary"
android:progressTint="?android:attr/textColorPrimary"
- android:progressBackgroundTint="?android:attr/colorBackground"
+ android:progressBackgroundTint="?android:attr/textColorTertiary"
android:splitTrack="false" />
- <!-- App name -->
- <TextView
- android:id="@+id/app_name"
- android:textColor="?android:attr/textColorPrimary"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:fontFamily="@*android:string/config_headlineFontFamily"
- android:textDirection="locale"
- android:textSize="14sp" />
-
<!-- Song name -->
<TextView
android:id="@+id/header_title"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:singleLine="true"
@@ -200,7 +205,7 @@
<!-- Artist name -->
<TextView
android:id="@+id/header_artist"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:singleLine="true"
@@ -209,39 +214,28 @@
<com.android.internal.widget.CachingIconView
android:id="@+id/icon"
- android:tint="?android:attr/textColorPrimary"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_margin="6dp" />
+ android:tint="?android:attr/colorAccent"
+ android:layout_width="@dimen/qs_media_icon_size"
+ android:layout_height="@dimen/qs_media_icon_size"
+ android:background="@drawable/qs_media_icon_background"
+ />
- <!-- Constraints are set here as they are the same regardless of host -->
+ <!-- Long press menu -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
- android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
- android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
- android:id="@+id/media_text"
- android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- android:textColor="?android:attr/textColorSecondary"
- android:text="@string/controls_media_title"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintBottom_toTopOf="@id/remove_text"
- app:layout_constraintVertical_chainStyle="spread_inside"/>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
- android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginStart="@dimen/qs_media_padding"
+ android:layout_marginEnd="@dimen/qs_media_padding"
android:id="@+id/remove_text"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:singleLine="true"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/controls_media_close_session"
- app:layout_constraintTop_toBottomOf="@id/media_text"
+ android:gravity="center_horizontal|top"
+ app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/settings"/>
<FrameLayout
@@ -249,8 +243,8 @@
android:background="@drawable/qs_media_light_source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
- android:paddingBottom="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginStart="@dimen/qs_media_padding"
+ android:paddingBottom="@dimen/qs_media_padding"
android:minWidth="48dp"
android:minHeight="48dp"
app:layout_constraintBottom_toBottomOf="parent"
@@ -261,6 +255,7 @@
android:layout_gravity="bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:background="@drawable/qs_media_button_background"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/controls_media_settings_button" />
@@ -271,17 +266,19 @@
android:background="@drawable/qs_media_light_source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
- android:paddingBottom="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginEnd="8dp"
+ android:paddingBottom="@dimen/qs_media_padding"
android:minWidth="48dp"
android:minHeight="48dp"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toStartOf="@id/dismiss" >
+ app:layout_constraintEnd_toStartOf="@id/dismiss"
+ app:layout_constraintTop_toBottomOf="@id/remove_text">
<TextView
android:layout_gravity="bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:background="@drawable/qs_media_button_background"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/cancel" />
@@ -292,17 +289,19 @@
android:background="@drawable/qs_media_light_source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
- android:paddingBottom="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginEnd="@dimen/qs_media_padding"
+ android:paddingBottom="@dimen/qs_media_padding"
android:minWidth="48dp"
android:minHeight="48dp"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent">
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/remove_text">
<TextView
android:layout_gravity="bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:background="@drawable/qs_media_button_background"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/controls_media_dismiss_button"
diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
index c3f1113..666ec27 100644
--- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
+++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml
@@ -21,5 +21,6 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
- android:clipChildren="true"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:paddingBottom="@dimen/qs_paged_tile_layout_padding_bottom" />
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 30e52e9..2cafd1b 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -36,6 +36,8 @@
android:elevation="4dp"
android:importantForAccessibility="no"
android:scrollbars="none"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:layout_weight="1">
<com.android.systemui.qs.QSPanel
android:id="@+id/quick_settings_panel"
@@ -43,7 +45,9 @@
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:focusable="true"
- android:accessibilityTraversalBefore="@android:id/edit">
+ android:accessibilityTraversalBefore="@android:id/edit"
+ android:clipToPadding="false"
+ android:clipChildren="false">
<include layout="@layout/qs_footer_impl" />
<include layout="@layout/qs_media_divider"
android:id="@+id/divider"/>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 5feb957..120b3f8a 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -375,7 +375,7 @@
<bool name="config_showNotificationGear">true</bool>
<!-- Whether or not a background should be drawn behind a notification. -->
- <bool name="config_drawNotificationBackground">true</bool>
+ <bool name="config_drawNotificationBackground">false</bool>
<!-- Whether or the notifications can be shown and dismissed with a drag. -->
<bool name="config_enableNotificationShadeDrag">true</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index f7578cbd..b76a4ce 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -139,7 +139,7 @@
<dimen name="notification_min_height_before_s">106dp</dimen>
<!-- Height of a large notification in the status bar -->
- <dimen name="notification_max_height">294dp</dimen>
+ <dimen name="notification_max_height">358dp</dimen>
<!-- Height of a heads up notification in the status bar for legacy custom views -->
<dimen name="notification_max_heads_up_height_legacy">128dp</dimen>
@@ -157,7 +157,7 @@
<dimen name="notification_max_heads_up_height_increased">188dp</dimen>
<!-- Side padding on the lockscreen on the side of notifications -->
- <dimen name="notification_side_paddings">4dp</dimen>
+ <dimen name="notification_side_paddings">16dp</dimen>
<!-- padding between the heads up and the statusbar -->
<dimen name="heads_up_status_bar_padding">8dp</dimen>
@@ -179,10 +179,10 @@
<dimen name="notification_min_interaction_height">40dp</dimen>
<!-- Radius for notifications corners without adjacent notifications -->
- <dimen name="notification_corner_radius">8dp</dimen>
+ <dimen name="notification_corner_radius">28dp</dimen>
<!-- Radius for notifications corners with adjacent notifications -->
- <dimen name="notification_corner_radius_small">0dp</dimen>
+ <dimen name="notification_corner_radius_small">4dp</dimen>
<!-- the padding of the shelf icon container -->
<dimen name="shelf_icon_container_padding">13dp</dimen>
@@ -667,7 +667,7 @@
<dimen name="z_distance_between_notifications">0.5dp</dimen>
<!-- The height of the divider between the individual notifications. -->
- <dimen name="notification_divider_height">1dp</dimen>
+ <dimen name="notification_divider_height">2dp</dimen>
<!-- The corner radius of the shadow behind the notification. -->
<dimen name="notification_shadow_radius">0dp</dimen>
@@ -680,7 +680,7 @@
<dimen name="notification_children_container_divider_height">0.5dp</dimen>
<!-- The horizontal margin of the content in the notification shade -->
- <dimen name="notification_shade_content_margin_horizontal">4dp</dimen>
+ <dimen name="notification_shade_content_margin_horizontal">16dp</dimen>
<!-- The top margin for the notification children container in its non-expanded form. -->
<dimen name="notification_children_container_margin_top">
@@ -1254,26 +1254,31 @@
<!-- Size of media cards in the QSPanel carousel -->
<dimen name="qs_media_padding">16dp</dimen>
- <dimen name="qs_media_panel_outer_padding">16dp</dimen>
- <dimen name="qs_media_album_size">120dp</dimen>
+ <dimen name="qs_media_album_size_small">72dp</dimen>
+ <dimen name="qs_media_album_size">92dp</dimen>
<dimen name="qs_media_album_radius">14dp</dimen>
- <dimen name="qs_media_icon_size">16dp</dimen>
+ <dimen name="qs_media_album_device_padding">26dp</dimen>
+ <dimen name="qs_media_info_margin">12dp</dimen>
+ <dimen name="qs_media_info_spacing">4dp</dimen>
+ <dimen name="qs_media_icon_size">20dp</dimen>
+ <dimen name="qs_media_icon_offset">4dp</dimen>
<dimen name="qs_center_guideline_padding">10dp</dimen>
- <dimen name="qs_seamless_icon_size">@dimen/qs_media_icon_size</dimen>
+ <dimen name="qs_media_action_spacing">4dp</dimen>
+ <dimen name="qs_media_action_top">8dp</dimen>
+ <dimen name="qs_seamless_height">24dp</dimen>
+ <dimen name="qs_seamless_icon_size">16dp</dimen>
<dimen name="qs_seamless_fallback_icon_size">@dimen/qs_seamless_icon_size</dimen>
- <dimen name="qs_seamless_fallback_end_margin">16dp</dimen>
- <dimen name="qqs_media_spacing">16dp</dimen>
+ <dimen name="qs_seamless_fallback_margin">20dp</dimen>
<dimen name="qs_footer_horizontal_margin">22dp</dimen>
<dimen name="qs_media_disabled_seekbar_height">1dp</dimen>
- <dimen name="qs_media_enabled_seekbar_height">3dp</dimen>
- <dimen name="qs_media_enabled_seekbar_vertical_padding">15dp</dimen>
- <dimen name="qs_media_disabled_seekbar_vertical_padding">16dp</dimen>
+ <dimen name="qs_media_enabled_seekbar_height">2dp</dimen>
+ <dimen name="qs_media_enabled_seekbar_vertical_padding">35dp</dimen>
+ <dimen name="qs_media_disabled_seekbar_vertical_padding">36dp</dimen>
<!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
<dimen name="qs_aa_media_rec_album_size">80dp</dimen>
<dimen name="qs_aa_media_rec_icon_size">20dp</dimen>
-
<!-- Window magnification -->
<dimen name="magnification_border_drag_size">35dp</dimen>
<dimen name="magnification_outer_border_margin">15dp</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 5827f4e..f4d4ab8 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -20,9 +20,9 @@
<bool name="flag_notification_pipeline2">true</bool>
<bool name="flag_notification_pipeline2_rendering">false</bool>
- <bool name="flag_notif_updates">false</bool>
+ <bool name="flag_notif_updates">true</bool>
- <bool name="flag_shade_is_opaque">false</bool>
+ <bool name="flag_shade_is_opaque">true</bool>
<bool name="flag_monet">false</bool>
<!-- b/171917882 -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index a9f6946..bbb5519 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2727,7 +2727,7 @@
<string name="accessibility_floating_button_action_move_bottom_right">Move bottom right</string>
<!-- Action in accessibility menu to move the accessibility floating button to the edge and hide it to half. [CHAR LIMIT=30]-->
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half">Move to edge and hide</string>
- <!-- Action in accessibility menu to move the accessibility floating button out the edge and show. [CHAR LIMIT=30]-->
+ <!-- Action in accessibility menu to move the accessibility floating button out the edge and show. [CHAR LIMIT=36]-->
<string name="accessibility_floating_button_action_move_out_edge_and_show">Move out edge and show</string>
<!-- Device Controls strings -->
@@ -2826,10 +2826,10 @@
<!-- Title for media controls [CHAR_LIMIT=50] -->
<string name="controls_media_title">Media</string>
- <!-- Explanation for closing controls associated with a specific media session [CHAR_LIMIT=NONE] -->
- <string name="controls_media_close_session">Hide the current session.</string>
- <!-- Explanation that controls associated with a specific media session are active [CHAR_LIMIT=NONE] -->
- <string name="controls_media_active_session">Current session cannot be hidden.</string>
+ <!-- Explanation for closing controls associated with a specific media session [CHAR_LIMIT=50] -->
+ <string name="controls_media_close_session">Hide this media session?</string>
+ <!-- Explanation that controls associated with a specific media session are active [CHAR_LIMIT=50] -->
+ <string name="controls_media_active_session">The current media session cannot be hidden.</string>
<!-- Label for a button that will hide media controls [CHAR_LIMIT=30] -->
<string name="controls_media_dismiss_button">Dismiss</string>
<!-- Label for button to resume media playback [CHAR_LIMIT=NONE] -->
@@ -2971,4 +2971,7 @@
<!-- Accessibility action for tapping on an affordance on an unlocked lock screen (ie: "Double
tap to enter device") [CHAR LIMIT=NONE] -->
<string name="accessibility_enter_hint">enter device</string>
+ <!-- Message shown to suggest authentication using [CHAR LIMIT=60]-->
+ <string name="keyguard_try_fingerprint">Use fingerprint to open</string>
+
</resources>
diff --git a/packages/SystemUI/res/xml/media_collapsed.xml b/packages/SystemUI/res/xml/media_collapsed.xml
index f83e3a1..9bd462e 100644
--- a/packages/SystemUI/res/xml/media_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_collapsed.xml
@@ -21,96 +21,82 @@
android:id="@+id/icon"
android:layout_width="@dimen/qs_media_icon_size"
android:layout_height="@dimen/qs_media_icon_size"
- android:layout_marginStart="18dp"
- android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toEndOf="@id/album_art"
- />
-
- <Constraint
- android:id="@+id/app_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/qs_center_guideline_padding"
- android:layout_marginStart="8dp"
- app:layout_constraintTop_toTopOf="@id/icon"
- app:layout_constraintBottom_toBottomOf="@id/icon"
- app:layout_constraintStart_toEndOf="@id/icon"
- app:layout_constraintEnd_toStartOf="@id/center_vertical_guideline"
- app:layout_constrainedWidth="true"
- app:layout_constraintHorizontal_bias="0"
+ android:translationY="@dimen/qs_media_icon_offset"
+ android:translationX="@dimen/qs_media_icon_offset"
+ app:layout_constraintEnd_toEndOf="@id/album_art"
+ app:layout_constraintBottom_toBottomOf="@id/album_art"
/>
<Constraint
android:id="@+id/media_seamless"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_height="48dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
+ app:layout_constraintBottom_toTopOf="@id/center_horizontal_guideline"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintHorizontal_bias="1"
app:layout_constrainedWidth="true"
app:layout_constraintWidth_min="48dp"
app:layout_constraintHeight_min="48dp"
+ android:paddingTop="@dimen/qs_media_padding"
+ android:paddingEnd="@dimen/qs_media_padding"
android:layout_marginStart="@dimen/qs_center_guideline_padding"
+ android:layout_marginBottom="4dp"
/>
<Constraint
android:id="@+id/media_seamless_fallback"
android:layout_width="@dimen/qs_seamless_fallback_icon_size"
android:layout_height="@dimen/qs_seamless_fallback_icon_size"
- android:layout_marginEnd="@dimen/qs_seamless_fallback_end_margin"
+ android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_media_padding"
android:layout_marginStart="@dimen/qs_center_guideline_padding"
+ android:layout_marginEnd="@dimen/qs_seamless_fallback_margin"
android:alpha="0.5"
android:visibility="gone"
app:layout_constraintHorizontal_bias="1"
- app:layout_constraintTop_toTopOf="@id/icon"
- app:layout_constraintBottom_toBottomOf="@id/icon"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/center_horizontal_guideline"
app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
app:layout_constraintEnd_toEndOf="parent"
/>
<Constraint
android:id="@+id/album_art"
- android:layout_width="@dimen/qs_media_album_size"
- android:layout_height="@dimen/qs_media_album_size"
- android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
- android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
- android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
+ android:layout_width="@dimen/qs_media_album_size_small"
+ android:layout_height="@dimen/qs_media_album_size_small"
+ android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginStart="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_media_padding"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/media_action_barrier"
- app:layout_constraintHorizontal_bias="0"
/>
<!-- Song name -->
<Constraint
android:id="@+id/header_title"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/qqs_media_spacing"
- android:layout_marginStart="@dimen/qqs_media_spacing"
- android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginStart="@dimen/qs_media_info_margin"
+ android:layout_marginEnd="@dimen/qs_center_guideline_padding"
app:layout_constrainedWidth="true"
- app:layout_constraintTop_toBottomOf="@id/icon"
- app:layout_constraintBottom_toTopOf="@id/header_artist"
+ app:layout_constraintBottom_toTopOf="@id/center_horizontal_guideline"
app:layout_constraintStart_toEndOf="@id/album_art"
- app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/center_vertical_guideline"
app:layout_constraintHorizontal_bias="0"/>
<!-- Artist name -->
<Constraint
android:id="@+id/header_artist"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginTop="3dp"
- android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
- android:layout_marginBottom="@dimen/qqs_media_spacing"
app:layout_constrainedWidth="true"
- app:layout_constraintTop_toBottomOf="@id/header_title"
+ android:layout_marginTop="@dimen/qs_media_info_spacing"
+ app:layout_constraintTop_toTopOf="@id/center_horizontal_guideline"
app:layout_constraintStart_toStartOf="@id/header_title"
- app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/media_action_barrier"
app:layout_constraintHorizontal_bias="0"/>
<!-- Seek Bar -->
@@ -130,9 +116,6 @@
android:alpha="0.0"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginTop="35dp"
- android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
- android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
app:layout_constraintTop_toBottomOf="@id/album_art"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
@@ -143,15 +126,16 @@
android:id="@+id/action0"
android:layout_width="48dp"
android:layout_height="48dp"
- android:layout_marginStart="@dimen/qqs_media_spacing"
- android:layout_marginEnd="4dp"
- android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginStart="@dimen/qs_media_padding"
+ android:layout_marginEnd="@dimen/qs_media_action_spacing"
+ android:layout_marginTop="@dimen/qs_media_action_spacing"
android:visibility="gone"
app:layout_constraintHorizontal_chainStyle="packed"
- app:layout_constraintTop_toBottomOf="@id/header_artist"
+ app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
+ app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/action1"
- app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintHorizontal_bias="1"
>
</Constraint>
@@ -159,10 +143,10 @@
android:id="@+id/action1"
android:layout_width="48dp"
android:layout_height="48dp"
- android:layout_marginStart="4dp"
- android:layout_marginEnd="4dp"
- android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
- app:layout_constraintTop_toBottomOf="@id/header_artist"
+ android:layout_marginStart="@dimen/qs_media_action_spacing"
+ android:layout_marginEnd="@dimen/qs_media_action_spacing"
+ android:layout_marginTop="@dimen/qs_media_action_spacing"
+ app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/action0"
app:layout_constraintRight_toLeftOf="@id/action2"
@@ -173,10 +157,10 @@
android:id="@+id/action2"
android:layout_width="48dp"
android:layout_height="48dp"
- android:layout_marginStart="4dp"
- android:layout_marginEnd="4dp"
- android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
- app:layout_constraintTop_toBottomOf="@id/header_artist"
+ android:layout_marginStart="@dimen/qs_media_action_spacing"
+ android:layout_marginEnd="@dimen/qs_media_action_spacing"
+ android:layout_marginTop="@dimen/qs_media_action_spacing"
+ app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/action1"
app:layout_constraintRight_toLeftOf="@id/action3"
@@ -187,10 +171,10 @@
android:id="@+id/action3"
android:layout_width="48dp"
android:layout_height="48dp"
- android:layout_marginStart="4dp"
- android:layout_marginEnd="4dp"
- android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
- app:layout_constraintTop_toBottomOf="@id/header_artist"
+ android:layout_marginStart="@dimen/qs_media_action_spacing"
+ android:layout_marginEnd="@dimen/qs_media_action_spacing"
+ android:layout_marginTop="@dimen/qs_media_action_spacing"
+ app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/action2"
app:layout_constraintRight_toLeftOf="@id/action4"
@@ -201,12 +185,12 @@
android:id="@+id/action4"
android:layout_width="48dp"
android:layout_height="48dp"
- android:layout_marginStart="4dp"
- android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginStart="@dimen/qs_media_action_spacing"
+ android:layout_marginEnd="@dimen/qs_media_padding"
android:visibility="gone"
- android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginTop="@dimen/qs_media_action_spacing"
app:layout_constraintHorizontal_chainStyle="packed"
- app:layout_constraintTop_toBottomOf="@id/header_artist"
+ app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/action3"
app:layout_constraintRight_toRightOf="parent"
diff --git a/packages/SystemUI/res/xml/media_expanded.xml b/packages/SystemUI/res/xml/media_expanded.xml
index 7c67720..fd04fa0 100644
--- a/packages/SystemUI/res/xml/media_expanded.xml
+++ b/packages/SystemUI/res/xml/media_expanded.xml
@@ -21,30 +21,16 @@
android:id="@+id/icon"
android:layout_width="@dimen/qs_media_icon_size"
android:layout_height="@dimen/qs_media_icon_size"
- android:layout_marginStart="18dp"
- android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toEndOf="@id/album_art"
- />
-
- <Constraint
- android:id="@+id/app_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/qs_center_guideline_padding"
- android:layout_marginStart="8dp"
- app:layout_constraintTop_toTopOf="@id/icon"
- app:layout_constraintBottom_toBottomOf="@id/icon"
- app:layout_constraintStart_toEndOf="@id/icon"
- app:layout_constraintEnd_toStartOf="@id/center_vertical_guideline"
- app:layout_constrainedWidth="true"
- app:layout_constraintHorizontal_bias="0"
- />
+ android:translationY="@dimen/qs_media_icon_offset"
+ android:translationX="@dimen/qs_media_icon_offset"
+ app:layout_constraintEnd_toEndOf="@id/album_art"
+ app:layout_constraintBottom_toBottomOf="@id/album_art"
+ />
<Constraint
android:id="@+id/media_seamless"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_height="48dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
@@ -53,20 +39,23 @@
app:layout_constrainedWidth="true"
app:layout_constraintWidth_min="48dp"
app:layout_constraintHeight_min="48dp"
+ android:paddingTop="@dimen/qs_media_padding"
+ android:paddingEnd="@dimen/qs_media_padding"
android:layout_marginStart="@dimen/qs_center_guideline_padding"
- />
+ android:layout_marginBottom="4dp" />
<Constraint
android:id="@+id/media_seamless_fallback"
android:layout_width="@dimen/qs_seamless_fallback_icon_size"
android:layout_height="@dimen/qs_seamless_fallback_icon_size"
- android:layout_marginEnd="@dimen/qs_seamless_fallback_end_margin"
+ android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginBottom="16dp"
android:layout_marginStart="@dimen/qs_center_guideline_padding"
+ android:layout_marginEnd="@dimen/qs_seamless_fallback_margin"
android:alpha="0.5"
android:visibility="gone"
app:layout_constraintHorizontal_bias="1"
- app:layout_constraintTop_toTopOf="@id/icon"
- app:layout_constraintBottom_toBottomOf="@id/icon"
+ app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/center_vertical_guideline"
app:layout_constraintEnd_toEndOf="parent"
/>
@@ -75,9 +64,9 @@
android:id="@+id/album_art"
android:layout_width="@dimen/qs_media_album_size"
android:layout_height="@dimen/qs_media_album_size"
- android:layout_marginTop="@dimen/qs_media_panel_outer_padding"
- android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
- android:layout_marginBottom="@dimen/qqs_media_spacing"
+ android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginStart="@dimen/qs_media_padding"
+ android:layout_marginBottom="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
/>
@@ -87,11 +76,11 @@
android:id="@+id/header_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/qqs_media_spacing"
- android:layout_marginStart="@dimen/qqs_media_spacing"
- android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginTop="26dp"
+ android:layout_marginStart="@dimen/qs_media_info_margin"
+ android:layout_marginEnd="@dimen/qs_media_padding"
app:layout_constrainedWidth="true"
- app:layout_constraintTop_toBottomOf="@+id/icon"
+ app:layout_constraintTop_toTopOf="@+id/album_art"
app:layout_constraintStart_toEndOf="@id/album_art"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"/>
@@ -101,10 +90,10 @@
android:id="@+id/header_artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
- android:layout_marginBottom="@dimen/qqs_media_spacing"
- android:layout_marginTop="3dp"
+ android:layout_marginEnd="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_media_padding"
app:layout_constrainedWidth="true"
+ android:layout_marginTop="@dimen/qs_media_info_spacing"
app:layout_constraintTop_toBottomOf="@id/header_title"
app:layout_constraintStart_toStartOf="@id/header_title"
app:layout_constraintEnd_toEndOf="parent"
@@ -115,7 +104,6 @@
android:id="@+id/media_progress_bar"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginTop="35dp"
app:layout_constraintTop_toBottomOf="@id/header_artist"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
@@ -125,10 +113,9 @@
android:id="@+id/notification_media_progress_time"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginTop="70dp"
- android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
- android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
- app:layout_constraintTop_toBottomOf="@id/header_artist"
+ android:layout_marginEnd="@dimen/qs_media_padding"
+ android:layout_marginStart="@dimen/qs_media_padding"
+ app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>
@@ -137,9 +124,10 @@
android:id="@+id/action0"
android:layout_width="48dp"
android:layout_height="48dp"
- android:layout_marginTop="5dp"
- android:layout_marginStart="@dimen/qs_media_panel_outer_padding"
- android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginTop="@dimen/qs_media_action_top"
+ android:layout_marginStart="@dimen/qs_media_padding"
+ android:layout_marginEnd="@dimen/qs_media_action_spacing"
+ android:layout_marginBottom="@dimen/qs_media_padding"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/action1"
@@ -151,12 +139,12 @@
android:id="@+id/action1"
android:layout_width="48dp"
android:layout_height="48dp"
- android:layout_marginStart="4dp"
- android:layout_marginEnd="4dp"
- android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginTop="@dimen/qs_media_action_top"
+ android:layout_marginStart="@dimen/qs_media_action_spacing"
+ android:layout_marginEnd="@dimen/qs_media_action_spacing"
+ android:layout_marginBottom="@dimen/qs_media_padding"
app:layout_constraintLeft_toRightOf="@id/action0"
app:layout_constraintRight_toLeftOf="@id/action2"
- app:layout_constraintTop_toTopOf="@id/action0"
app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
app:layout_constraintBottom_toBottomOf="parent">
</Constraint>
@@ -165,9 +153,10 @@
android:id="@+id/action2"
android:layout_width="48dp"
android:layout_height="48dp"
- android:layout_marginStart="4dp"
- android:layout_marginEnd="4dp"
- android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginTop="@dimen/qs_media_action_top"
+ android:layout_marginStart="@dimen/qs_media_action_spacing"
+ android:layout_marginEnd="@dimen/qs_media_action_spacing"
+ android:layout_marginBottom="@dimen/qs_media_padding"
app:layout_constraintLeft_toRightOf="@id/action1"
app:layout_constraintRight_toLeftOf="@id/action3"
app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
@@ -178,9 +167,10 @@
android:id="@+id/action3"
android:layout_width="48dp"
android:layout_height="48dp"
- android:layout_marginStart="4dp"
- android:layout_marginEnd="4dp"
- android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginTop="@dimen/qs_media_action_top"
+ android:layout_marginStart="@dimen/qs_media_action_spacing"
+ android:layout_marginEnd="@dimen/qs_media_action_spacing"
+ android:layout_marginBottom="@dimen/qs_media_padding"
app:layout_constraintLeft_toRightOf="@id/action2"
app:layout_constraintRight_toLeftOf="@id/action4"
app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
@@ -191,9 +181,10 @@
android:id="@+id/action4"
android:layout_width="48dp"
android:layout_height="48dp"
- android:layout_marginStart="4dp"
- android:layout_marginEnd="@dimen/qs_media_panel_outer_padding"
- android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
+ android:layout_marginTop="@dimen/qs_media_action_top"
+ android:layout_marginStart="@dimen/qs_media_action_spacing"
+ android:layout_marginEnd="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_media_padding"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toRightOf="@id/action3"
app:layout_constraintRight_toRightOf="parent"
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 351dfd5..5708855 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -101,6 +101,9 @@
public RemoteTransitionCompat(RecentsAnimationListener recents,
RecentsAnimationControllerCompat controller) {
mTransition = new IRemoteTransition.Stub() {
+ final RecentsControllerWrap mRecentsSession = new RecentsControllerWrap();
+ IBinder mToken = null;
+
@Override
public void startAnimation(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction t,
@@ -110,6 +113,7 @@
final RemoteAnimationTargetCompat[] wallpapers =
RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */);
// TODO(b/177438007): Move this set-up logic into launcher's animation impl.
+ mToken = transition;
// This transition is for opening recents, so recents is on-top. We want to draw
// the current going-away task on top of recents, though, so move it to front
WindowContainerToken pausingTask = null;
@@ -127,9 +131,8 @@
t.setAlpha(wallpapers[i].leash.mSurfaceControl, 1);
}
t.apply();
- final RecentsAnimationControllerCompat wrapControl =
- new RecentsControllerWrap(controller, info, finishedCallback, pausingTask);
- recents.onAnimationStart(wrapControl, apps, wallpapers, new Rect(0, 0, 0, 0),
+ mRecentsSession.setup(controller, info, finishedCallback, pausingTask);
+ recents.onAnimationStart(mRecentsSession, apps, wallpapers, new Rect(0, 0, 0, 0),
new Rect());
}
@@ -137,7 +140,13 @@
public void mergeAnimation(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction t, IBinder mergeTarget,
IRemoteTransitionFinishedCallback finishedCallback) {
- // TODO: hook up merge to onTaskAppeared. Until then, just ignore incoming merges.
+ if (!mergeTarget.equals(mToken)) return;
+ if (!mRecentsSession.merge(info, t, recents)) return;
+ try {
+ finishedCallback.onTransitionFinished(null /* wct */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error merging transition.", e);
+ }
}
};
}
@@ -159,19 +168,57 @@
*/
@VisibleForTesting
static class RecentsControllerWrap extends RecentsAnimationControllerCompat {
- private final RecentsAnimationControllerCompat mWrapped;
- private final IRemoteTransitionFinishedCallback mFinishCB;
- private final WindowContainerToken mPausingTask;
- private final TransitionInfo mInfo;
+ private RecentsAnimationControllerCompat mWrapped = null;
+ private IRemoteTransitionFinishedCallback mFinishCB = null;
+ private WindowContainerToken mPausingTask = null;
+ private TransitionInfo mInfo = null;
+ private SurfaceControl mOpeningLeash = null;
- RecentsControllerWrap(RecentsAnimationControllerCompat wrapped, TransitionInfo info,
+ void setup(RecentsAnimationControllerCompat wrapped, TransitionInfo info,
IRemoteTransitionFinishedCallback finishCB, WindowContainerToken pausingTask) {
+ if (mInfo != null) {
+ throw new IllegalStateException("Trying to run a new recents animation while"
+ + " recents is already active.");
+ }
mWrapped = wrapped;
mInfo = info;
mFinishCB = finishCB;
mPausingTask = pausingTask;
}
+ @SuppressLint("NewApi")
+ boolean merge(TransitionInfo info, SurfaceControl.Transaction t,
+ RecentsAnimationListener recents) {
+ TransitionInfo.Change openingTask = null;
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) {
+ if (change.getTaskInfo() != null) {
+ if (openingTask != null) {
+ Log.w(TAG, " Expecting to merge a task-open, but got >1 opening "
+ + "tasks");
+ }
+ openingTask = change;
+ }
+ }
+ }
+ if (openingTask == null) return false;
+ mOpeningLeash = openingTask.getLeash();
+ if (openingTask.getContainer().equals(mPausingTask)) {
+ // In this case, we are "returning" to the already running app, so just consume
+ // the merge and do nothing.
+ return true;
+ }
+ // We are receiving a new opening task, so convert to onTaskAppeared.
+ final int layer = mInfo.getChanges().size() * 3;
+ t.reparent(mOpeningLeash, mInfo.getRootLeash());
+ t.setLayer(mOpeningLeash, layer);
+ t.hide(mOpeningLeash);
+ t.apply();
+ recents.onTaskAppeared(new RemoteAnimationTargetCompat(openingTask, layer));
+ return true;
+ }
+
@Override public ThumbnailData screenshotTask(int taskId) {
return mWrapped != null ? mWrapped.screenshotTask(taskId) : null;
}
@@ -198,25 +245,42 @@
@Override
@SuppressLint("NewApi")
public void finish(boolean toHome, boolean sendUserLeaveHint) {
+ if (mFinishCB == null) {
+ Log.e(TAG, "Duplicate call to finish", new RuntimeException());
+ return;
+ }
+ if (mWrapped != null) mWrapped.finish(toHome, sendUserLeaveHint);
try {
- if (!toHome && mPausingTask != null) {
+ if (!toHome && mPausingTask != null && mOpeningLeash == null) {
// The gesture went back to opening the app rather than continuing with
// recents, so end the transition by moving the app back to the top.
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.reorder(mPausingTask, true /* onTop */);
mFinishCB.onTransitionFinished(wct);
} else {
+ if (mOpeningLeash != null) {
+ // TODO: the launcher animation should handle this
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.show(mOpeningLeash);
+ t.setAlpha(mOpeningLeash, 1.f);
+ t.apply();
+ }
mFinishCB.onTransitionFinished(null /* wct */);
}
} catch (RemoteException e) {
Log.e("RemoteTransitionCompat", "Failed to call animation finish callback", e);
}
- if (mWrapped != null) mWrapped.finish(toHome, sendUserLeaveHint);
// Release surface references now. This is apparently to free GPU
// memory while doing quick operations (eg. during CTS).
for (int i = 0; i < mInfo.getChanges().size(); ++i) {
mInfo.getChanges().get(i).getLeash().release();
}
+ // Reset all members.
+ mWrapped = null;
+ mFinishCB = null;
+ mPausingTask = null;
+ mInfo = null;
+ mOpeningLeash = null;
}
@Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 60b677a..825e008 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -21,12 +21,14 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
+import android.hardware.biometrics.BiometricSourceType;
import android.icu.text.NumberFormat;
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.util.ViewController;
@@ -35,13 +37,15 @@
import java.util.TimeZone;
/**
- * Controller for an AnimatableClockView.
+ * Controller for an AnimatableClockView. Instantiated by {@link KeyguardClockSwitchController}.
*/
public class AnimatableClockController extends ViewController<AnimatableClockView> {
private static final int FORMAT_NUMBER = 1234567890;
private final StatusBarStateController mStatusBarStateController;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final KeyguardBypassController mBypassController;
private final int mDozingColor = Color.WHITE;
private int mLockScreenColor;
@@ -59,12 +63,16 @@
AnimatableClockView view,
StatusBarStateController statusBarStateController,
BroadcastDispatcher broadcastDispatcher,
- BatteryController batteryController) {
+ BatteryController batteryController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ KeyguardBypassController bypassController) {
super(view);
mStatusBarStateController = statusBarStateController;
mIsDozing = mStatusBarStateController.isDozing();
mDozeAmount = mStatusBarStateController.getDozeAmount();
mBroadcastDispatcher = broadcastDispatcher;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mBypassController = bypassController;
mBurmeseNumerals = mBurmeseNf.format(FORMAT_NUMBER);
mBurmeseLineSpacing = getContext().getResources().getFloat(
@@ -98,14 +106,29 @@
mStatusBarStateController.addCallback(mStatusBarStateListener);
mIsDozing = mStatusBarStateController.isDozing();
mDozeAmount = mStatusBarStateController.getDozeAmount();
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
+
refreshTime();
initColors();
}
+ private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
+ if (biometricSourceType == BiometricSourceType.FACE
+ && mBypassController.canBypass()) {
+ mView.animateDisappear();
+ }
+ }
+ };
+
@Override
protected void onViewDetached() {
mBroadcastDispatcher.unregisterReceiver(mLocaleBroadcastReceiver);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
+ mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
index 0d6f64f..dc14f82 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
@@ -27,6 +27,7 @@
import android.widget.TextView;
import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import java.util.Calendar;
import java.util.TimeZone;
@@ -155,6 +156,21 @@
mLockScreenColor = lockScreenColor;
}
+ void animateDisappear() {
+ if (mTextAnimator == null) {
+ return;
+ }
+
+ setTextStyle(
+ 0 /* weight */,
+ -1 /* text size, no update */,
+ null /* color, no update */,
+ true /* animate */,
+ KeyguardBypassController.BYPASS_FADE_DURATION /* duration */,
+ 0 /* delay */,
+ null /* onAnimationEnd */);
+ }
+
void animateCharge(boolean isDozing) {
if (mTextAnimator == null || mTextAnimator.isRunning()) {
// Skip charge animation if dozing animation is already playing.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 5559a18..e92cae4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -24,8 +24,16 @@
import android.app.smartspace.SmartspaceConfig;
import android.app.smartspace.SmartspaceManager;
import android.app.smartspace.SmartspaceSession;
+import android.app.smartspace.SmartspaceTarget;
+import android.content.Context;
import android.content.Intent;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.View;
@@ -47,15 +55,18 @@
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.ViewController;
+import com.android.systemui.util.settings.SecureSettings;
import java.util.Locale;
import java.util.TimeZone;
@@ -90,10 +101,19 @@
private SmartspaceSession mSmartspaceSession;
private SmartspaceSession.OnTargetsAvailableListener mSmartspaceCallback;
- private int mWallpaperTextColor;
private ConfigurationController mConfigurationController;
private ActivityStarter mActivityStarter;
private FalsingManager mFalsingManager;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final KeyguardBypassController mBypassController;
+ private Handler mHandler;
+ private UserTracker mUserTracker;
+ private SecureSettings mSecureSettings;
+ private ContentObserver mSettingsObserver;
+ private boolean mShowSensitiveContentForCurrentUser;
+ private boolean mShowSensitiveContentForManagedUser;
+ private UserHandle mManagedUserHandle;
+ private UserTracker.Callback mUserTrackerCallback;
/**
* Listener for changes to the color palette.
@@ -147,7 +167,12 @@
ConfigurationController configurationController,
SystemUIFactory systemUIFactory,
ActivityStarter activityStarter,
- FalsingManager falsingManager) {
+ FalsingManager falsingManager,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
+ KeyguardBypassController bypassController,
+ @Main Handler handler,
+ UserTracker userTracker,
+ SecureSettings secureSettings) {
super(keyguardClockSwitch);
mStatusBarStateController = statusBarStateController;
mColorExtractor = colorExtractor;
@@ -162,6 +187,11 @@
mSystemUIFactory = systemUIFactory;
mActivityStarter = activityStarter;
mFalsingManager = falsingManager;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mBypassController = bypassController;
+ mHandler = handler;
+ mUserTracker = userTracker;
+ mSecureSettings = secureSettings;
}
/**
@@ -185,19 +215,23 @@
mLargeClockFrame = mView.findViewById(R.id.lockscreen_clock_view_large);
mClockViewController =
- new AnimatableClockController(
- mView.findViewById(R.id.animatable_clock_view),
- mStatusBarStateController,
- mBroadcastDispatcher,
- mBatteryController);
+ new AnimatableClockController(
+ mView.findViewById(R.id.animatable_clock_view),
+ mStatusBarStateController,
+ mBroadcastDispatcher,
+ mBatteryController,
+ mKeyguardUpdateMonitor,
+ mBypassController);
mClockViewController.init();
mLargeClockViewController =
- new AnimatableClockController(
- mView.findViewById(R.id.animatable_clock_view_large),
- mStatusBarStateController,
- mBroadcastDispatcher,
- mBatteryController);
+ new AnimatableClockController(
+ mView.findViewById(R.id.animatable_clock_view_large),
+ mStatusBarStateController,
+ mBroadcastDispatcher,
+ mBatteryController,
+ mKeyguardUpdateMonitor,
+ mBypassController);
mLargeClockViewController.init();
mStatusBarStateController.addCallback(mStatusBarStateListener);
@@ -248,15 +282,74 @@
mSmartspaceSession = getContext().getSystemService(SmartspaceManager.class)
.createSmartspaceSession(
new SmartspaceConfig.Builder(getContext(), "lockscreen").build());
- mSmartspaceCallback = targets -> smartspaceDataPlugin.onTargetsAvailable(targets);
+ mSmartspaceCallback = targets -> {
+ targets.removeIf(this::filterSmartspaceTarget);
+ smartspaceDataPlugin.onTargetsAvailable(targets);
+ };
mSmartspaceSession.addOnTargetsAvailableListener(mUiExecutor, mSmartspaceCallback);
- mSmartspaceSession.requestSmartspaceUpdate();
+ mSettingsObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ reloadSmartspace();
+ }
+ };
+
+ mUserTrackerCallback = new UserTracker.Callback() {
+ public void onUserChanged(int newUser, Context userContext) {
+ reloadSmartspace();
+ }
+ };
+ mUserTracker.addCallback(mUserTrackerCallback, mUiExecutor);
+
+ getContext().getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+ true, mSettingsObserver, UserHandle.USER_ALL);
+ reloadSmartspace();
}
float dozeAmount = mStatusBarStateController.getDozeAmount();
mStatusBarStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
}
+ @VisibleForTesting
+ boolean filterSmartspaceTarget(SmartspaceTarget t) {
+ if (!t.isSensitive()) return false;
+
+ if (t.getUserHandle().equals(mUserTracker.getUserHandle())) {
+ return !mShowSensitiveContentForCurrentUser;
+ }
+ if (t.getUserHandle().equals(mManagedUserHandle)) {
+ return !mShowSensitiveContentForManagedUser;
+ }
+
+ return false;
+ }
+
+ private void reloadSmartspace() {
+ mManagedUserHandle = getWorkProfileUser();
+ final String setting = Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
+
+ mShowSensitiveContentForCurrentUser =
+ mSecureSettings.getIntForUser(setting, 0, mUserTracker.getUserId()) == 1;
+ if (mManagedUserHandle != null) {
+ int id = mManagedUserHandle.getIdentifier();
+ mShowSensitiveContentForManagedUser =
+ mSecureSettings.getIntForUser(setting, 0, id) == 1;
+ }
+
+ mSmartspaceSession.requestSmartspaceUpdate();
+ }
+
+ private UserHandle getWorkProfileUser() {
+ for (UserInfo userInfo : mUserTracker.getUserProfiles()) {
+ if (userInfo.isManagedProfile()) {
+ return userInfo.getUserHandle();
+ }
+ }
+ return null;
+ }
+
private void updateWallpaperColor() {
if (mSmartspaceView != null) {
int color = Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColor);
@@ -279,6 +372,14 @@
}
mStatusBarStateController.removeCallback(mStatusBarStateListener);
mConfigurationController.removeCallback(mConfigurationListener);
+
+ if (mSettingsObserver != null) {
+ getContext().getContentResolver().unregisterContentObserver(mSettingsObserver);
+ }
+
+ if (mUserTrackerCallback != null) {
+ mUserTracker.removeCallback(mUserTrackerCallback);
+ }
}
/**
@@ -427,4 +528,9 @@
ConfigurationController.ConfigurationListener getConfigurationListener() {
return mConfigurationListener;
}
+
+ @VisibleForTesting
+ ContentObserver getSettingsObserver() {
+ return mSettingsObserver;
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 9b0ae6b..b5f2ab2 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -83,6 +83,7 @@
private boolean mQsExpanded;
private int mStatusBarState;
private boolean mIsKeyguardShowing;
+ private boolean mUserUnlockedWithBiometric;
private boolean mShowButton;
private boolean mShowUnlockIcon;
@@ -155,7 +156,8 @@
mView.setLocation(new PointF(props[0], props[1]), props[2]);
}
- mIsKeyguardShowing = mKeyguardViewController.isShowing();
+ updateKeyguardShowing();
+ mUserUnlockedWithBiometric = false;
mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
mIsDozing = mStatusBarStateController.isDozing();
mRunningFPS = mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
@@ -216,7 +218,8 @@
}
// these three states are mutually exclusive:
- mShowButton = mUdfpsEnrolled && !mCanDismissLockScreen && !mRunningFPS && isLockScreen();
+ mShowButton = mUdfpsEnrolled && !mCanDismissLockScreen && !mRunningFPS
+ && !mUserUnlockedWithBiometric && isLockScreen();
mShowUnlockIcon = mFaceAuthEnrolled & mCanDismissLockScreen && isLockScreen();
mShowLockIcon = !mUdfpsEnrolled && !mCanDismissLockScreen && isLockScreen()
&& mFaceAuthEnrolled;
@@ -289,6 +292,11 @@
}
}
+ private void updateKeyguardShowing() {
+ mIsKeyguardShowing = mKeyguardStateController.isShowing()
+ && !mKeyguardStateController.isKeyguardGoingAway();
+ }
+
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println(" mShowBouncerButton: " + mShowButton);
@@ -300,6 +308,7 @@
pw.println(" mIsKeyguardShowing: " + mIsKeyguardShowing);
pw.println(" mIsDozing: " + mIsDozing);
pw.println(" mIsBouncerShowing: " + mIsBouncerShowing);
+ pw.println(" mUserUnlockedWithBiometric: " + mUserUnlockedWithBiometric);
pw.println(" mRunningFPS: " + mRunningFPS);
pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen);
pw.println(" mStatusBarState: " + StatusBarState.toShortString(mStatusBarState));
@@ -326,18 +335,20 @@
@Override
public void onKeyguardBouncerChanged(boolean bouncer) {
mIsBouncerShowing = bouncer;
- mIsKeyguardShowing = mKeyguardStateController.isShowing();
updateVisibility();
}
@Override
public void onBiometricRunningStateChanged(boolean running,
BiometricSourceType biometricSourceType) {
+ mUserUnlockedWithBiometric =
+ mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(
+ KeyguardUpdateMonitor.getCurrentUser());
+
if (biometricSourceType == FINGERPRINT) {
mRunningFPS = running;
+ updateVisibility();
}
-
- updateVisibility();
}
};
@@ -346,15 +357,23 @@
@Override
public void onUnlockedChanged() {
mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
+ updateKeyguardShowing();
updateVisibility();
}
+
@Override
public void onKeyguardShowingChanged() {
- mIsKeyguardShowing = mKeyguardStateController.isShowing();
+ updateKeyguardShowing();
mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
mFaceAuthEnrolled = mKeyguardUpdateMonitor.isFaceEnrolled();
updateVisibility();
}
+
+ @Override
+ public void onKeyguardFadingAwayChanged() {
+ updateKeyguardShowing();
+ updateVisibility();
+ }
};
private final AccessibilityManager.TouchExplorationStateChangeListener
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index cbfdce5..351ae82 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -54,6 +54,7 @@
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarOverlayController;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.PluginDependencyProvider;
@@ -356,6 +357,7 @@
@Inject Lazy<TelephonyListenerManager> mTelephonyListenerManager;
@Inject Lazy<SystemStatusAnimationScheduler> mSystemStatusAnimationSchedulerLazy;
@Inject Lazy<PrivacyDotViewController> mPrivacyDotViewControllerLazy;
+ @Inject Lazy<EdgeBackGestureHandler> mEdgeBackGestureHandler;
@Inject
public Dependency() {
@@ -568,6 +570,7 @@
mProviders.put(SystemStatusAnimationScheduler.class,
mSystemStatusAnimationSchedulerLazy::get);
mProviders.put(PrivacyDotViewController.class, mPrivacyDotViewControllerLazy::get);
+ mProviders.put(EdgeBackGestureHandler.class, mEdgeBackGestureHandler::get);
Dependency.setInstance(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index 2808450..58881d9 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -16,6 +16,8 @@
package com.android.systemui.classifier;
+import static com.android.systemui.classifier.Classifier.BACK_GESTURE;
+import static com.android.systemui.classifier.Classifier.GENERIC;
import static com.android.systemui.classifier.FalsingManagerProxy.FALSING_SUCCESS;
import static com.android.systemui.classifier.FalsingModule.BRIGHT_LINE_GESTURE_CLASSIFERS;
@@ -196,7 +198,7 @@
@Override
public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
mPriorInteractionType = interactionType;
- if (skipFalsing()) {
+ if (skipFalsing(interactionType)) {
mPriorResults = getPassedResult(1);
logDebug("Skipped falsing");
return false;
@@ -229,7 +231,7 @@
@Override
public boolean isFalseTap(@Penalty int penalty) {
- if (skipFalsing()) {
+ if (skipFalsing(GENERIC)) {
mPriorResults = getPassedResult(1);
logDebug("Skipped falsing");
return false;
@@ -291,7 +293,7 @@
@Override
public boolean isFalseDoubleTap() {
- if (skipFalsing()) {
+ if (skipFalsing(GENERIC)) {
mPriorResults = getPassedResult(1);
logDebug("Skipped falsing");
return false;
@@ -306,8 +308,9 @@
return result.isFalse();
}
- private boolean skipFalsing() {
- return !mKeyguardStateController.isShowing()
+ private boolean skipFalsing(@Classifier.InteractionType int interactionType) {
+ return interactionType == BACK_GESTURE
+ || !mKeyguardStateController.isShowing()
|| mTestHarness
|| mDataProvider.isJustUnlockedWithFace()
|| mDockManager.isDocked();
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index 6f70672..ffdcff2 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -43,6 +43,7 @@
public static final int UDFPS_AUTHENTICATION = 13;
public static final int DISABLED_UDFPS_AFFORDANCE = 14;
public static final int QS_SWIPE = 15;
+ public static final int BACK_GESTURE = 16;
@IntDef({
QUICK_SETTINGS,
@@ -61,7 +62,8 @@
BRIGHTNESS_SLIDER,
UDFPS_AUTHENTICATION,
DISABLED_UDFPS_AFFORDANCE,
- QS_SWIPE
+ QS_SWIPE,
+ BACK_GESTURE
})
@Retention(RetentionPolicy.SOURCE)
public @interface InteractionType {}
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 2ea139e..b668e88 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -54,6 +54,14 @@
}
}
})
+
+ // First let's set the desired state that we want for this host
+ mediaHost.expansion = MediaHostState.COLLAPSED
+ mediaHost.showsOnlyActiveMedia = true
+ mediaHost.falsingProtectionNeeded = true
+
+ // Let's now initialize this view, which also creates the host view for us.
+ mediaHost.init(MediaHierarchyManager.LOCATION_LOCKSCREEN)
}
var visibilityChangedListener: ((Boolean) -> Unit)? = null
@@ -71,13 +79,7 @@
*/
fun attachSinglePaneContainer(mediaView: MediaHeaderView?) {
singlePaneContainer = mediaView
- // First let's set the desired state that we want for this host
- mediaHost.expansion = MediaHostState.COLLAPSED
- mediaHost.showsOnlyActiveMedia = true
- mediaHost.falsingProtectionNeeded = true
- // Let's now initialize this view, which also creates the host view for us.
- mediaHost.init(MediaHierarchyManager.LOCATION_LOCKSCREEN)
// Required to show it for the first time, afterwards visibility is managed automatically
mediaHost.visible = true
mediaHost.addVisibilityChangeListener { visible ->
@@ -133,6 +135,10 @@
}
// might be called a few times for the same view, no need to add hostView again
if (activeContainer?.childCount == 0) {
+ // Detach the hostView from its parent view if exists
+ mediaHost.hostView.parent ?.let {
+ (it as? ViewGroup)?.removeView(mediaHost.hostView)
+ }
activeContainer.addView(mediaHost.hostView)
}
setVisibility(activeContainer, View.VISIBLE)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index c3c617c..d87142f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -1,6 +1,5 @@
package com.android.systemui.media
-import android.animation.ArgbEvaluator
import android.app.smartspace.SmartspaceTarget
import android.content.Context
import android.content.Intent
@@ -116,8 +115,7 @@
private var needsReordering: Boolean = false
private var keysNeedRemoval = mutableSetOf<String>()
private var bgColor = getBackgroundColor()
- private var fgColor = com.android.settingslib.Utils.getColorAttr(context,
- com.android.internal.R.attr.textColorPrimary).defaultColor
+ private var fgColor = getForegroundColor()
private var isRtl: Boolean = false
set(value) {
if (value != field) {
@@ -359,9 +357,7 @@
private fun recreatePlayers() {
bgColor = getBackgroundColor()
-
- fgColor = com.android.settingslib.Utils.getColorAttr(context,
- com.android.internal.R.attr.textColorPrimary).defaultColor
+ fgColor = getForegroundColor()
pageIndicator.tintList = ColorStateList.valueOf(fgColor)
MediaPlayerData.mediaData().forEach { (key, data) ->
@@ -371,12 +367,12 @@
}
private fun getBackgroundColor(): Int {
- val themeAccent = com.android.settingslib.Utils.getColorAttr(context,
- com.android.internal.R.attr.colorAccent).defaultColor
- val themeBackground = com.android.settingslib.Utils.getColorAttr(context,
- com.android.internal.R.attr.colorBackground).defaultColor
- // Simulate transparency - cannot be actually transparent because of lockscreen
- return ArgbEvaluator().evaluate(0.25f, themeBackground, themeAccent) as Int
+ return context.getColor(android.R.color.system_accent2_50)
+ }
+
+ private fun getForegroundColor(): Int {
+ return com.android.settingslib.Utils.getColorAttr(context,
+ com.android.internal.R.attr.textColorPrimary).defaultColor
}
private fun updatePageIndicator() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 473db9f..96ae2cd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -25,7 +25,6 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
-import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -33,10 +32,8 @@
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Bundle;
-import android.os.SystemProperties;
import android.util.Log;
import android.view.View;
-import android.view.ViewOutlineProvider;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
@@ -46,7 +43,6 @@
import androidx.annotation.UiThread;
import androidx.constraintlayout.widget.ConstraintSet;
-import com.android.settingslib.Utils;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -75,11 +71,6 @@
private static final String EXTRAS_MEDIA_SOURCE_PACKAGE_NAME = "package_name";
private static final int MEDIA_RECOMMENDATION_MAX_NUM = 4;
- private final boolean mShowAppName = SystemProperties.getBoolean(
- "persist.sysui.qs_media_show_app_name", false);
- private final boolean mShowDeviceName = SystemProperties.getBoolean(
- "persist.sysui.qs_media_show_device_name", false);
-
private static final Intent SETTINGS_INTENT = new Intent(ACTION_MEDIA_CONTROLS_SETTINGS);
// Button IDs for QS controls
@@ -106,10 +97,8 @@
private KeyguardDismissUtil mKeyguardDismissUtil;
private Lazy<MediaDataManager> mMediaDataManagerLazy;
private int mBackgroundColor;
+ private int mDevicePadding;
private int mAlbumArtSize;
- private int mAlbumArtRadius;
- // This will provide the corners for the album art.
- private final ViewOutlineProvider mViewOutlineProvider;
private final MediaOutputDialogFactory mMediaOutputDialogFactory;
/**
@@ -133,13 +122,6 @@
mKeyguardDismissUtil = keyguardDismissUtil;
mMediaOutputDialogFactory = mediaOutputDialogFactory;
loadDimens();
-
- mViewOutlineProvider = new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(0, 0, mAlbumArtSize, mAlbumArtSize, mAlbumArtRadius);
- }
- };
}
public void onDestroy() {
@@ -151,9 +133,9 @@
}
private void loadDimens() {
- mAlbumArtRadius = mContext.getResources().getDimensionPixelSize(
- Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
mAlbumArtSize = mContext.getResources().getDimensionPixelSize(R.dimen.qs_media_album_size);
+ mDevicePadding = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.qs_media_album_device_padding);
}
/**
@@ -211,10 +193,6 @@
mPlayerViewHolder = vh;
TransitionLayout player = vh.getPlayer();
- ImageView albumView = vh.getAlbumView();
- albumView.setOutlineProvider(mViewOutlineProvider);
- albumView.setClipToOutline(true);
-
mSeekBarObserver = new SeekBarObserver(vh);
mSeekBarViewModel.getProgress().observeForever(mSeekBarObserver);
mSeekBarViewModel.attachTouchHandlers(vh.getSeekBar());
@@ -297,10 +275,19 @@
boolean hasArtwork = data.getArtwork() != null;
if (hasArtwork) {
Drawable artwork = scaleDrawable(data.getArtwork());
+ albumView.setPadding(0, 0, 0, 0);
albumView.setImageDrawable(artwork);
+ } else {
+ Drawable deviceIcon;
+ if (data.getDevice() != null && data.getDevice().getIcon() != null) {
+ deviceIcon = data.getDevice().getIcon().getConstantState().newDrawable().mutate();
+ } else {
+ deviceIcon = getContext().getDrawable(R.drawable.ic_headphone);
+ }
+ deviceIcon.setTintList(ColorStateList.valueOf(mBackgroundColor));
+ albumView.setPadding(mDevicePadding, mDevicePadding, mDevicePadding, mDevicePadding);
+ albumView.setImageDrawable(deviceIcon);
}
- setVisibleAndAlpha(collapsedSet, R.id.album_art, hasArtwork);
- setVisibleAndAlpha(expandedSet, R.id.album_art, hasArtwork);
// App icon
ImageView appIcon = mPlayerViewHolder.getAppIcon();
@@ -315,13 +302,6 @@
TextView titleText = mPlayerViewHolder.getTitleText();
titleText.setText(data.getSong());
- // App title
- TextView appName = mPlayerViewHolder.getAppName();
- appName.setText(data.getApp());
- appName.setVisibility(mShowAppName ? View.VISIBLE : View.GONE);
- setVisibleAndAlpha(collapsedSet, R.id.app_name, mShowAppName);
- setVisibleAndAlpha(expandedSet, R.id.app_name, mShowAppName);
-
// Artist name
TextView artistText = mPlayerViewHolder.getArtistText();
artistText.setText(data.getArtist());
@@ -333,10 +313,6 @@
mPlayerViewHolder.getSeamless().setOnClickListener(v -> {
mMediaOutputDialogFactory.create(data.getPackageName(), true);
});
- TextView mDeviceName = mPlayerViewHolder.getSeamlessText();
- mDeviceName.setVisibility(mShowDeviceName ? View.VISIBLE : View.GONE);
- setVisibleAndAlpha(collapsedSet, R.id.media_seamless_text, mShowDeviceName);
- setVisibleAndAlpha(expandedSet, R.id.media_seamless_text, mShowDeviceName);
ImageView iconView = mPlayerViewHolder.getSeamlessIcon();
TextView deviceName = mPlayerViewHolder.getSeamlessText();
@@ -406,8 +382,12 @@
// Hide any unused buttons
for (; i < ACTION_IDS.length; i++) {
- setVisibleAndAlpha(expandedSet, ACTION_IDS[i], false /*visible */);
setVisibleAndAlpha(collapsedSet, ACTION_IDS[i], false /*visible */);
+ setVisibleAndAlpha(expandedSet, ACTION_IDS[i], false /* visible */);
+ }
+ // If no expanded buttons, set the first view as INVISIBLE so z remains constant
+ if (actionIcons.size() == 0) {
+ expandedSet.setVisibility(ACTION_IDS[0], ConstraintSet.INVISIBLE);
}
// Seek Bar
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index 6ef29d6..6f0c887 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -31,7 +31,7 @@
*/
val app: String?,
/**
- * Icon shown on player, close to app name.
+ * App icon shown on player.
*/
val appIcon: Icon?,
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 2bd7729..9163044 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -16,6 +16,7 @@
package com.android.systemui.media
+import android.app.smartspace.SmartspaceAction
import android.app.smartspace.SmartspaceTarget
import android.os.SystemProperties
import android.util.Log
@@ -121,6 +122,12 @@
}
// If no recent media, continue with smartspace update
+ if (isMediaRecommendationEmpty(data)) {
+ Log.d(TAG, "Empty media recommendations. Skip showing the card")
+ return
+ }
+
+ // Proceed only if the Smartspace recommendation is not empty.
listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data) }
}
@@ -214,4 +221,10 @@
* Remove a listener that was registered with addListener
*/
fun removeListener(listener: MediaDataManager.Listener) = _listeners.remove(listener)
+
+ /** Check if the Smartspace sends an empty update. */
+ private fun isMediaRecommendationEmpty(data: SmartspaceTarget): Boolean {
+ val mediaRecommendationList: List<SmartspaceAction> = data.getIconGrid()
+ return mediaRecommendationList == null || mediaRecommendationList.isEmpty()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index a070861..138c422 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -115,12 +115,15 @@
// UI surface label for subscribing Smartspace updates.
@JvmField
val SMARTSPACE_UI_SURFACE_LABEL = "media_data_manager"
+
+ // Maximum number of actions allowed in compact view
+ @JvmField
+ val MAX_COMPACT_ACTIONS = 3
}
private val themeText = com.android.settingslib.Utils.getColorAttr(context,
com.android.internal.R.attr.textColorPrimary).defaultColor
- private val bgColor = com.android.settingslib.Utils.getColorAttr(context,
- com.android.internal.R.attr.colorBackground).defaultColor
+ private val bgColor = context.getColor(android.R.color.system_accent2_50)
// Internal listeners are part of the internal pipeline. External listeners (those registered
// with [MediaDeviceManager.addListener]) receive events after they have propagated through
@@ -549,8 +552,12 @@
// Control buttons
val actionIcons: MutableList<MediaAction> = ArrayList()
val actions = notif.actions
- val actionsToShowCollapsed = notif.extras.getIntArray(
+ var actionsToShowCollapsed = notif.extras.getIntArray(
Notification.EXTRA_COMPACT_ACTIONS)?.toMutableList() ?: mutableListOf<Int>()
+ if (actionsToShowCollapsed.size > MAX_COMPACT_ACTIONS) {
+ Log.e(TAG, "Too many compact actions for $key, limiting to first $MAX_COMPACT_ACTIONS")
+ actionsToShowCollapsed = actionsToShowCollapsed.subList(0, MAX_COMPACT_ACTIONS)
+ }
// TODO: b/153736623 look into creating actions when this isn't a media style notification
if (actions != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index 2347481..28e4640 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -25,6 +25,8 @@
private val tmpLocationOnScreen: IntArray = intArrayOf(0, 0)
+ private var inited: Boolean = false
+
/**
* Get the current bounds on the screen. This makes sure the state is fresh and up to date
*/
@@ -84,6 +86,11 @@
* transitions.
*/
fun init(@MediaLocation location: Int) {
+ if (inited) {
+ return
+ }
+ inited = true
+
this.location = location
hostView = mediaHierarchyManager.register(this)
hostView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {
diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
index 16327bd..08d8726 100644
--- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
@@ -35,7 +35,6 @@
// Player information
val appIcon = itemView.requireViewById<ImageView>(R.id.icon)
- val appName = itemView.requireViewById<TextView>(R.id.app_name)
val albumView = itemView.requireViewById<ImageView>(R.id.album_art)
val titleText = itemView.requireViewById<TextView>(R.id.header_title)
val artistText = itemView.requireViewById<TextView>(R.id.header_artist)
@@ -120,6 +119,7 @@
R.id.header_title,
R.id.header_artist,
R.id.media_seamless,
+ R.id.media_seamless_fallback,
R.id.notification_media_progress_time,
R.id.media_progress_bar,
R.id.action0,
@@ -130,7 +130,6 @@
R.id.icon
)
val gutsIds = setOf(
- R.id.media_text,
R.id.remove_text,
R.id.cancel,
R.id.dismiss,
diff --git a/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt
index 19c83bc..02150c4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/RecommendationViewHolder.kt
@@ -20,7 +20,6 @@
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
-import android.widget.TextView
import androidx.annotation.IntegerRes
import com.android.systemui.R
import com.android.systemui.util.animation.TransitionLayout
@@ -56,7 +55,6 @@
val cancel = itemView.requireViewById<View>(R.id.cancel)
val dismiss = itemView.requireViewById<ViewGroup>(R.id.dismiss)
val dismissLabel = dismiss.getChildAt(0)
- val recommendationText = itemView.requireViewById<TextView>(R.id.recommendation_text)
val settings = itemView.requireViewById<View>(R.id.settings)
init {
@@ -105,7 +103,6 @@
// Res Ids for the components on the guts panel.
val gutsIds = setOf(
- R.id.recommendation_text,
R.id.remove_text,
R.id.cancel,
R.id.dismiss,
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
index d789501..f17ad6f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
@@ -78,6 +78,7 @@
fun setVerticalPadding(padding: Int) {
val leftPadding = holder.seekBar.paddingLeft
val rightPadding = holder.seekBar.paddingRight
- holder.seekBar.setPadding(leftPadding, padding, rightPadding, padding)
+ val bottomPadding = holder.seekBar.paddingBottom
+ holder.seekBar.setPadding(leftPadding, padding, rightPadding, bottomPadding)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 01c80f6..0ed4d86 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -83,7 +83,6 @@
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsOnboarding;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.SysUiStatsLog;
@@ -113,7 +112,6 @@
private final RegionSamplingHelper mRegionSamplingHelper;
private final int mNavColorSampleMargin;
private final SysUiState mSysUiFlagContainer;
- private final PluginManager mPluginManager;
View mCurrentView = null;
private View mVertical;
@@ -316,7 +314,6 @@
boolean isGesturalMode = isGesturalMode(mNavBarMode);
mSysUiFlagContainer = Dependency.get(SysUiState.class);
- mPluginManager = Dependency.get(PluginManager.class);
// Set up the context group of buttons
mContextualButtonGroup = new ContextualButtonGroup(R.id.menu_container);
final ContextualButton imeSwitcherButton = new ContextualButton(R.id.ime_switcher,
@@ -366,8 +363,8 @@
mNavColorSampleMargin = getResources()
.getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);
- mEdgeBackGestureHandler = new EdgeBackGestureHandler(context, mOverviewProxyService,
- mSysUiFlagContainer, mPluginManager, this::updateStates);
+ mEdgeBackGestureHandler = Dependency.get(EdgeBackGestureHandler.class);
+ mEdgeBackGestureHandler.setStateChangeCallback(this::updateStates);
mRegionSamplingHelper = new RegionSamplingHelper(this,
new RegionSamplingHelper.SamplingCallback() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 806ea4f..fc615de 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui.navigationbar.gestural;
+import static com.android.systemui.classifier.Classifier.BACK_GESTURE;
+
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
@@ -27,8 +29,6 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.input.InputManager;
import android.os.Looper;
import android.os.RemoteException;
@@ -41,6 +41,7 @@
import android.view.Choreographer;
import android.view.Display;
import android.view.ISystemGestureExclusionListener;
+import android.view.IWindowManager;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputMonitor;
@@ -50,18 +51,19 @@
import android.view.Surface;
import android.view.ViewConfiguration;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.view.WindowMetrics;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.policy.GestureNavigationSettingsObserver;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.NavigationEdgeBackPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.recents.OverviewProxyService;
@@ -85,9 +87,12 @@
import java.util.Map;
import java.util.concurrent.Executor;
+import javax.inject.Inject;
+
/**
* Utility class to handle edge swipes for back gesture
*/
+@SysUISingleton
public class EdgeBackGestureHandler extends CurrentUserTracker
implements PluginListener<NavigationEdgeBackPlugin>, ProtoTraceable<SystemUiTraceProto> {
@@ -164,9 +169,15 @@
private final Context mContext;
private final OverviewProxyService mOverviewProxyService;
private final SysUiState mSysUiState;
- private final Runnable mStateChangeCallback;
+ private Runnable mStateChangeCallback;
private final PluginManager mPluginManager;
+ private final ProtoTracer mProtoTracer;
+ private final NavigationModeController mNavigationModeController;
+ private final ViewConfiguration mViewConfiguration;
+ private final WindowManager mWindowManager;
+ private final IWindowManager mWindowManagerService;
+ private final FalsingManager mFalsingManager;
// Activities which should not trigger Back gesture.
private final List<ComponentName> mGestureBlockingActivities = new ArrayList<>();
@@ -237,6 +248,9 @@
new NavigationEdgeBackPlugin.BackCallback() {
@Override
public void triggerBack() {
+ // Notify FalsingManager that an intentional gesture has occurred.
+ // TODO(b/186519446): use a different method than isFalseTouch
+ mFalsingManager.isFalseTouch(BACK_GESTURE);
boolean sendDown = sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
boolean sendUp = sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
if (DEBUG_MISSING_GESTURE) {
@@ -267,16 +281,26 @@
}
};
+ @Inject
public EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService,
- SysUiState sysUiState, PluginManager pluginManager, Runnable stateChangeCallback) {
- super(Dependency.get(BroadcastDispatcher.class));
+ SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor,
+ BroadcastDispatcher broadcastDispatcher, ProtoTracer protoTracer,
+ NavigationModeController navigationModeController, ViewConfiguration viewConfiguration,
+ WindowManager windowManager, IWindowManager windowManagerService,
+ FalsingManager falsingManager) {
+ super(broadcastDispatcher);
mContext = context;
mDisplayId = context.getDisplayId();
- mMainExecutor = context.getMainExecutor();
+ mMainExecutor = executor;
mOverviewProxyService = overviewProxyService;
mSysUiState = sysUiState;
mPluginManager = pluginManager;
- mStateChangeCallback = stateChangeCallback;
+ mProtoTracer = protoTracer;
+ mNavigationModeController = navigationModeController;
+ mViewConfiguration = viewConfiguration;
+ mWindowManager = windowManager;
+ mWindowManagerService = windowManagerService;
+ mFalsingManager = falsingManager;
ComponentName recentsComponentName = ComponentName.unflattenFromString(
context.getString(com.android.internal.R.string.config_recentsComponentName));
if (recentsComponentName != null) {
@@ -309,9 +333,12 @@
updateCurrentUserResources();
}
+ public void setStateChangeCallback(Runnable callback) {
+ mStateChangeCallback = callback;
+ }
+
public void updateCurrentUserResources() {
- Resources res = Dependency.get(NavigationModeController.class).getCurrentUserContext()
- .getResources();
+ Resources res = mNavigationModeController.getCurrentUserContext().getResources();
mEdgeWidthLeft = mGestureNavigationSettingsObserver.getLeftSensitivity(res);
mEdgeWidthRight = mGestureNavigationSettingsObserver.getRightSensitivity(res);
mIsBackGestureAllowed =
@@ -336,7 +363,7 @@
// TODO(b/130352502) Tune this value and extract into a constant
final float backGestureSlop = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.BACK_GESTURE_SLOP_MULTIPLIER, 0.75f);
- mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop() * backGestureSlop;
+ mTouchSlop = mViewConfiguration.getScaledTouchSlop() * backGestureSlop;
}
private void onNavigationSettingsChanged() {
@@ -358,7 +385,7 @@
*/
public void onNavBarAttached() {
mIsAttached = true;
- Dependency.get(ProtoTracer.class).add(this);
+ mProtoTracer.add(this);
mOverviewProxyService.addCallback(mQuickSwitchListener);
mSysUiState.addCallback(mSysUiStateCallback);
updateIsEnabled();
@@ -370,7 +397,7 @@
*/
public void onNavBarDetached() {
mIsAttached = false;
- Dependency.get(ProtoTracer.class).remove(this);
+ mProtoTracer.remove(this);
mOverviewProxyService.removeCallback(mQuickSwitchListener);
mSysUiState.removeCallback(mSysUiStateCallback);
updateIsEnabled();
@@ -424,9 +451,8 @@
DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
try {
- WindowManagerGlobal.getWindowManagerService()
- .unregisterSystemGestureExclusionListener(
- mGestureExclusionListener, mDisplayId);
+ mWindowManagerService.unregisterSystemGestureExclusionListener(
+ mGestureExclusionListener, mDisplayId);
} catch (RemoteException | IllegalArgumentException e) {
Log.e(TAG, "Failed to unregister window manager callbacks", e);
}
@@ -439,13 +465,11 @@
}
TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
- runnable -> (mContext.getMainThreadHandler()).post(runnable),
- mOnPropertiesChangedListener);
+ mMainExecutor::execute, mOnPropertiesChangedListener);
try {
- WindowManagerGlobal.getWindowManagerService()
- .registerSystemGestureExclusionListener(
- mGestureExclusionListener, mDisplayId);
+ mWindowManagerService.registerSystemGestureExclusionListener(
+ mGestureExclusionListener, mDisplayId);
} catch (RemoteException | IllegalArgumentException e) {
Log.e(TAG, "Failed to register window manager callbacks", e);
}
@@ -801,7 +825,7 @@
}
}
- Dependency.get(ProtoTracer.class).scheduleFrameUpdate();
+ mProtoTracer.scheduleFrameUpdate();
}
private void updateDisabledForQuickstep(Configuration newConfig) {
@@ -822,8 +846,7 @@
}
private void updateDisplaySize() {
- WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
- .getMaximumWindowMetrics();
+ WindowMetrics metrics = mWindowManager.getMaximumWindowMetrics();
Rect bounds = metrics.getBounds();
mDisplaySize.set(bounds.width(), bounds.height());
if (DEBUG_MISSING_GESTURE) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index f7fa5bf..c552e89 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -8,7 +8,6 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Rect;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
@@ -63,7 +62,6 @@
private int mPageToRestore = -1;
private int mLayoutOrientation;
private int mLayoutDirection;
- private final Rect mClippingRect;
private final UiEventLogger mUiEventLogger = QSEvents.INSTANCE.getQsUiEventsLogger();
private int mExcessHeight;
private int mLastExcessHeight;
@@ -78,14 +76,16 @@
setCurrentItem(0, false);
mLayoutOrientation = getResources().getConfiguration().orientation;
mLayoutDirection = getLayoutDirection();
- mClippingRect = new Rect();
-
- // Make sure there's a space between pages when scroling
- setPageMargin(context.getResources().getDimensionPixelOffset(
- R.dimen.qs_tile_margin_horizontal));
}
private int mLastMaxHeight = -1;
+ @Override
+ public void setPageMargin(int marginPixels) {
+ if (marginPixels != getPageMargin()) {
+ super.setPageMargin(marginPixels);
+ }
+ }
+
public void saveInstanceState(Bundle outState) {
outState.putInt(CURRENT_PAGE, getCurrentItem());
}
@@ -353,14 +353,6 @@
}
@Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- // Clip to margins
- mClippingRect.set(0, 0, (r - l), b - t);
- setClipBounds(mClippingRect);
- }
-
- @Override
public boolean setMinRows(int minRows) {
mMinRows = minRows;
boolean changed = false;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 294d765..488ada9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -80,7 +80,6 @@
private QSExpansionPathInterpolator mQSExpansionPathInterpolator;
private TouchAnimator mFirstPageAnimator;
private TouchAnimator mFirstPageDelayedAnimator;
- private TouchAnimator mTranslationXAnimator;
private TouchAnimator mTranslationYAnimator;
private TouchAnimator mNonfirstPageAnimator;
private TouchAnimator mNonfirstPageDelayedAnimator;
@@ -219,10 +218,29 @@
mOnFirstPage = isFirst;
}
+ private void translateContent(
+ View qqsView,
+ View qsView,
+ View commonParent,
+ int yOffset,
+ int[] temp,
+ TouchAnimator.Builder animatorBuilder
+ ) {
+ getRelativePosition(temp, qqsView, commonParent);
+ int qqsPos = temp[1];
+ getRelativePosition(temp, qsView, commonParent);
+ int qsPos = temp[1];
+
+ int diff = qsPos - qqsPos - yOffset;
+ animatorBuilder.addFloat(qqsView, "translationY", 0, diff);
+ animatorBuilder.addFloat(qsView, "translationY", -diff, 0);
+ mAllViews.add(qqsView);
+ mAllViews.add(qsView);
+ }
+
private void updateAnimators() {
mNeedsAnimatorUpdate = false;
TouchAnimator.Builder firstPageBuilder = new Builder();
- TouchAnimator.Builder translationXBuilder = new Builder();
TouchAnimator.Builder translationYBuilder = new Builder();
Collection<QSTile> tiles = mHost.getTiles();
@@ -241,7 +259,6 @@
QSTileLayout tileLayout = mQsPanelController.getTileLayout();
mAllViews.add((View) tileLayout);
int height = mQs.getView() != null ? mQs.getView().getMeasuredHeight() : 0;
- int width = mQs.getView() != null ? mQs.getView().getMeasuredWidth() : 0;
int heightDiff = height - mQs.getHeader().getBottom()
+ mQs.getHeader().getPaddingBottom();
if (!mTranslateWhileExpanding) {
@@ -268,68 +285,62 @@
QSTileView quickTileView = mQuickQSPanelController.getTileView(tile);
if (quickTileView == null) continue;
- getRelativePosition(loc1, quickTileView.getIcon().getIconView(), view);
- getRelativePosition(loc2, tileIcon, view);
- final int xDiff = loc2[0] - loc1[0];
- int yDiff = loc2[1] - loc1[1];
+ getRelativePosition(loc1, quickTileView, view);
+ getRelativePosition(loc2, tileView, view);
+ int yOffset = loc2[1] - loc1[1];
- if (count < tileLayout.getNumVisibleTiles()) {
- getRelativePosition(loc1, quickTileView, view);
- getRelativePosition(loc2, tileView, view);
- int yOffset = loc2[1] - loc1[1];
- // Move the quick tile right from its location to the new one.
- View v = quickTileView.getIcon();
- translationXBuilder.addFloat(v, "translationX", 0, xDiff);
- translationYBuilder.addFloat(v, "translationY", 0, yDiff - yOffset);
- mAllViews.add(v);
+ // Offset the translation animation on the views
+ // (that goes from 0 to getOffsetTranslation)
+ int offsetWithQSBHTranslation =
+ yOffset - mQuickStatusBarHeader.getOffsetTranslation();
+ translationYBuilder.addFloat(quickTileView, "translationY", 0,
+ offsetWithQSBHTranslation);
+ translationYBuilder.addFloat(tileView, "translationY",
+ -offsetWithQSBHTranslation, 0);
- // Move the real tile from the quick tile position to its final
- // location.
- v = tileIcon;
- translationXBuilder.addFloat(v, "translationX", -xDiff, 0);
- translationYBuilder.addFloat(v, "translationY", -yDiff + yOffset, 0);
-
- // Offset the translation animation on the views
- // (that goes from 0 to getOffsetTranslation)
- int offsetWithQSBHTranslation =
- yOffset - mQuickStatusBarHeader.getOffsetTranslation();
- translationYBuilder.addFloat(quickTileView, "translationY", 0,
- offsetWithQSBHTranslation);
- translationYBuilder.addFloat(tileView, "translationY",
- -offsetWithQSBHTranslation, 0);
-
- if (mQQSTileHeightAnimator == null) {
- mQQSTileHeightAnimator = new HeightExpansionAnimator(this,
- quickTileView.getHeight(), tileView.getHeight());
- qqsTileHeight = quickTileView.getHeight();
- }
-
- mQQSTileHeightAnimator.addView(quickTileView);
- View qqsLabelContainer = quickTileView.getLabelContainer();
- View qsLabelContainer = tileView.getLabelContainer();
-
- getRelativePosition(loc1, qqsLabelContainer, view);
- getRelativePosition(loc2, qsLabelContainer, view);
- yDiff = loc2[1] - loc1[1] - yOffset;
-
- translationYBuilder.addFloat(qqsLabelContainer, "translationY", 0, yDiff);
- translationYBuilder.addFloat(qsLabelContainer, "translationY", -yDiff, 0);
- mAllViews.add(qqsLabelContainer);
- mAllViews.add(qsLabelContainer);
- } else { // These tiles disappear when expanding
- firstPageBuilder.addFloat(quickTileView, "alpha", 1, 0);
- translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
-
- // xDiff is negative here and this makes it "more" negative
- final int translationX =
- mQsPanelController.isLayoutRtl() ? xDiff - width : xDiff + width;
- translationXBuilder.addFloat(quickTileView, "translationX", 0,
- translationX);
+ if (mQQSTileHeightAnimator == null) {
+ mQQSTileHeightAnimator = new HeightExpansionAnimator(this,
+ quickTileView.getHeight(), tileView.getHeight());
+ qqsTileHeight = quickTileView.getHeight();
}
+ mQQSTileHeightAnimator.addView(quickTileView);
+
+ // Icons
+ translateContent(
+ quickTileView.getIcon(),
+ tileView.getIcon(),
+ view,
+ yOffset,
+ loc1,
+ translationYBuilder
+ );
+
+ // Label containers
+ translateContent(
+ quickTileView.getLabelContainer(),
+ tileView.getLabelContainer(),
+ view,
+ yOffset,
+ loc1,
+ translationYBuilder
+ );
+
+ // Secondary icon
+ translateContent(
+ quickTileView.getSecondaryIcon(),
+ tileView.getSecondaryIcon(),
+ view,
+ yOffset,
+ loc1,
+ translationYBuilder
+ );
+
+ firstPageBuilder.addFloat(quickTileView.getSecondaryLabel(), "alpha", 0, 1);
+
mQuickQsViews.add(tileView);
- mAllViews.add(tileView.getIcon());
mAllViews.add(quickTileView);
+ mAllViews.add(quickTileView.getSecondaryLabel());
} else if (mFullRows && isIconInAnimatedRow(count)) {
firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0);
@@ -391,17 +402,7 @@
if (mQsPanelController.getDivider() != null) {
mAllViews.add(mQsPanelController.getDivider());
}
-
- float px = 0;
- if (tiles.size() <= 3) {
- px = 1;
- } else if (tiles.size() <= 6) {
- px = .4f;
- }
- mQSExpansionPathInterpolator.setControlX2(px);
- translationXBuilder.setInterpolator(mQSExpansionPathInterpolator.getXInterpolator());
translationYBuilder.setInterpolator(mQSExpansionPathInterpolator.getYInterpolator());
- mTranslationXAnimator = translationXBuilder.build();
mTranslationYAnimator = translationYBuilder.build();
if (mQQSTileHeightAnimator != null) {
mQQSTileHeightAnimator.setInterpolator(
@@ -474,7 +475,6 @@
mQuickQsPanel.setAlpha(1);
mFirstPageAnimator.setPosition(position);
mFirstPageDelayedAnimator.setPosition(position);
- mTranslationXAnimator.setPosition(position);
mTranslationYAnimator.setPosition(position);
if (mBrightnessAnimator != null) {
mBrightnessAnimator.setPosition(position);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index e89803d..9abb430 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -154,11 +154,9 @@
// bottom and footer are inside the screen.
MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanelContainer.getLayoutParams();
- // The footer is pinned to the bottom of QSPanel (same bottoms), therefore we don't need to
- // subtract its height. We do not care if the collapsed notifications fit in the screen.
- int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin
+ int availableScreenHeight = getDisplayHeight() - mNavBarInset;
+ int maxQs = availableScreenHeight - layoutParams.topMargin - layoutParams.bottomMargin
- getPaddingBottom();
- maxQs -= mNavBarInset;
int padding = mPaddingLeft + mPaddingRight + layoutParams.leftMargin
+ layoutParams.rightMargin;
final int qsPanelWidthSpec = getChildMeasureSpec(widthMeasureSpec, padding,
@@ -169,7 +167,7 @@
int height = layoutParams.topMargin + layoutParams.bottomMargin
+ mQSPanelContainer.getMeasuredHeight() + getPaddingBottom();
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ MeasureSpec.makeMeasureSpec(availableScreenHeight, MeasureSpec.EXACTLY));
// QSCustomizer will always be the height of the screen, but do this after
// other measuring to avoid changing the height of the QS.
mQSCustomizer.measure(widthMeasureSpec,
@@ -316,6 +314,9 @@
if (view == mQSPanelContainer) {
// QS panel lays out some of its content full width
qsPanelController.setContentMargins(mContentPadding, mContentPadding);
+ // Set it as double the side margin (to simulate end margin of current page +
+ // start margin of next page).
+ qsPanelController.setPageMargin(2 * mSideMargins);
} else if (view == mHeader) {
// The header contains the QQS panel which needs to have special padding, to
// visually align them.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index d5cb777..814846c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -480,8 +480,12 @@
private void updateQsBounds() {
if (mLastQSExpansion == 1.0f) {
// Fully expanded, let's set the layout bounds as clip bounds. This is necessary because
- // it's a scrollview and otherwise wouldn't be clipped.
- mQsBounds.set(0, 0, mQSPanelScrollView.getWidth(), mQSPanelScrollView.getHeight());
+ // it's a scrollview and otherwise wouldn't be clipped. However, we set the horizontal
+ // bounds so the pages go to the ends of QSContainerImpl
+ ViewGroup.MarginLayoutParams lp =
+ (ViewGroup.MarginLayoutParams) mQSPanelScrollView.getLayoutParams();
+ mQsBounds.set(-lp.leftMargin, 0, mQSPanelScrollView.getWidth() + lp.rightMargin,
+ mQSPanelScrollView.getHeight());
}
mQSPanelScrollView.setClipBounds(mQsBounds);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index efa1a13..4e16b74 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -138,7 +138,7 @@
mHorizontalTileLayout = createHorizontalTileLayout();
LayoutParams lp = new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1);
- int marginSize = (int) mContext.getResources().getDimension(R.dimen.qqs_media_spacing);
+ int marginSize = (int) mContext.getResources().getDimension(R.dimen.qs_media_padding);
lp.setMarginStart(0);
lp.setMarginEnd(marginSize);
lp.gravity = Gravity.CENTER_VERTICAL;
@@ -301,12 +301,6 @@
protected void updatePadding() {
final Resources res = mContext.getResources();
int padding = res.getDimensionPixelSize(R.dimen.qs_panel_padding_top);
- if (mUsingHorizontalLayout) {
- // When using the horizontal layout, our space is quite constrained. We therefore
- // reduce some of the padding on the top, which makes the brightness bar overlapp,
- // but since that has naturally quite a bit of built in padding, that's fine.
- padding = (int) (padding * 0.6f);
- }
setPaddingRelative(getPaddingStart(),
padding,
getPaddingEnd(),
@@ -678,6 +672,16 @@
switchSecurityFooter();
}
+ protected void setPageMargin(int pageMargin) {
+ if (mRegularTileLayout instanceof PagedTileLayout) {
+ ((PagedTileLayout) mRegularTileLayout).setPageMargin(pageMargin);
+ }
+ if (mHorizontalTileLayout != mRegularTileLayout
+ && mHorizontalTileLayout instanceof PagedTileLayout) {
+ ((PagedTileLayout) mHorizontalTileLayout).setPageMargin(pageMargin);
+ }
+ }
+
void setUsingHorizontalLayout(boolean horizontal, ViewGroup mediaHostView, boolean force,
UiEventLogger uiEventLogger) {
if (horizontal != mUsingHorizontalLayout || force) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 93ccfc72..fff3d1f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -321,5 +321,9 @@
public boolean isExpanded() {
return mView.isExpanded();
}
+
+ void setPageMargin(int pageMargin) {
+ mView.setPageMargin(pageMargin);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ButtonRelativeLayout.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ButtonRelativeLayout.java
index 2c17b87..962537a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ButtonRelativeLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ButtonRelativeLayout.java
@@ -16,11 +16,17 @@
import android.content.Context;
import android.util.AttributeSet;
+import android.view.View;
import android.widget.Button;
import android.widget.RelativeLayout;
+/**
+ * Used for QS tile labels
+ */
public class ButtonRelativeLayout extends RelativeLayout {
+ private View mIgnoredView;
+
public ButtonRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -29,4 +35,27 @@
public CharSequence getAccessibilityClassName() {
return Button.class.getName();
}
+
+ /**
+ * Set a view to be ignored for measure.
+ *
+ * The view will be measured and laid out, but its size will be subtracted from the total size
+ * of this view. It assumes that this view only contributes vertical height.
+ */
+ public void setIgnoredView(View view) {
+ if (mIgnoredView == null || mIgnoredView.getParent() == this) {
+ mIgnoredView = view;
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mIgnoredView != null && mIgnoredView.getVisibility() != GONE) {
+ int height = mIgnoredView.getMeasuredHeight();
+ MarginLayoutParams lp = (MarginLayoutParams) mIgnoredView.getLayoutParams();
+ height = height - lp.bottomMargin - lp.topMargin;
+ setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight() - height);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index 3d5a709..50417e9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -22,7 +22,6 @@
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
@@ -44,7 +43,7 @@
protected TextView mSecondLine;
private ImageView mPadLock;
protected int mState;
- protected ViewGroup mLabelContainer;
+ protected ButtonRelativeLayout mLabelContainer;
private View mExpandIndicator;
private View mExpandSpace;
protected ColorStateList mColorLabelActive;
@@ -92,7 +91,7 @@
}
protected void createLabel() {
- mLabelContainer = (ViewGroup) LayoutInflater.from(getContext())
+ mLabelContainer = (ButtonRelativeLayout) LayoutInflater.from(getContext())
.inflate(R.layout.qs_tile_label, this, false);
mLabelContainer.setClipChildren(false);
mLabelContainer.setClipToPadding(false);
@@ -140,7 +139,7 @@
}
if (!Objects.equals(mSecondLine.getText(), state.secondaryLabel)) {
mSecondLine.setText(state.secondaryLabel);
- mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) || mCollapsedView
+ mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel)
? View.GONE : View.VISIBLE);
}
boolean dualTarget = mDualTargetAllowed && state.dualTarget;
@@ -193,4 +192,9 @@
public View getLabelContainer() {
return mLabelContainer;
}
+
+ @Override
+ public View getSecondaryLabel() {
+ return mSecondLine;
+ }
}
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 b953323..70d51ee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
@@ -25,6 +25,7 @@
import android.service.quicksettings.Tile.STATE_ACTIVE
import android.view.Gravity
import android.view.View
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.RelativeLayout
@@ -43,7 +44,7 @@
private var paintColor = Color.WHITE
private var paintAnimator: ValueAnimator? = null
private var labelAnimator: ValueAnimator? = null
- private var mSideView: ImageView = ImageView(mContext)
+ private var sideView: ImageView = ImageView(mContext)
override var heightOverride: Int = HeightOverrideable.NO_OVERRIDE
init {
@@ -59,13 +60,14 @@
val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size)
addView(mIcon, 0, LayoutParams(iconSize, iconSize))
- mSideView.visibility = View.GONE
- addView(
- mSideView,
- -1,
- LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).apply {
- gravity = Gravity.CENTER_VERTICAL
- })
+ sideView.visibility = View.GONE
+ val sideViewLayoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
+ gravity = Gravity.CENTER_VERTICAL
+ marginStart = context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
+ }
+ addView(sideView, -1, sideViewLayoutParams)
+ sideView.adjustViewBounds = true
+ sideView.scaleType = ImageView.ScaleType.FIT_CENTER
mColorLabelActive = ColorStateList.valueOf(getColorForState(getContext(), STATE_ACTIVE))
changeLabelColor(getLabelColor(mState)) // Matches the default state of the tile
@@ -89,16 +91,17 @@
mLabelContainer.setPadding(0, 0, 0, 0)
(mLabelContainer.layoutParams as MarginLayoutParams).apply {
marginStart = context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
+ marginEnd = 0
+ gravity = Gravity.CENTER_VERTICAL or Gravity.START
}
mLabel.gravity = Gravity.START
mLabel.textDirection = TEXT_DIRECTION_LOCALE
mSecondLine.gravity = Gravity.START
mSecondLine.textDirection = TEXT_DIRECTION_LOCALE
- (mLabelContainer.layoutParams as LayoutParams).gravity =
- Gravity.CENTER_VERTICAL or Gravity.START
if (mCollapsedView) {
- mSecondLine.visibility = GONE
+ mSecondLine.alpha = 0f
+ mLabelContainer.setIgnoredView(mSecondLine)
}
}
@@ -183,9 +186,7 @@
private fun setLabelsColor(color: ColorStateList) {
mLabel.setTextColor(color)
- if (!mCollapsedView) {
- mSecondLine.setTextColor(color)
- }
+ mSecondLine.setTextColor(color)
}
private fun clearBackgroundAnimator() {
@@ -198,19 +199,17 @@
private fun loadSideViewDrawableIfNecessary(state: QSTile.State) {
if (state.sideViewDrawable != null) {
- (mSideView.layoutParams as MarginLayoutParams).apply {
- marginStart =
- context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
- }
- mSideView.setImageDrawable(state.sideViewDrawable)
- mSideView.visibility = View.VISIBLE
- mSideView.adjustViewBounds = true
- mSideView.scaleType = ImageView.ScaleType.FIT_CENTER
+ sideView.setImageDrawable(state.sideViewDrawable)
+ sideView.visibility = View.VISIBLE
} else {
- mSideView.setImageDrawable(null)
- mSideView.visibility = GONE
+ sideView.setImageDrawable(null)
+ sideView.visibility = GONE
}
}
override fun handleExpand(dualTarget: Boolean) {}
+
+ override fun getSecondaryIcon(): View {
+ return sideView
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index ff830bc..be40423 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -129,8 +129,15 @@
Intent intent = new Intent(mContext, WalletActivity.class)
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
- mActivityStarter.startActivity(intent, true /* dismissShade */,
- animationController);
+ if (mKeyguardStateController.isUnlocked()) {
+ mActivityStarter.startActivity(intent, true /* dismissShade */,
+ animationController);
+ } else {
+ mHost.collapsePanels();
+ // Do not use ActivityStarter here because the WalletActivity is required to be
+ // started without prompting keyguard when the device is locked.
+ mContext.startActivity(intent);
+ }
} else {
if (mQuickAccessWalletClient.createWalletIntent() == null) {
Log.w(TAG, "Could not get intent of the wallet app.");
@@ -147,7 +154,7 @@
protected void handleUpdateState(State state, Object arg) {
state.label = mLabel;
state.contentDescription = state.label;
- state.icon = ResourceIcon.get(R.drawable.ic_qs_wallet);
+ state.icon = ResourceIcon.get(R.drawable.ic_wallet_lockscreen);
boolean isDeviceLocked = !mKeyguardStateController.isUnlocked();
if (mQuickAccessWalletClient.isWalletServiceAvailable()) {
if (mHasCard) {
@@ -219,7 +226,12 @@
refreshState();
return;
}
- mCardViewDrawable = cards.get(0).getCardImage().loadDrawable(mContext);
+ int selectedIndex = response.getSelectedIndex();
+ if (selectedIndex >= cards.size()) {
+ Log.d(TAG, "Selected card index out of bounds.");
+ return;
+ }
+ mCardViewDrawable = cards.get(selectedIndex).getCardImage().loadDrawable(mContext);
mHasCard = true;
refreshState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
index 0a55fbe..3c6ab34 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
@@ -47,9 +47,9 @@
private ValueAnimator mColorAnimation;
private int mMainColorTo;
private float mCornerRadius;
- private Rect mBounds;
private ConcaveInfo mConcaveInfo;
private int mBottomEdgePosition;
+ private boolean mCornerRadiusEnabled;
public ScrimDrawable() {
mPaint = new Paint();
@@ -134,29 +134,50 @@
}
/**
- * Enable drawable shape to have rounded corners with provided radius
+ * Corner radius used by either concave or convex corners.
*/
public void setRoundedCorners(float radius) {
+ if (radius == mCornerRadius) {
+ return;
+ }
mCornerRadius = radius;
+ if (mConcaveInfo != null) {
+ mConcaveInfo.setCornerRadius(radius);
+ updatePath();
+ }
+ invalidateSelf();
}
/**
- * Make bottom edge concave with provided corner radius
+ * If we should draw a rounded rect instead of a rect.
*/
- public void setBottomEdgeConcave(float radius) {
- if (radius == 0) {
- // Disable clipping completely when there's no radius.
- mConcaveInfo = null;
+ public void setRoundedCornersEnabled(boolean enabled) {
+ if (mCornerRadiusEnabled == enabled) {
return;
}
- // only rounding top corners for clip out path
- float[] cornerRadii = new float[]{radius, radius, radius, radius, 0, 0, 0, 0};
- mConcaveInfo = new ConcaveInfo(radius, cornerRadii);
+ mCornerRadiusEnabled = enabled;
+ invalidateSelf();
+ }
+
+ /**
+ * If we should draw a concave rounded rect instead of a rect.
+ */
+ public void setBottomEdgeConcave(boolean enabled) {
+ if (enabled && mConcaveInfo != null) {
+ return;
+ }
+ if (!enabled) {
+ mConcaveInfo = null;
+ } else {
+ mConcaveInfo = new ConcaveInfo();
+ mConcaveInfo.setCornerRadius(mCornerRadius);
+ }
+ invalidateSelf();
}
/**
* Location of concave edge.
- * @see #setBottomEdgeConcave(float)
+ * @see #setBottomEdgeConcave(boolean)
*/
public void setBottomEdgePosition(int y) {
if (mBottomEdgePosition == y) {
@@ -176,34 +197,35 @@
mPaint.setAlpha(mAlpha);
if (mConcaveInfo != null) {
drawConcave(canvas);
- } else {
+ } else if (mCornerRadiusEnabled && mCornerRadius > 0) {
canvas.drawRoundRect(getBounds().left, getBounds().top, getBounds().right,
getBounds().bottom + mCornerRadius,
/* x radius*/ mCornerRadius, /* y radius*/ mCornerRadius, mPaint);
+ } else {
+ canvas.drawRect(getBounds().left, getBounds().top, getBounds().right,
+ getBounds().bottom, mPaint);
}
}
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ updatePath();
+ }
+
private void drawConcave(Canvas canvas) {
- // checking if width of clip out path needs to change
- if (mBounds == null
- || getBounds().right != mBounds.right
- || getBounds().left != mBounds.left) {
- mBounds = getBounds();
- updatePath();
- }
canvas.clipOutPath(mConcaveInfo.mPath);
canvas.drawRect(getBounds().left, getBounds().top, getBounds().right,
mBottomEdgePosition + mConcaveInfo.mPathOverlap, mPaint);
}
private void updatePath() {
- mConcaveInfo.mPath.reset();
- if (mBounds == null) {
- mBounds = getBounds();
+ if (mConcaveInfo == null) {
+ return;
}
+ mConcaveInfo.mPath.reset();
float top = mBottomEdgePosition;
float bottom = mBottomEdgePosition + mConcaveInfo.mPathOverlap;
- mConcaveInfo.mPath.addRoundRect(mBounds.left, top, mBounds.right, bottom,
+ mConcaveInfo.mPath.addRoundRect(getBounds().left, top, getBounds().right, bottom,
mConcaveInfo.mCornerRadii, Path.Direction.CW);
}
@@ -213,13 +235,20 @@
}
private static class ConcaveInfo {
- private final float mPathOverlap;
+ private float mPathOverlap;
private final float[] mCornerRadii;
private final Path mPath = new Path();
- ConcaveInfo(float pathOverlap, float[] cornerRadii) {
- mPathOverlap = pathOverlap;
- mCornerRadii = cornerRadii;
+ ConcaveInfo() {
+ mCornerRadii = new float[] {0, 0, 0, 0, 0, 0, 0, 0};
+ }
+
+ public void setCornerRadius(float radius) {
+ mPathOverlap = radius;
+ mCornerRadii[0] = radius;
+ mCornerRadii[1] = radius;
+ mCornerRadii[2] = radius;
+ mCornerRadii[3] = radius;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
index 0d9ade6d..1a5c9ee 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
@@ -31,14 +31,12 @@
import android.util.AttributeSet;
import android.view.View;
-import androidx.annotation.DimenRes;
import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
-import com.android.systemui.R;
import java.util.concurrent.Executor;
@@ -50,9 +48,6 @@
*/
public class ScrimView extends View {
- @DimenRes
- private static final int CORNER_RADIUS = R.dimen.notification_scrim_corner_radius;
-
private final Object mColorLock = new Object();
@GuardedBy("mColorLock")
@@ -301,13 +296,10 @@
* Make bottom edge concave so overlap between layers is not visible for alphas between 0 and 1
* @return height of concavity
*/
- public float enableBottomEdgeConcave(boolean clipScrim) {
+ public void enableBottomEdgeConcave(boolean clipScrim) {
if (mDrawable instanceof ScrimDrawable) {
- float radius = clipScrim ? getResources().getDimensionPixelSize(CORNER_RADIUS) : 0;
- ((ScrimDrawable) mDrawable).setBottomEdgeConcave(radius);
- return radius;
+ ((ScrimDrawable) mDrawable).setBottomEdgeConcave(clipScrim);
}
- return 0;
}
/**
@@ -321,12 +313,11 @@
}
/**
- * Enable view to have rounded corners with radius of {@link #CORNER_RADIUS}
+ * Enable view to have rounded corners.
*/
- public void enableRoundedCorners() {
+ public void enableRoundedCorners(boolean enabled) {
if (mDrawable instanceof ScrimDrawable) {
- int radius = getResources().getDimensionPixelSize(CORNER_RADIUS);
- ((ScrimDrawable) mDrawable).setRoundedCorners(radius);
+ ((ScrimDrawable) mDrawable).setRoundedCornersEnabled(enabled);
}
}
@@ -340,4 +331,15 @@
mDrawableBounds.set((int) left, (int) top, (int) right, (int) bottom);
mDrawable.setBounds(mDrawableBounds);
}
+
+ /**
+ * Corner radius of both concave or convex corners.
+ * @see #enableRoundedCorners(boolean)
+ * @see #enableBottomEdgeConcave(boolean)
+ */
+ public void setCornerRadius(int radius) {
+ if (mDrawable instanceof ScrimDrawable) {
+ ((ScrimDrawable) mDrawable).setRoundedCorners(radius);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index a4bb095..7c0496b 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -16,8 +16,6 @@
package com.android.systemui.sensorprivacy
-import android.app.KeyguardManager
-import android.app.KeyguardManager.KeyguardDismissCallback
import android.content.DialogInterface
import android.content.Intent.EXTRA_PACKAGE_NAME
import android.content.pm.PackageManager
@@ -28,16 +26,15 @@
import android.os.Bundle
import android.os.Handler
import android.text.Html
-import android.util.Log
import android.view.View.GONE
import android.view.View.VISIBLE
import android.widget.ImageView
import com.android.internal.app.AlertActivity
import com.android.internal.widget.DialogTitle
-import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.R
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
-
+import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
/**
@@ -48,8 +45,8 @@
*/
class SensorUseStartedActivity @Inject constructor(
private val sensorPrivacyController: IndividualSensorPrivacyController,
- private val keyguardManager: KeyguardManager,
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor
+ private val keyguardStateController: KeyguardStateController,
+ private val keyguardDismissUtil: KeyguardDismissUtil
) : AlertActivity(), DialogInterface.OnClickListener {
companion object {
@@ -180,17 +177,11 @@
override fun onClick(dialog: DialogInterface?, which: Int) {
when (which) {
BUTTON_POSITIVE -> {
- if (keyguardUpdateMonitor.getUserHasTrust(userId)) {
- keyguardManager
- .requestDismissKeyguard(this, object : KeyguardDismissCallback() {
- override fun onDismissError() {
- Log.e(LOG_TAG, "Cannot dismiss keyguard")
- }
-
- override fun onDismissSucceeded() {
- disableSensorPrivacy()
- }
- })
+ if (keyguardStateController.isMethodSecure && keyguardStateController.isShowing) {
+ keyguardDismissUtil.executeWhenUnlocked({
+ disableSensorPrivacy()
+ false
+ }, false)
} else {
disableSensorPrivacy()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index c1eaaaf..6b68fc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -888,7 +888,14 @@
if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
// The face timeout message is not very actionable, let's ask the user to
// manually retry.
- showSwipeUpToUnlock();
+ if (!mStatusBarKeyguardViewManager.isBouncerShowing()
+ && mKeyguardUpdateMonitor.isUdfpsEnrolled()) {
+ // suggest trying fingerprint
+ showTransientIndication(R.string.keyguard_try_fingerprint);
+ } else {
+ // suggest swiping up to unlock (try face auth again or swipe up to bouncer)
+ showSwipeUpToUnlock();
+ }
} else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.showBouncerMessage(errString, mInitialTextColorState);
} else if (mKeyguardUpdateMonitor.isScreenOn()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
index 3bf1ff2..91415f27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
@@ -34,8 +34,14 @@
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.leak.RotationUtils
import com.android.systemui.R
+import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
import javax.inject.Inject
+import kotlin.math.min
+import kotlin.math.pow
+
+private const val MAX_DEBOUNCE_LEVEL = 3
+private const val BASE_DEBOUNCE_TIME = 2000
/***
* Controls the ripple effect that shows when wired charging begins.
@@ -47,9 +53,11 @@
batteryController: BatteryController,
configurationController: ConfigurationController,
featureFlags: FeatureFlags,
- private val context: Context
+ private val context: Context,
+ private val windowManager: WindowManager,
+ private val systemClock: SystemClock
) {
- private var charging: Boolean? = null
+ private var pluggedIn: Boolean? = null
private val rippleEnabled: Boolean = featureFlags.isChargingRippleEnabled &&
!SystemProperties.getBoolean("persist.debug.suppress-charging-ripple", false)
private var normalizedPortPosX: Float = context.resources.getFloat(
@@ -68,6 +76,8 @@
or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
setTrustedOverlay()
}
+ private var lastTriggerTime: Long? = null
+ private var debounceLevel = 0
@VisibleForTesting
var rippleView: ChargingRippleView = ChargingRippleView(context, attrs = null)
@@ -76,19 +86,18 @@
val batteryStateChangeCallback = object : BatteryController.BatteryStateChangeCallback {
override fun onBatteryLevelChanged(
level: Int,
- pluggedIn: Boolean,
- nowCharging: Boolean
+ nowPluggedIn: Boolean,
+ charging: Boolean
) {
// Suppresses the ripple when it's disabled, or when the state change comes
// from wireless charging.
- if (!rippleEnabled || batteryController.isWirelessCharging) {
+ if (!rippleEnabled || batteryController.isPluggedInWireless) {
return
}
- val wasCharging = charging
- charging = nowCharging
- // Only triggers when the keyguard is active and the device is just plugged in.
- if ((wasCharging == null || !wasCharging) && nowCharging) {
- startRipple()
+ val wasPluggedIn = pluggedIn
+ pluggedIn = nowPluggedIn
+ if ((wasPluggedIn == null || !wasPluggedIn) && nowPluggedIn) {
+ startRippleWithDebounce()
}
}
}
@@ -118,6 +127,22 @@
updateRippleColor()
}
+ // Lazily debounce ripple to avoid triggering ripple constantly (e.g. from flaky chargers).
+ internal fun startRippleWithDebounce() {
+ val now = systemClock.elapsedRealtime()
+ // Debounce wait time = 2 ^ debounce level
+ if (lastTriggerTime == null ||
+ (now - lastTriggerTime!!) > BASE_DEBOUNCE_TIME * (2.0.pow(debounceLevel))) {
+ // Not waiting for debounce. Start ripple.
+ startRipple()
+ debounceLevel = 0
+ } else {
+ // Still waiting for debounce. Ignore ripple and bump debounce level.
+ debounceLevel = min(MAX_DEBOUNCE_LEVEL, debounceLevel + 1)
+ }
+ lastTriggerTime = now
+ }
+
fun startRipple() {
if (!rippleEnabled || rippleView.rippleInProgress || rippleView.parent != null) {
// Skip if ripple is still playing, or not playing but already added the parent
@@ -125,7 +150,6 @@
// the animation ends.)
return
}
- val mWM = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
windowLayoutParams.packageName = context.opPackageName
rippleView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
override fun onViewDetachedFromWindow(view: View?) {}
@@ -133,12 +157,12 @@
override fun onViewAttachedToWindow(view: View?) {
layoutRipple()
rippleView.startRipple(Runnable {
- mWM.removeView(rippleView)
+ windowManager.removeView(rippleView)
})
rippleView.removeOnAttachStateChangeListener(this)
}
})
- mWM.addView(rippleView, windowLayoutParams)
+ windowManager.addView(rippleView, windowLayoutParams)
}
private fun layoutRipple() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index 30d9841..26c6fe9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -217,7 +217,7 @@
}
companion object {
- const val BYPASS_PANEL_FADE_DURATION = 67
+ const val BYPASS_FADE_DURATION = 67
private const val FACE_UNLOCK_BYPASS_NO_OVERRIDE = 0
private const val FACE_UNLOCK_BYPASS_ALWAYS = 1
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 8574830..a86fbc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -523,12 +523,13 @@
private final Rect mKeyguardStatusAreaClipBounds = new Rect();
private int mOldLayoutDirection;
private NotificationShelfController mNotificationShelfController;
+ private int mScrimCornerRadius;
+ private int mScreenCornerRadius;
private final QuickAccessWalletClient mQuickAccessWalletClient;
private final Executor mUiExecutor;
private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
- private int mScrimCornerRadius;
private KeyguardMediaController mKeyguardMediaController;
private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
@@ -807,6 +808,10 @@
com.android.internal.R.dimen.status_bar_height);
mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize(
R.dimen.heads_up_status_bar_padding);
+ mScrimCornerRadius = mResources.getDimensionPixelSize(
+ R.dimen.notification_scrim_corner_radius);
+ mScreenCornerRadius = mResources.getDimensionPixelSize(
+ com.android.internal.R.dimen.rounded_corner_radius);
}
private void updateViewControllers(KeyguardStatusView keyguardStatusView,
@@ -871,8 +876,6 @@
public void updateResources() {
mSplitShadeNotificationsTopPadding =
mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade);
- mScrimCornerRadius =
- mResources.getDimensionPixelSize(R.dimen.notification_scrim_corner_radius);
int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
mShouldUseSplitNotificationShade =
@@ -2057,7 +2060,9 @@
int bottom = 0;
int left = 0;
int right = 0;
- boolean visible = qsFraction > 0 || qsPanelBottomY > 0;
+ boolean visible = (qsFraction > 0 || qsPanelBottomY > 0)
+ && !mShouldUseSplitNotificationShade;
+ int radius = mScrimCornerRadius;
if (visible || !mShouldUseSplitNotificationShade) {
if (!mShouldUseSplitNotificationShade) {
float notificationTop = mAmbientState.getStackY() - mQsNotificationTopPadding;
@@ -2065,6 +2070,8 @@
bottom = getView().getBottom();
left = getView().getLeft();
right = getView().getRight();
+ radius = (int) MathUtils.lerp(mScreenCornerRadius, mScrimCornerRadius,
+ Math.min(top / (float) mScrimCornerRadius, 1f));
} else {
top = Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding);
bottom = mNotificationStackScrollLayoutController.getHeight();
@@ -2073,17 +2080,18 @@
}
}
+ // Fancy clipping for quick settings
+ if (mQs != null) {
+ mQs.setFancyClipping(top, bottom, radius, visible);
+ }
if (!mShouldUseSplitNotificationShade) {
- // Fancy clipping for quick settings
- if (mQs != null) {
- mQs.setFancyClipping(top, bottom, mScrimCornerRadius, visible);
- }
// The padding on this area is large enough that we can use a cheaper clipping strategy
mKeyguardStatusAreaClipBounds.set(left, top, right, bottom);
mKeyguardStatusViewController.setClipBounds(visible
? mKeyguardStatusAreaClipBounds : null);
}
mScrimController.setNotificationsBounds(left, top, right, bottom);
+ mScrimController.setScrimCornerRadius(radius);
}
private int calculateQsBottomPosition(float qsExpansionFraction) {
@@ -2237,6 +2245,7 @@
break;
case FLING_HIDE:
default:
+ mQs.closeDetail();
target = 0;
}
if (target == mQsExpansionHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 0d96ead..6fb18d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -269,7 +269,7 @@
updateThemeColors();
behindScrim.enableBottomEdgeConcave(mClipsQsScrim);
- mNotificationsScrim.enableRoundedCorners();
+ mNotificationsScrim.enableRoundedCorners(true);
if (mScrimBehindChangeRunnable != null) {
mScrimBehind.setChangeRunnable(mScrimBehindChangeRunnable, mMainExecutor);
@@ -294,6 +294,17 @@
mKeyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback);
}
+ /**
+ * Sets corner radius of scrims.
+ */
+ public void setScrimCornerRadius(int radius) {
+ if (mScrimBehind == null || mNotificationsScrim == null) {
+ return;
+ }
+ mScrimBehind.setCornerRadius(radius);
+ mNotificationsScrim.setCornerRadius(radius);
+ }
+
void setScrimVisibleListener(Consumer<Integer> listener) {
mScrimVisibleListener = listener;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 14aeca1..f403cc94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -615,7 +615,7 @@
boolean needsFading = needsBypassFading();
if (needsFading) {
delay = 0;
- fadeoutDuration = KeyguardBypassController.BYPASS_PANEL_FADE_DURATION;
+ fadeoutDuration = KeyguardBypassController.BYPASS_FADE_DURATION;
} else if (wakeUnlockPulsing) {
delay = 0;
fadeoutDuration = 240;
@@ -979,7 +979,6 @@
resetAlternateAuth(false);
executeAfterKeyguardGoneAction();
}
-
}
public void showBouncerMessage(String message, ColorStateList colorState) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 6ae5e90..95a7316 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -43,6 +43,13 @@
boolean isPluggedIn();
/**
+ * Returns {@code true} if the device is currently plugged in via wireless charger.
+ */
+ default boolean isPluggedInWireless() {
+ return false;
+ }
+
+ /**
* Returns {@code true} if the device is currently in power save mode.
*/
boolean isPowerSave();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 288eb3d..9e2c478 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -72,6 +72,7 @@
protected int mLevel;
protected boolean mPluggedIn;
+ private boolean mPluggedInWireless;
protected boolean mCharging;
private boolean mStateUnknown = false;
private boolean mCharged;
@@ -175,6 +176,8 @@
* intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
/ intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
+ mPluggedInWireless = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0)
+ == BatteryManager.BATTERY_PLUGGED_WIRELESS;
final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
@@ -260,6 +263,11 @@
}
@Override
+ public boolean isPluggedInWireless() {
+ return mPluggedInWireless;
+ }
+
+ @Override
public void getEstimatedTimeRemainingString(EstimateFetchCompletion completion) {
// Need to fetch or refresh the estimate, but it may involve binder calls so offload the
// work
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index 08cdebd..d2bbcd50d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -34,6 +34,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.os.Debug;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -59,6 +60,7 @@
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -489,12 +491,16 @@
? "Dumping..."
: mContext.getString(R.string.heap_dump_tile_name);
if (pmi != null) {
+ final long views = Debug.countInstancesOfClass(View.class);
+ final long enrs = Debug.countInstancesOfClass(ExpandableNotificationRow.class);
+ Log.v(TAG, String.format("updating tile state; rss=%d", pmi.currentRss));
+ Log.v(TAG, String.format("views: %d; ExpandableNotificationRows: %d", views, enrs));
icon.setRss(pmi.currentRss);
state.secondaryLabel =
String.format(
- "rss: %s / %s",
+ "rss=%s views=%d\nenr=%d",
formatBytes(pmi.currentRss * 1024),
- formatBytes(gm.mHeapLimit * 1024));
+ views, enrs);
} else {
icon.setRss(0);
state.secondaryLabel = null;
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 81bb819..74077a2 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -236,6 +236,14 @@
oneHanded.registerTransitionCallback(new OneHandedTransitionCallback() {
@Override
+ public void onStartTransition(boolean isEntering) {
+ mSysUiMainExecutor.execute(() -> {
+ mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
+ true).commitUpdate(DEFAULT_DISPLAY);
+ });
+ }
+
+ @Override
public void onStartFinished(Rect bounds) {
mSysUiMainExecutor.execute(() -> {
mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 39ebe68..d07a8da 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -19,14 +19,21 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.smartspace.SmartspaceTarget;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
+import android.os.Handler;
+import android.os.UserHandle;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.util.AttributeSet;
@@ -47,12 +54,15 @@
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.settings.SecureSettings;
import org.junit.Before;
import org.junit.Test;
@@ -62,6 +72,8 @@
import org.mockito.MockitoAnnotations;
import org.mockito.verification.VerificationMode;
+import java.util.Collections;
+import java.util.List;
import java.util.concurrent.Executor;
@SmallTest
@@ -114,10 +126,23 @@
ActivityStarter mActivityStarter;
@Mock
FalsingManager mFalsingManager;
+ @Mock
+ KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ KeyguardBypassController mBypassController;
+ @Mock
+ Handler mHandler;
+ @Mock
+ UserTracker mUserTracker;
+ @Mock
+ SecureSettings mSecureSettings;
private KeyguardClockSwitchController mController;
private View mStatusArea;
+ private static final int USER_ID = 5;
+ private static final int MANAGED_USER_ID = 15;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@@ -152,7 +177,12 @@
mConfigurationController,
mSystemUIFactory,
mActivityStarter,
- mFalsingManager
+ mFalsingManager,
+ mKeyguardUpdateMonitor,
+ mBypassController,
+ mHandler,
+ mUserTracker,
+ mSecureSettings
);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
@@ -253,6 +283,89 @@
verify(mSmartspaceView, times(2)).setPrimaryTextColor(anyInt());
}
+ @Test
+ public void doNotFilterRegularTarget() {
+ setupPrimaryAndManagedUser();
+ mController.init();
+
+ when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(USER_ID))).thenReturn(0);
+ when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(MANAGED_USER_ID)))
+ .thenReturn(0);
+
+ mController.getSettingsObserver().onChange(true, null);
+
+ SmartspaceTarget t = mock(SmartspaceTarget.class);
+ when(t.isSensitive()).thenReturn(false);
+ when(t.getUserHandle()).thenReturn(new UserHandle(USER_ID));
+ assertEquals(false, mController.filterSmartspaceTarget(t));
+
+ reset(t);
+ when(t.isSensitive()).thenReturn(false);
+ when(t.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID));
+ assertEquals(false, mController.filterSmartspaceTarget(t));
+ }
+
+ @Test
+ public void filterAllSensitiveTargetsAllUsers() {
+ setupPrimaryAndManagedUser();
+ mController.init();
+
+ when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(USER_ID))).thenReturn(0);
+ when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(MANAGED_USER_ID)))
+ .thenReturn(0);
+
+ mController.getSettingsObserver().onChange(true, null);
+
+ SmartspaceTarget t = mock(SmartspaceTarget.class);
+ when(t.isSensitive()).thenReturn(true);
+ when(t.getUserHandle()).thenReturn(new UserHandle(USER_ID));
+ assertEquals(true, mController.filterSmartspaceTarget(t));
+
+ reset(t);
+ when(t.isSensitive()).thenReturn(true);
+ when(t.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID));
+ assertEquals(true, mController.filterSmartspaceTarget(t));
+ }
+
+ @Test
+ public void filterSensitiveManagedUserTargets() {
+ setupPrimaryAndManagedUser();
+ mController.init();
+
+ when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(USER_ID))).thenReturn(1);
+ when(mSecureSettings.getIntForUser(anyString(), anyInt(), eq(MANAGED_USER_ID)))
+ .thenReturn(0);
+
+ mController.getSettingsObserver().onChange(true, null);
+
+ SmartspaceTarget t = mock(SmartspaceTarget.class);
+ when(t.isSensitive()).thenReturn(true);
+ when(t.getUserHandle()).thenReturn(new UserHandle(USER_ID));
+ assertEquals(false, mController.filterSmartspaceTarget(t));
+
+ reset(t);
+ when(t.isSensitive()).thenReturn(true);
+ when(t.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID));
+ assertEquals(true, mController.filterSmartspaceTarget(t));
+ }
+
+ private void setupPrimaryAndManagedUser() {
+ UserInfo userInfo = mock(UserInfo.class);
+ when(userInfo.isManagedProfile()).thenReturn(true);
+ when(userInfo.getUserHandle()).thenReturn(new UserHandle(MANAGED_USER_ID));
+ when(mUserTracker.getUserProfiles()).thenReturn(List.of(userInfo));
+
+ when(mUserTracker.getUserId()).thenReturn(USER_ID);
+ when(mUserTracker.getUserHandle()).thenReturn(new UserHandle(USER_ID));
+ }
+
+ private void setupPrimaryAndNoManagedUser() {
+ when(mUserTracker.getUserProfiles()).thenReturn(Collections.emptyList());
+
+ when(mUserTracker.getUserId()).thenReturn(USER_ID);
+ when(mUserTracker.getUserHandle()).thenReturn(new UserHandle(USER_ID));
+ }
+
private void verifyAttachment(VerificationMode times) {
verify(mClockManager, times).addOnClockChangedListener(
any(ClockManager.ClockChangedListener.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 73b0a6b..94252d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -95,7 +95,6 @@
@Mock private lateinit var collapsedSet: ConstraintSet
@Mock private lateinit var mediaOutputDialogFactory: MediaOutputDialogFactory
private lateinit var appIcon: ImageView
- private lateinit var appName: TextView
private lateinit var albumView: ImageView
private lateinit var titleText: TextView
private lateinit var artistText: TextView
@@ -138,8 +137,6 @@
whenever(holder.player).thenReturn(view)
appIcon = ImageView(context)
whenever(holder.appIcon).thenReturn(appIcon)
- appName = TextView(context)
- whenever(holder.appName).thenReturn(appName)
albumView = ImageView(context)
whenever(holder.albumView).thenReturn(albumView)
titleText = TextView(context)
@@ -220,7 +217,6 @@
val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null)
player.bindPlayer(state, PACKAGE)
- assertThat(appName.getText()).isEqualTo(APP)
assertThat(titleText.getText()).isEqualTo(TITLE)
assertThat(artistText.getText()).isEqualTo(ARTIST)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
index a9d256b..ac24cde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.media
+import android.app.smartspace.SmartspaceAction
import android.app.smartspace.SmartspaceTarget
import android.graphics.Color
import androidx.test.filters.SmallTest
@@ -72,6 +73,8 @@
private lateinit var executor: Executor
@Mock
private lateinit var smartspaceData: SmartspaceTarget
+ @Mock
+ private lateinit var smartspaceMediaRecommendationItem: SmartspaceAction
private lateinit var mediaDataFilter: MediaDataFilter
private lateinit var dataMain: MediaData
@@ -97,6 +100,7 @@
emptyList(), emptyList(), PACKAGE, null, null, device, true, null)
`when`(smartspaceData.smartspaceTargetId).thenReturn(SMARTSPACE_KEY)
+ `when`(smartspaceData.iconGrid).thenReturn(listOf(smartspaceMediaRecommendationItem))
}
private fun setUser(id: Int) {
@@ -222,7 +226,7 @@
}
@Test
- fun testOnSmartspaceMediaDataLoaded_noMedia_usesSmartspace() {
+ fun testOnSmartspaceMediaDataLoaded_noMedia_nonEmptyRecommendation_usesSmartspace() {
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
verify(listener).onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData))
@@ -230,7 +234,18 @@
}
@Test
- fun testOnSmartspaceMediaDataLoaded_noRecentMedia_usesSmartspace() {
+ fun testOnSmartspaceMediaDataLoaded_noMedia_emptyRecommendation_showsNothing() {
+ `when`(smartspaceData.iconGrid).thenReturn(listOf())
+
+ mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+
+ verify(listener, never())
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData))
+ assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
+ }
+
+ @Test
+ fun testOnSmartspaceMediaDataLoaded_noRecentMedia_nonEmptyRecommendation_usesSmartspace() {
val dataOld = dataMain.copy(active = false, lastActive = 0L)
mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
@@ -240,6 +255,19 @@
}
@Test
+ fun testOnSmartspaceMediaDataLoaded_noRecentMedia_emptyRecommendation_showsNothing() {
+ `when`(smartspaceData.iconGrid).thenReturn(listOf())
+
+ val dataOld = dataMain.copy(active = false, lastActive = 0L)
+ mediaDataFilter.onMediaDataLoaded(KEY, null, dataOld)
+ mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+
+ verify(listener, never())
+ .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData))
+ assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
+ }
+
+ @Test
fun testOnSmartspaceMediaDataLoaded_hasRecentMedia_usesMedia() {
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = System.currentTimeMillis())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 678f89a..daa8b4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -425,4 +425,30 @@
assertThat(mediaDataCaptor.value.resumption).isTrue()
assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTimeMillis)
}
+
+ @Test
+ fun testTooManyCompactActions_isTruncated() {
+ // GIVEN a notification where too many compact actions were specified
+ val notif = SbnBuilder().run {
+ setPkg(PACKAGE_NAME)
+ modifyNotification(context).also {
+ it.setSmallIcon(android.R.drawable.ic_media_pause)
+ it.setStyle(MediaStyle().apply {
+ setMediaSession(session.sessionToken)
+ setShowActionsInCompactView(0, 1, 2, 3, 4)
+ })
+ }
+ build()
+ }
+
+ // WHEN the notification is loaded
+ mediaDataManager.onNotificationAdded(KEY, notif)
+ assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+ assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+
+ // THEN only the first MAX_COMPACT_ACTIONS are actually set
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor))
+ assertThat(mediaDataCaptor.value.actionsToShowInCompact.size).isEqualTo(
+ MediaDataManager.MAX_COMPACT_ACTIONS)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java
index 37b7cbe..0eeb955 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java
@@ -36,6 +36,7 @@
import com.android.systemui.SysuiTestableContext;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.navigationbar.NavigationBarView;
+import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -67,6 +68,7 @@
mDependency.injectMockDependency(OverviewProxyService.class);
mDependency.injectMockDependency(KeyguardStateController.class);
mDependency.injectMockDependency(NavigationBarController.class);
+ mDependency.injectMockDependency(EdgeBackGestureHandler.class);
mNavBar = new NavigationBarView(context, null);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index f0c48bd..4ec45b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -72,6 +72,7 @@
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
@@ -132,6 +133,7 @@
mDependency.injectMockDependency(StatusBarStateController.class);
mDependency.injectMockDependency(NavigationBarController.class);
mOverviewProxyService = mDependency.injectMockDependency(OverviewProxyService.class);
+ mDependency.injectMockDependency(EdgeBackGestureHandler.class);
TestableLooper.get(this).runWithLooper(() -> {
mNavigationBar = createNavBar(mContext);
mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
index 7e0920c..e4d32f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
@@ -32,9 +32,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.navigationbar.NavigationBarTransitions;
-import com.android.systemui.navigationbar.NavigationBarView;
-import com.android.systemui.navigationbar.NavigationModeController;
+import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.CommandQueue;
@@ -60,6 +58,7 @@
mDependency.injectMockDependency(StatusBarStateController.class);
mDependency.injectMockDependency(KeyguardStateController.class);
mDependency.injectMockDependency(NavigationBarController.class);
+ mDependency.injectMockDependency(EdgeBackGestureHandler.class);
doReturn(mContext)
.when(mDependency.injectMockDependency(NavigationModeController.class))
.getCurrentUserContext();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index 2f28b13..5cdad05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -207,7 +207,25 @@
}
@Test
- public void testHandleClick_hasCards_startWalletActivity() {
+ public void testHandleClick_hasCards_deviceLocked_startWalletActivity() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(false);
+ setUpWalletCard(/* hasCard= */ true);
+
+ mTile.handleClick(null /* view */);
+ mTestableLooper.processAllMessages();
+
+ verify(mSpiedContext).startActivity(mIntentCaptor.capture());
+
+ Intent nextStartedIntent = mIntentCaptor.getValue();
+ String walletClassName = "com.android.systemui.wallet.ui.WalletActivity";
+
+ assertNotNull(nextStartedIntent);
+ assertThat(nextStartedIntent.getComponent().getClassName()).isEqualTo(walletClassName);
+ }
+
+ @Test
+ public void testHandleClick_hasCards_deviceUnlocked_startWalletActivity() {
+ when(mKeyguardStateController.isUnlocked()).thenReturn(true);
setUpWalletCard(/* hasCard= */ true);
mTile.handleClick(null /* view */);
@@ -226,7 +244,7 @@
@Test
public void testHandleUpdateState_updateLabelAndIcon() {
QSTile.State state = new QSTile.State();
- QSTile.Icon icon = QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_wallet);
+ QSTile.Icon icon = QSTileImpl.ResourceIcon.get(R.drawable.ic_wallet_lockscreen);
mTile.handleUpdateState(state, null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
index 5e783a5..03744b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.charging
-import android.content.Context
import android.testing.AndroidTestingRunner
import android.view.View
import android.view.WindowManager
@@ -26,7 +25,7 @@
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.time.FakeSystemClock
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -37,6 +36,7 @@
import org.mockito.Mockito.any
import org.mockito.Mockito.eq
import org.mockito.Mockito.reset
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -50,6 +50,7 @@
@Mock private lateinit var configurationController: ConfigurationController
@Mock private lateinit var rippleView: ChargingRippleView
@Mock private lateinit var windowManager: WindowManager
+ private val systemClock = FakeSystemClock()
@Before
fun setUp() {
@@ -57,9 +58,8 @@
`when`(featureFlags.isChargingRippleEnabled).thenReturn(true)
controller = WiredChargingRippleController(
commandRegistry, batteryController, configurationController,
- featureFlags, context)
+ featureFlags, context, windowManager, systemClock)
controller.rippleView = rippleView // Replace the real ripple view with a mock instance
- context.addMockSystemService(Context.WINDOW_SERVICE, windowManager)
}
@Test
@@ -71,8 +71,8 @@
// Verify ripple added to window manager.
captor.value.onBatteryLevelChanged(
0 /* unusedBatteryLevel */,
- false /* plugged in */,
- true /* charging */)
+ true /* plugged in */,
+ false /* charging */)
val attachListenerCaptor =
ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
verify(rippleView).addOnAttachStateChangeListener(attachListenerCaptor.capture())
@@ -103,4 +103,37 @@
captor.value.onUiModeChanged()
verify(rippleView).setColor(ArgumentMatchers.anyInt())
}
+
+ @Test
+ fun testDebounceRipple() {
+ var time: Long = 0
+ systemClock.setElapsedRealtime(time)
+
+ controller.startRippleWithDebounce()
+ verify(rippleView).addOnAttachStateChangeListener(ArgumentMatchers.any())
+
+ reset(rippleView)
+ // Wait a short while and trigger.
+ time += 100
+ systemClock.setElapsedRealtime(time)
+ controller.startRippleWithDebounce()
+
+ // Verify the ripple is debounced.
+ verify(rippleView, never()).addOnAttachStateChangeListener(ArgumentMatchers.any())
+
+ // Trigger many times.
+ for (i in 0..100) {
+ time += 100
+ systemClock.setElapsedRealtime(time)
+ controller.startRippleWithDebounce()
+ }
+ // Verify all attempts are debounced.
+ verify(rippleView, never()).addOnAttachStateChangeListener(ArgumentMatchers.any())
+
+ // Wait a long while and trigger.
+ systemClock.setElapsedRealtime(time + 500000)
+ controller.startRippleWithDebounce()
+ // Verify that ripple is triggered.
+ verify(rippleView).addOnAttachStateChangeListener(ArgumentMatchers.any())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index ab7cbf7..bfb98de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -4,6 +4,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -309,6 +310,7 @@
// Put RSSI in the middle of the range.
rssi += amountPerLevel / 2;
when(mVcnTransportInfo.getWifiInfo()).thenReturn(mWifiInfo);
+ when(mVcnTransportInfo.makeCopy(anyLong())).thenReturn(mVcnTransportInfo);
when(mWifiInfo.getRssi()).thenReturn(rssi);
when(mWifiInfo.isCarrierMerged()).thenReturn(true);
when(mWifiInfo.getSubscriptionId()).thenReturn(1);
@@ -318,6 +320,7 @@
protected void setWifiStateForVcn(boolean connected, String ssid) {
when(mVcnTransportInfo.getWifiInfo()).thenReturn(mWifiInfo);
+ when(mVcnTransportInfo.makeCopy(anyLong())).thenReturn(mVcnTransportInfo);
when(mWifiInfo.getSSID()).thenReturn(ssid);
when(mWifiInfo.isCarrierMerged()).thenReturn(true);
when(mWifiInfo.getSubscriptionId()).thenReturn(1);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
index d8271e8..db6164d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
@@ -71,6 +71,10 @@
mCurrentTimeMillis = millis;
}
+ public void setElapsedRealtime(long millis) {
+ mElapsedRealtime = millis;
+ }
+
/**
* Advances the time tracked by the fake clock and notifies any listeners that the time has
* changed (for example, an attached {@link FakeExecutor} may fire its pending runnables).
diff --git a/services/Android.bp b/services/Android.bp
index 20b89de..2281a15 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -180,68 +180,12 @@
" --hide-package com.google.android.startop.iorap" +
" --hide DeprecationMismatch" +
" --hide HiddenTypedefConstant",
- visibility: ["//visibility:private"],
+ visibility: ["//frameworks/base:__subpackages__"],
filter_packages: ["com.android."],
}
droidstubs {
- name: "services-stubs.sources",
- srcs: [":services-all-sources"],
- defaults: ["services-stubs-default"],
- check_api: {
- current: {
- api_file: "api/current.txt",
- removed_api_file: "api/removed.txt",
- },
- last_released: {
- api_file: ":android.api.system-server.latest",
- removed_api_file: ":removed.api.system-server.latest",
- baseline_file: ":android-incompatibilities.api.system-server.latest",
- },
- api_lint: {
- enabled: true,
- new_since: ":android.api.system-server.latest",
- baseline_file: "api/lint-baseline.txt",
- },
- },
- dists: [
- {
- targets: [
- "sdk",
- "win_sdk",
- ],
- dir: "apistubs/android/system-server/api",
- dest: "android.txt",
- tag: ".api.txt",
- },
- {
- targets: [
- "sdk",
- "win_sdk",
- ],
- dir: "apistubs/android/system-server/api",
- dest: "removed.txt",
- tag: ".removed-api.txt",
- },
- ],
-}
-
-java_library {
- name: "android_system_server_stubs_current",
- defaults: ["android_stubs_dists_default"],
- srcs: [":services-stubs.sources"],
- installable: false,
- static_libs: ["android_module_lib_stubs_current"],
- sdk_version: "none",
- system_modules: "none",
- java_version: "1.8",
- dist: {
- dir: "apistubs/android/system-server",
- },
-}
-
-droidstubs {
- name: "services-non-updatable-stubs.sources",
+ name: "services-non-updatable-stubs",
srcs: [":services-non-updatable-sources"],
defaults: ["services-stubs-default"],
check_api: {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index fd355d8..dc2628f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -16,6 +16,8 @@
package com.android.server.accessibility;
+import static android.content.pm.PackageManagerInternal.PACKAGE_INSTALLER;
+
import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.NonNull;
@@ -24,7 +26,9 @@
import android.appwidget.AppWidgetManagerInternal;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
@@ -33,11 +37,13 @@
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
import libcore.util.EmptyArray;
@@ -666,13 +672,66 @@
/**
* Identifies whether the accessibility service is true and designed for accessibility. An
- * accessibility service is considered as accessibility category if
- * {@link AccessibilityServiceInfo#isAccessibilityTool} is true.
+ * accessibility service is considered as accessibility category if meets all conditions below:
+ * <ul>
+ * <li> {@link AccessibilityServiceInfo#isAccessibilityTool} is true</li>
+ * <li> is installed from the trusted install source</li>
+ * </ul>
*
* @param serviceInfo The accessibility service's serviceInfo.
* @return Returns true if it is a true accessibility service.
*/
public boolean isA11yCategoryService(AccessibilityServiceInfo serviceInfo) {
- return serviceInfo.isAccessibilityTool();
+ if (!serviceInfo.isAccessibilityTool()) {
+ return false;
+ }
+ if (!serviceInfo.getResolveInfo().serviceInfo.applicationInfo.isSystemApp()) {
+ return hasTrustedSystemInstallSource(
+ serviceInfo.getResolveInfo().serviceInfo.packageName);
+ }
+ return true;
+ }
+
+ /** Returns true if the {@code installedPackage} is installed from the trusted install source.
+ */
+ private boolean hasTrustedSystemInstallSource(String installedPackage) {
+ try {
+ InstallSourceInfo installSourceInfo = mPackageManager.getInstallSourceInfo(
+ installedPackage);
+ if (installSourceInfo == null) {
+ return false;
+ }
+ final String installSourcePackageName = installSourceInfo.getInitiatingPackageName();
+ if (installSourcePackageName == null || !mPackageManager.getPackageInfo(
+ installSourcePackageName,
+ 0).applicationInfo.isSystemApp()) {
+ return false;
+ }
+ return isTrustedInstallSource(installSourcePackageName);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(LOG_TAG, "can't find the package's install source:" + installedPackage);
+ }
+ return false;
+ }
+
+ /** Returns true if the {@code installerPackage} is a trusted install source. */
+ private boolean isTrustedInstallSource(String installerPackage) {
+ final String[] allowedInstallingSources = mContext.getResources().getStringArray(
+ com.android.internal.R.array
+ .config_accessibility_allowed_install_source);
+
+ if (allowedInstallingSources.length == 0) {
+ //Filters unwanted default installers if no allowed install sources.
+ String defaultInstaller = ArrayUtils.firstOrNull(LocalServices.getService(
+ PackageManagerInternal.class).getKnownPackageNames(PACKAGE_INSTALLER,
+ mCurrentUserId));
+ return !TextUtils.equals(defaultInstaller, installerPackage);
+ }
+ for (int i = 0; i < allowedInstallingSources.length; i++) {
+ if (TextUtils.equals(allowedInstallingSources[i], installerPackage)) {
+ return true;
+ }
+ }
+ return false;
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillUriGrantsManager.java b/services/autofill/java/com/android/server/autofill/AutofillUriGrantsManager.java
index 801be5e..51e023d 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillUriGrantsManager.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillUriGrantsManager.java
@@ -20,6 +20,8 @@
import static com.android.server.autofill.Helper.sVerbose;
+import static java.lang.Integer.toHexString;
+
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.IUriGrantsManager;
@@ -33,32 +35,16 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Pair;
import android.util.Slog;
-import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
-import com.android.server.uri.UriGrantsManagerInternal;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
+import com.android.server.wm.ActivityTaskManagerInternal;
/**
- * Grants and revokes URI permissions for content-based autofill suggestions.
+ * Grants URI permissions for content-based autofill suggestions.
*
- * <p>Note that the system cannot just hand out grants directly; it must always do so on behalf of
- * an owner (see {@link com.android.server.uri.UriGrantsManagerService}). For autofill, the owner
- * is the autofill service provider that creates a given autofill suggestion containing a content
- * URI. Therefore, this manager class must be instantiated with the service uid of the provider for
- * which it will manage URI grants.
- *
- * <p>To dump the state of this class, use {@code adb shell dumpsys autofill}.
+ * <p>URI permissions granted by this class are tied to the activity being filled. When the
+ * activity finishes, its URI grants are automatically revoked.
*
* <p>To dump all active URI permissions, use {@code adb shell dumpsys activity permissions}.
*/
@@ -69,26 +55,10 @@
@UserIdInt
private final int mSourceUserId;
@NonNull
- private final IBinder mPermissionOwner;
- @NonNull
- private final UriGrantsManagerInternal mUgmInternal;
+ private final ActivityTaskManagerInternal mActivityTaskMgrInternal;
@NonNull
private final IUriGrantsManager mUgm;
- // We use a local lock here for simplicity, since the synchronized code does not depend on
- // any other resources (the "hold and wait" condition required for deadlock is not present).
- // If this changes in the future, instead of using a local lock this should be updated to
- // use the shared lock from AutofillManagerServiceImpl.
- @NonNull
- private final Object mLock;
-
- // Tracks the URIs that have been granted to each package. For each URI, the map stores the
- // activities that triggered the grant. This allows revoking permissions only once all
- // activities that triggered the grant are finished.
- @NonNull
- @GuardedBy("mLock")
- private final ArrayMap<String, List<Pair<Uri, String>>> mActiveGrantsByPackage;
-
/**
* Creates a new instance of the manager.
*
@@ -99,159 +69,60 @@
AutofillUriGrantsManager(int serviceUid) {
mSourceUid = serviceUid;
mSourceUserId = UserHandle.getUserId(mSourceUid);
- mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
- mPermissionOwner = mUgmInternal.newUriPermissionOwner("autofill-" + serviceUid);
+ mActivityTaskMgrInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mUgm = UriGrantsManager.getService();
- mLock = new Object();
- mActiveGrantsByPackage = new ArrayMap<>(0);
}
public void grantUriPermissions(@NonNull ComponentName targetActivity,
- @UserIdInt int targetUserId, @NonNull ClipData clip) {
- String targetPkg = targetActivity.getPackageName();
+ @NonNull IBinder targetActivityToken, @UserIdInt int targetUserId,
+ @NonNull ClipData clip) {
+ final String targetPkg = targetActivity.getPackageName();
+ final IBinder permissionOwner =
+ mActivityTaskMgrInternal.getUriPermissionOwnerForActivity(targetActivityToken);
+ if (permissionOwner == null) {
+ Slog.w(TAG, "Can't grant URI permissions, because the target activity token is invalid:"
+ + " clip=" + clip
+ + ", targetActivity=" + targetActivity + ", targetUserId=" + targetUserId
+ + ", targetActivityToken=" + toHexString(targetActivityToken.hashCode()));
+ return;
+ }
for (int i = 0; i < clip.getItemCount(); i++) {
ClipData.Item item = clip.getItemAt(i);
Uri uri = item.getUri();
if (uri == null || !SCHEME_CONTENT.equals(uri.getScheme())) {
continue;
}
- if (grantUriPermissions(targetPkg, targetUserId, uri)) {
- addToActiveGrants(uri, targetActivity);
- }
+ grantUriPermissions(uri, targetPkg, targetUserId, permissionOwner);
}
}
- public void revokeUriPermissions(@NonNull ComponentName targetActivity,
- @UserIdInt int targetUserId) {
- String targetPkg = targetActivity.getPackageName();
- Set<Uri> urisWhoseGrantsShouldBeRevoked = removeFromActiveGrants(targetActivity);
- for (Uri uri : urisWhoseGrantsShouldBeRevoked) {
- revokeUriPermissions(targetPkg, targetUserId, uri);
- }
- }
-
- private boolean grantUriPermissions(@NonNull String targetPkg, @UserIdInt int targetUserId,
- @NonNull Uri uri) {
+ private void grantUriPermissions(@NonNull Uri uri, @NonNull String targetPkg,
+ @UserIdInt int targetUserId, @NonNull IBinder permissionOwner) {
final int sourceUserId = ContentProvider.getUserIdFromUri(uri, mSourceUserId);
if (sVerbose) {
Slog.v(TAG, "Granting URI permissions: uri=" + uri
+ ", sourceUid=" + mSourceUid + ", sourceUserId=" + sourceUserId
- + ", targetPkg=" + targetPkg + ", targetUserId=" + targetUserId);
+ + ", targetPkg=" + targetPkg + ", targetUserId=" + targetUserId
+ + ", permissionOwner=" + toHexString(permissionOwner.hashCode()));
}
final Uri uriWithoutUserId = ContentProvider.getUriWithoutUserId(uri);
final long ident = Binder.clearCallingIdentity();
try {
mUgm.grantUriPermissionFromOwner(
- mPermissionOwner,
+ permissionOwner,
mSourceUid,
targetPkg,
uriWithoutUserId,
Intent.FLAG_GRANT_READ_URI_PERMISSION,
sourceUserId,
targetUserId);
- return true;
} catch (RemoteException e) {
Slog.e(TAG, "Granting URI permissions failed: uri=" + uri
+ ", sourceUid=" + mSourceUid + ", sourceUserId=" + sourceUserId
- + ", targetPkg=" + targetPkg + ", targetUserId=" + targetUserId, e);
- return false;
+ + ", targetPkg=" + targetPkg + ", targetUserId=" + targetUserId
+ + ", permissionOwner=" + toHexString(permissionOwner.hashCode()), e);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
-
- private void revokeUriPermissions(@NonNull String targetPkg, @UserIdInt int targetUserId,
- @NonNull Uri uri) {
- final int sourceUserId = ContentProvider.getUserIdFromUri(uri, mSourceUserId);
- if (sVerbose) {
- Slog.v(TAG, "Revoking URI permissions: uri=" + uri
- + ", sourceUid=" + mSourceUid + ", sourceUserId=" + sourceUserId
- + ", target=" + targetPkg + ", targetUserId=" + targetUserId);
- }
- final Uri uriWithoutUserId = ContentProvider.getUriWithoutUserId(uri);
- final long ident = Binder.clearCallingIdentity();
- try {
- mUgmInternal.revokeUriPermissionFromOwner(
- mPermissionOwner,
- uriWithoutUserId,
- Intent.FLAG_GRANT_READ_URI_PERMISSION,
- sourceUserId,
- targetPkg,
- targetUserId);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- private void addToActiveGrants(@NonNull Uri uri, @NonNull ComponentName targetActivity) {
- synchronized (mLock) {
- String packageName = targetActivity.getPackageName();
- List<Pair<Uri, String>> uris = mActiveGrantsByPackage.computeIfAbsent(packageName,
- k -> new ArrayList<>(1));
- uris.add(Pair.create(uri, targetActivity.getClassName()));
- }
- }
-
- private Set<Uri> removeFromActiveGrants(@NonNull ComponentName targetActivity) {
- synchronized (mLock) {
- String targetPackageName = targetActivity.getPackageName();
- List<Pair<Uri, String>> uris = mActiveGrantsByPackage.get(targetPackageName);
- if (uris == null || uris.isEmpty()) {
- return Collections.emptySet();
- }
-
- // Collect all URIs whose grant was triggered by the target activity.
- String targetActivityClassName = targetActivity.getClassName();
- Set<Uri> urisWhoseGrantsShouldBeRevoked = new ArraySet<>(1);
- for (Iterator<Pair<Uri, String>> iter = uris.iterator(); iter.hasNext(); ) {
- Pair<Uri, String> uriAndActivity = iter.next();
- if (uriAndActivity.second.equals(targetActivityClassName)) {
- urisWhoseGrantsShouldBeRevoked.add(uriAndActivity.first);
- iter.remove();
- }
- }
-
- // A URI grant may have been triggered by more than one activity for the same package.
- // We should not revoke a grant if it was triggered by multiple activities and one or
- // more of those activities is still alive. Therefore we do a second pass and prune
- // the set of URIs to be revoked if an additional activity that triggered its grant
- // is still present.
- for (Pair<Uri, String> uriAndActivity : uris) {
- urisWhoseGrantsShouldBeRevoked.remove(uriAndActivity.first);
- }
-
- // If there are no remaining URIs granted to the package, drop the entry from the map.
- if (uris.isEmpty()) {
- mActiveGrantsByPackage.remove(targetPackageName);
- }
- return urisWhoseGrantsShouldBeRevoked;
- }
- }
-
- /**
- * Dump the active URI grants.
- */
- public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
- synchronized (mLock) {
- if (mActiveGrantsByPackage.isEmpty()) {
- pw.print(prefix); pw.println("URI grants: none");
- return;
- }
- pw.print(prefix); pw.println("URI grants:");
- final String prefix2 = prefix + " ";
- final String prefix3 = prefix2 + " ";
- for (int i = mActiveGrantsByPackage.size() - 1; i >= 0; i--) {
- String packageName = mActiveGrantsByPackage.keyAt(i);
- pw.print(prefix2); pw.println(packageName);
- List<Pair<Uri, String>> uris = mActiveGrantsByPackage.valueAt(i);
- if (uris == null || uris.isEmpty()) {
- continue;
- }
- for (Pair<Uri, String> uriAndActivity : uris) {
- pw.print(prefix3);
- pw.println(uriAndActivity.first + ": " + uriAndActivity.second);
- }
- }
- }
- }
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index db5bc4d..8525e36 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -57,7 +57,6 @@
import com.android.internal.os.IResultReceiver;
import com.android.server.autofill.ui.InlineFillUi;
-import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CancellationException;
@@ -152,8 +151,8 @@
* Called by {@link Session} to request augmented autofill.
*/
public void onRequestAutofillLocked(int sessionId, @NonNull IAutoFillManagerClient client,
- int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId,
- @Nullable AutofillValue focusedValue,
+ int taskId, @NonNull ComponentName activityComponent, @NonNull IBinder activityToken,
+ @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue,
@Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
@Nullable Function<InlineFillUi, Boolean> inlineSuggestionsCallback,
@NonNull Runnable onErrorCallback,
@@ -181,7 +180,8 @@
inlineSuggestionsRequest, inlineSuggestionsData,
clientState, focusedId, focusedValue,
inlineSuggestionsCallback, client, onErrorCallback,
- remoteRenderService, userId, activityComponent);
+ remoteRenderService, userId,
+ activityComponent, activityToken);
if (!showingFillWindow) {
requestAutofill.complete(null);
}
@@ -253,7 +253,7 @@
@NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback,
@Nullable RemoteInlineSuggestionRenderService remoteRenderService,
int userId,
- @NonNull ComponentName targetActivity) {
+ @NonNull ComponentName targetActivity, @NonNull IBinder targetActivityToken) {
if (inlineSuggestionsData == null || inlineSuggestionsData.isEmpty()
|| inlineSuggestionsCallback == null || request == null
|| remoteRenderService == null) {
@@ -307,8 +307,8 @@
final ArrayList<AutofillId> fieldIds = dataset.getFieldIds();
final ClipData content = dataset.getFieldContent();
if (content != null) {
- mUriGrantsManager.grantUriPermissions(
- targetActivity, userId, content);
+ mUriGrantsManager.grantUriPermissions(targetActivity,
+ targetActivityToken, userId, content);
final AutofillId fieldId = fieldIds.get(0);
if (sDebug) {
Slog.d(TAG, "Calling client autofillContent(): "
@@ -368,12 +368,6 @@
+ ComponentName.flattenToShortString(mComponentName) + "]";
}
- @Override
- public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
- super.dump(prefix, pw);
- mUriGrantsManager.dump(prefix, pw);
- }
-
/**
* Called by {@link Session} when it's time to destroy all augmented autofill requests.
*/
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index f1dcdff..042631d 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1689,7 +1689,7 @@
if (content != null) {
final AutofillUriGrantsManager autofillUgm =
remoteAugmentedAutofillService.getAutofillUriGrantsManager();
- autofillUgm.grantUriPermissions(mComponentName, userId, content);
+ autofillUgm.grantUriPermissions(mComponentName, mActivityToken, userId, content);
}
// Fill the value into the field.
@@ -3537,7 +3537,8 @@
synchronized (mLock) {
logAugmentedAutofillRequestLocked(mode, remoteService.getComponentName(),
focusedId, isWhitelisted, inlineSuggestionsRequest != null);
- remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName,
+ remoteService.onRequestAutofillLocked(id, mClient,
+ taskId, mComponentName, mActivityToken,
AutofillId.withoutSession(focusedId), currentValue,
inlineSuggestionsRequest, inlineSuggestionsResponseCallback,
/*onErrorCallback=*/ () -> {
@@ -4167,13 +4168,6 @@
if (remoteRenderService != null) {
remoteRenderService.destroySuggestionViews(userId, id);
}
- final RemoteAugmentedAutofillService remoteAugmentedAutofillService =
- mService.getRemoteAugmentedAutofillServiceIfCreatedLocked();
- if (remoteAugmentedAutofillService != null) {
- final AutofillUriGrantsManager autofillUgm =
- remoteAugmentedAutofillService.getAutofillUriGrantsManager();
- autofillUgm.revokeUriPermissions(mComponentName, userId);
- }
mDestroyed = true;
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 78610a2..0bd331b 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -19,6 +19,15 @@
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_COLLECT_LATENCY_DATA_KEY;
+import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_DETAILED_TRACKING_KEY;
+import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_ENABLED_KEY;
+import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_IGNORE_BATTERY_STATUS_KEY;
+import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_MAX_CALL_STATS_KEY;
+import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_SAMPLING_INTERVAL_KEY;
+import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_TRACK_DIRECT_CALLING_UID_KEY;
+import static com.android.internal.os.BinderCallsStats.SettingsObserver.SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY;
+
import android.app.ActivityThread;
import android.content.Context;
import android.content.pm.PackageInfo;
@@ -43,7 +52,6 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderInternal;
-import com.android.internal.os.BinderLatencyObserver;
import com.android.internal.os.CachedDeviceState;
import com.android.internal.util.DumpUtils;
@@ -125,28 +133,6 @@
/** Listens for flag changes. */
private static class SettingsObserver extends ContentObserver {
- // Settings for BinderCallsStats.
- private static final String SETTINGS_ENABLED_KEY = "enabled";
- private static final String SETTINGS_DETAILED_TRACKING_KEY = "detailed_tracking";
- private static final String SETTINGS_UPLOAD_DATA_KEY = "upload_data";
- private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
- private static final String SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY = "track_screen_state";
- private static final String SETTINGS_TRACK_DIRECT_CALLING_UID_KEY = "track_calling_uid";
- private static final String SETTINGS_MAX_CALL_STATS_KEY = "max_call_stats_count";
- private static final String SETTINGS_IGNORE_BATTERY_STATUS_KEY = "ignore_battery_status";
- // Settings for BinderLatencyObserver.
- private static final String SETTINGS_COLLECT_LATENCY_DATA_KEY = "collect_Latency_data";
- private static final String SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY =
- "latency_observer_sampling_interval";
- private static final String SETTINGS_LATENCY_OBSERVER_PUSH_INTERVAL_MINUTES_KEY =
- "latency_observer_push_interval_minutes";
- private static final String SETTINGS_LATENCY_HISTOGRAM_BUCKET_COUNT_KEY =
- "latency_histogram_bucket_count";
- private static final String SETTINGS_LATENCY_HISTOGRAM_FIRST_BUCKET_SIZE_KEY =
- "latency_histogram_first_bucket_size";
- private static final String SETTINGS_LATENCY_HISTOGRAM_BUCKET_SCALE_FACTOR_KEY =
- "latency_histogram_bucket_scale_factor";
-
private boolean mEnabled;
private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS);
private final Context mContext;
@@ -206,23 +192,9 @@
mParser.getBoolean(SETTINGS_COLLECT_LATENCY_DATA_KEY,
BinderCallsStats.DEFAULT_COLLECT_LATENCY_DATA));
// Binder latency observer settings.
- BinderLatencyObserver binderLatencyObserver = mBinderCallsStats.getLatencyObserver();
- binderLatencyObserver.setSamplingInterval(mParser.getInt(
- SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY,
- BinderLatencyObserver.PERIODIC_SAMPLING_INTERVAL_DEFAULT));
- binderLatencyObserver.setHistogramBucketsParams(
- mParser.getInt(
- SETTINGS_LATENCY_HISTOGRAM_BUCKET_COUNT_KEY,
- BinderLatencyObserver.BUCKET_COUNT_DEFAULT),
- mParser.getInt(
- SETTINGS_LATENCY_HISTOGRAM_FIRST_BUCKET_SIZE_KEY,
- BinderLatencyObserver.FIRST_BUCKET_SIZE_DEFAULT),
- mParser.getFloat(
- SETTINGS_LATENCY_HISTOGRAM_BUCKET_SCALE_FACTOR_KEY,
- BinderLatencyObserver.BUCKET_SCALE_FACTOR_DEFAULT));
- binderLatencyObserver.setPushInterval(mParser.getInt(
- SETTINGS_LATENCY_OBSERVER_PUSH_INTERVAL_MINUTES_KEY,
- BinderLatencyObserver.STATSD_PUSH_INTERVAL_MINUTES_DEFAULT));
+ BinderCallsStats.SettingsObserver.configureLatencyObserver(
+ mParser,
+ mBinderCallsStats.getLatencyObserver());
final boolean enabled =
mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 6fa8ecd4..f2762fc 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -3382,7 +3382,7 @@
pw.println(" Sets the inactive state of an app.");
pw.println(" get-inactive [--user <USER_ID>] <PACKAGE>");
pw.println(" Returns the inactive state of an app.");
- pw.println(" set-standby-bucket [--user <USER_ID>] <PACKAGE> active|working_set|frequent|rare");
+ pw.println(" set-standby-bucket [--user <USER_ID>] <PACKAGE> active|working_set|frequent|rare|restricted");
pw.println(" Puts an app in the standby bucket.");
pw.println(" get-standby-bucket [--user <USER_ID>] <PACKAGE>");
pw.println(" Returns the standby bucket of an app.");
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 859cc44..406e866 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -663,7 +663,7 @@
final long gnssChargeUC = measuredEnergyDeltas.gnssChargeUC;
if (gnssChargeUC != MeasuredEnergySnapshot.UNAVAILABLE) {
- mStats.updateGnssMeasuredEnergyStatsLocked(displayChargeUC, elapsedRealtime);
+ mStats.updateGnssMeasuredEnergyStatsLocked(gnssChargeUC, elapsedRealtime);
}
}
// Inform mStats about each applicable custom energy bucket.
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index efb0f4a..52388ff 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -33,6 +33,11 @@
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.IActivityManager;
+import android.app.StatsManager;
+import android.app.StatsManager.StatsPullAtomCallback;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
import android.apphibernation.IAppHibernationService;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -43,6 +48,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.Environment;
import android.os.RemoteException;
@@ -59,10 +65,12 @@
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.StatsEvent;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -148,8 +156,8 @@
intentFilter.addAction(ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
userAllContext.registerReceiver(mBroadcastReceiver, intentFilter);
-
LocalServices.addService(AppHibernationManagerInternal.class, mLocalService);
+ mInjector.getUsageStatsManagerInternal().registerListener(mUsageEventListener);
}
@Override
@@ -174,6 +182,12 @@
NAMESPACE_APP_HIBERNATION,
ActivityThread.currentApplication().getMainExecutor(),
this::onDeviceConfigChanged);
+ getContext().getSystemService(StatsManager.class)
+ .setPullAtomCallback(
+ FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS,
+ /* metadata */ null, // use default PullAtomMetadata values
+ mBackgroundExecutor,
+ new StatsPullAtomCallbackImpl());
}
}
@@ -269,6 +283,16 @@
} else {
unhibernatePackageForUser(packageName, userId, pkgState);
}
+ final UserLevelState stateSnapshot = new UserLevelState(pkgState);
+ final int userIdSnapshot = userId;
+ mBackgroundExecutor.execute(() -> {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.USER_LEVEL_HIBERNATION_STATE_CHANGED,
+ stateSnapshot.packageName,
+ userIdSnapshot,
+ stateSnapshot.hibernated,
+ stateSnapshot.lastUnhibernatedMs);
+ });
List<UserLevelState> states = new ArrayList<>(mUserStates.get(userId).values());
mUserDiskStores.get(userId).scheduleWriteHibernationStates(states);
}
@@ -785,6 +809,20 @@
}
};
+ private final UsageEventListener mUsageEventListener = (userId, event) -> {
+ if (!isAppHibernationEnabled()) {
+ return;
+ }
+ final int eventType = event.mEventType;
+ if (eventType == UsageEvents.Event.USER_INTERACTION
+ || eventType == UsageEvents.Event.ACTIVITY_RESUMED
+ || eventType == UsageEvents.Event.APP_COMPONENT_USED) {
+ final String pkgName = event.mPackage;
+ setHibernatingForUser(pkgName, userId, false);
+ setHibernatingGlobally(pkgName, false);
+ }
+ };
+
/**
* Whether app hibernation is enabled on this device.
*
@@ -817,6 +855,8 @@
Executor getBackgroundExecutor();
+ UsageStatsManagerInternal getUsageStatsManagerInternal();
+
HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore();
HibernationStateDiskStore<UserLevelState> getUserLevelDiskStore(int userId);
@@ -867,6 +907,11 @@
}
@Override
+ public UsageStatsManagerInternal getUsageStatsManagerInternal() {
+ return LocalServices.getService(UsageStatsManagerInternal.class);
+ }
+
+ @Override
public HibernationStateDiskStore<GlobalLevelState> getGlobalLevelDiskStore() {
File dir = new File(Environment.getDataSystemDirectory(), HIBERNATION_DIR_NAME);
return new HibernationStateDiskStore<>(
@@ -886,4 +931,29 @@
com.android.internal.R.bool.config_hibernationDeletesOatArtifactsEnabled);
}
}
+
+ private final class StatsPullAtomCallbackImpl implements StatsPullAtomCallback {
+ @Override
+ public int onPullAtom(int atomTag, @NonNull List<StatsEvent> data) {
+ if (atomTag != FrameworkStatsLog.USER_LEVEL_HIBERNATED_APPS) {
+ return StatsManager.PULL_SKIP;
+ }
+ if (isAppHibernationEnabled()) {
+ List<UserInfo> userInfos = mUserManager.getAliveUsers();
+ final int numUsers = userInfos.size();
+ for (int i = 0; i < numUsers; ++i) {
+ final int userId = userInfos.get(i).id;
+ if (mUserManager.isUserUnlockingOrUnlocked(userId)) {
+ data.add(
+ FrameworkStatsLog.buildStatsEvent(
+ atomTag,
+ getHibernatingPackagesForUser(userId).size(),
+ userId)
+ );
+ }
+ }
+ }
+ return StatsManager.PULL_SUCCESS;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/apphibernation/UserLevelState.java b/services/core/java/com/android/server/apphibernation/UserLevelState.java
index b75b19d..68c363c 100644
--- a/services/core/java/com/android/server/apphibernation/UserLevelState.java
+++ b/services/core/java/com/android/server/apphibernation/UserLevelState.java
@@ -31,6 +31,14 @@
@CurrentTimeMillisLong
public long lastUnhibernatedMs;
+ UserLevelState() {}
+
+ UserLevelState(UserLevelState state) {
+ packageName = state.packageName;
+ hibernated = state.hibernated;
+ lastUnhibernatedMs = state.lastUnhibernatedMs;
+ }
+
@Override
public String toString() {
return "UserLevelState{"
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index f2e422d..63b41b7 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -92,8 +92,8 @@
"discrete_history_quantization_millis";
private static final String PROPERTY_DISCRETE_FLAGS = "discrete_history_op_flags";
private static final String PROPERTY_DISCRETE_OPS_LIST = "discrete_history_ops_cslist";
- private static final String DEFAULT_DISCRETE_OPS = OP_CAMERA + "," + OP_RECORD_AUDIO + ","
- + OP_FINE_LOCATION + "," + OP_COARSE_LOCATION;
+ private static final String DEFAULT_DISCRETE_OPS = OP_FINE_LOCATION + "," + OP_COARSE_LOCATION
+ + "," + OP_CAMERA + "," + OP_RECORD_AUDIO;
private static final long DEFAULT_DISCRETE_HISTORY_CUTOFF = Duration.ofHours(24).toMillis();
private static final long MAXIMUM_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(30).toMillis();
private static final long DEFAULT_DISCRETE_HISTORY_QUANTIZATION =
@@ -104,7 +104,6 @@
private static int[] sDiscreteOps;
private static int sDiscreteFlags;
-
private static final String TAG_HISTORY = "h";
private static final String ATTR_VERSION = "v";
private static final int CURRENT_VERSION = 1;
@@ -144,6 +143,8 @@
@GuardedBy("mOnDiskLock")
private DiscreteOps mCachedOps = null;
+ private boolean mDebugMode = false;
+
DiscreteRegistry(Object inMemoryLock) {
mInMemoryLock = inMemoryLock;
}
@@ -159,40 +160,35 @@
AsyncTask.THREAD_POOL_EXECUTOR, (DeviceConfig.Properties p) -> {
setDiscreteHistoryParameters(p);
});
- sDiscreteHistoryCutoff = DeviceConfig.getLong(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_DISCRETE_HISTORY_CUTOFF, DEFAULT_DISCRETE_HISTORY_CUTOFF);
- sDiscreteHistoryQuantization = DeviceConfig.getLong(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_DISCRETE_HISTORY_QUANTIZATION, DEFAULT_DISCRETE_HISTORY_QUANTIZATION);
- sDiscreteFlags = DeviceConfig.getInt(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_DISCRETE_FLAGS, OP_FLAGS_DISCRETE);
- sDiscreteOps = parseOpsList(DeviceConfig.getString(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_DISCRETE_OPS_LIST, DEFAULT_DISCRETE_OPS));
+ setDiscreteHistoryParameters(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_PRIVACY));
}
private void setDiscreteHistoryParameters(DeviceConfig.Properties p) {
if (p.getKeyset().contains(PROPERTY_DISCRETE_HISTORY_CUTOFF)) {
sDiscreteHistoryCutoff = p.getLong(PROPERTY_DISCRETE_HISTORY_CUTOFF,
DEFAULT_DISCRETE_HISTORY_CUTOFF);
- if (!Build.IS_DEBUGGABLE) {
+ if (!Build.IS_DEBUGGABLE && !mDebugMode) {
sDiscreteHistoryCutoff = min(MAXIMUM_DISCRETE_HISTORY_CUTOFF,
sDiscreteHistoryCutoff);
}
+ } else {
+ sDiscreteHistoryCutoff = DEFAULT_DISCRETE_HISTORY_CUTOFF;
}
if (p.getKeyset().contains(PROPERTY_DISCRETE_HISTORY_QUANTIZATION)) {
sDiscreteHistoryQuantization = p.getLong(PROPERTY_DISCRETE_HISTORY_QUANTIZATION,
DEFAULT_DISCRETE_HISTORY_QUANTIZATION);
- if (!Build.IS_DEBUGGABLE) {
+ if (!Build.IS_DEBUGGABLE && !mDebugMode) {
sDiscreteHistoryQuantization = max(DEFAULT_DISCRETE_HISTORY_QUANTIZATION,
sDiscreteHistoryQuantization);
}
+ } else {
+ sDiscreteHistoryQuantization = DEFAULT_DISCRETE_HISTORY_QUANTIZATION;
}
- if (p.getKeyset().contains(PROPERTY_DISCRETE_FLAGS)) {
- sDiscreteFlags = p.getInt(PROPERTY_DISCRETE_FLAGS, OP_FLAGS_DISCRETE);
- }
- if (p.getKeyset().contains(PROPERTY_DISCRETE_OPS_LIST)) {
- sDiscreteOps = parseOpsList(p.getString(PROPERTY_DISCRETE_OPS_LIST,
- DEFAULT_DISCRETE_OPS));
- }
+ sDiscreteFlags = p.getKeyset().contains(PROPERTY_DISCRETE_FLAGS) ? sDiscreteFlags =
+ p.getInt(PROPERTY_DISCRETE_FLAGS, OP_FLAGS_DISCRETE) : OP_FLAGS_DISCRETE;
+ sDiscreteOps = p.getKeyset().contains(PROPERTY_DISCRETE_OPS_LIST) ? parseOpsList(
+ p.getString(PROPERTY_DISCRETE_OPS_LIST, DEFAULT_DISCRETE_OPS)) : parseOpsList(
+ DEFAULT_DISCRETE_OPS);
}
void recordDiscreteAccess(int uid, String packageName, int op, @Nullable String attributionTag,
@@ -232,6 +228,8 @@
@Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
@Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
DiscreteOps discreteOps = getAllDiscreteOps();
+ beginTimeMillis = max(beginTimeMillis, Instant.now().minus(sDiscreteHistoryCutoff,
+ ChronoUnit.MILLIS).toEpochMilli());
discreteOps.filter(beginTimeMillis, endTimeMillis, filter, uidFilter, packageNameFilter,
opNamesFilter, attributionTagFilter, flagsFilter);
discreteOps.applyToHistoricalOps(result);
@@ -282,6 +280,18 @@
}
}
+ void offsetHistory(long offset) {
+ synchronized (mOnDiskLock) {
+ DiscreteOps discreteOps;
+ synchronized (mInMemoryLock) {
+ discreteOps = getAllDiscreteOps();
+ clearHistory();
+ }
+ discreteOps.offsetHistory(offset);
+ persistDiscreteOpsLocked(discreteOps);
+ }
+ }
+
void dump(@NonNull PrintWriter pw, int uidFilter, @Nullable String packageNameFilter,
@Nullable String attributionTagFilter,
@AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp,
@@ -363,6 +373,13 @@
}
}
+ private void offsetHistory(long offset) {
+ int nUids = mUids.size();
+ for (int i = 0; i < nUids; i++) {
+ mUids.valueAt(i).offsetHistory(offset);
+ }
+ }
+
private void clearHistory(int uid, String packageName) {
if (mUids.containsKey(uid)) {
mUids.get(uid).clearPackage(packageName);
@@ -549,6 +566,13 @@
}
}
+ private void offsetHistory(long offset) {
+ int nPackages = mPackages.size();
+ for (int i = 0; i < nPackages; i++) {
+ mPackages.valueAt(i).offsetHistory(offset);
+ }
+ }
+
private void clearPackage(String packageName) {
mPackages.remove(packageName);
}
@@ -656,6 +680,13 @@
}
}
+ private void offsetHistory(long offset) {
+ int nOps = mPackageOps.size();
+ for (int i = 0; i < nOps; i++) {
+ mPackageOps.valueAt(i).offsetHistory(offset);
+ }
+ }
+
private DiscreteOp getOrCreateDiscreteOp(int op) {
DiscreteOp result = mPackageOps.get(op);
if (result == null) {
@@ -749,12 +780,29 @@
}
}
+ private void offsetHistory(long offset) {
+ int nTags = mAttributedOps.size();
+ for (int i = 0; i < nTags; i++) {
+ List<DiscreteOpEvent> list = mAttributedOps.valueAt(i);
+
+ int n = list.size();
+ for (int j = 0; j < n; j++) {
+ DiscreteOpEvent event = list.get(j);
+ list.set(j, new DiscreteOpEvent(event.mNoteTime - offset, event.mNoteDuration,
+ event.mUidState, event.mOpFlag));
+ }
+ }
+ }
+
void addDiscreteAccess(@Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
long accessTime, long accessDuration) {
List<DiscreteOpEvent> attributedOps = getOrCreateDiscreteOpEventsList(
attributionTag);
accessTime = accessTime / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
+ accessDuration = accessDuration == -1 ? -1
+ : (accessDuration + sDiscreteHistoryQuantization - 1)
+ / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
int nAttributedOps = attributedOps.size();
int i = nAttributedOps;
@@ -764,8 +812,7 @@
break;
}
if (previousOp.mOpFlag == flags && previousOp.mUidState == uidState) {
- if (accessDuration != previousOp.mNoteDuration
- && accessDuration > sDiscreteHistoryQuantization) {
+ if (accessDuration != previousOp.mNoteDuration) {
break;
} else {
return;
@@ -983,5 +1030,9 @@
}
return true;
}
+
+ void setDebugMode(boolean debugMode) {
+ this.mDebugMode = debugMode;
+ }
}
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index ffd2458..72e582e 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -553,6 +553,11 @@
if (mMode == AppOpsManager.HISTORICAL_MODE_DISABLED) {
clearHistoryOnDiskDLocked();
}
+ if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_PASSIVE) {
+ mDiscreteRegistry.setDebugMode(true);
+ } else {
+ mDiscreteRegistry.setDebugMode(false);
+ }
}
if (mBaseSnapshotInterval != baseSnapshotInterval) {
mBaseSnapshotInterval = baseSnapshotInterval;
@@ -591,6 +596,7 @@
mPersistence.persistHistoricalOpsDLocked(history);
}
}
+ mDiscreteRegistry.offsetHistory(offsetMillis);
}
void addHistoricalOps(HistoricalOps ops) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 94d47aa..779558e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -632,7 +632,7 @@
for (String instance : instances) {
final String fqName = IFace.DESCRIPTOR + "/" + instance;
final IFace face = IFace.Stub.asInterface(
- ServiceManager.waitForDeclaredService(fqName));
+ Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName)));
if (face == null) {
Slog.e(TAG, "Unable to get declared service: " + fqName);
continue;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 23be50e..84d239e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -33,6 +33,7 @@
import android.hardware.face.Face;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
+import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -173,7 +174,9 @@
Slog.d(getTag(), "Daemon was null, reconnecting");
mDaemon = IFace.Stub.asInterface(
- ServiceManager.waitForDeclaredService(IFace.DESCRIPTOR + "/" + mHalInstanceName));
+ Binder.allowBlocking(
+ ServiceManager.waitForDeclaredService(
+ IFace.DESCRIPTOR + "/" + mHalInstanceName)));
if (mDaemon == null) {
Slog.e(getTag(), "Unable to get daemon");
return null;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 871b4f67..418b969 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -6089,10 +6089,8 @@
@BinderThread
@Override
- public void updateStatusIcon(String packageName, @DrawableRes int iconId,
- IVoidResultCallback resultCallback) {
- CallbackUtils.onResult(resultCallback,
- () -> mImms.updateStatusIcon(mToken, packageName, iconId));
+ public void updateStatusIconAsync(String packageName, @DrawableRes int iconId) {
+ mImms.updateStatusIcon(mToken, packageName, iconId);
}
@BinderThread
@@ -6119,16 +6117,14 @@
@BinderThread
@Override
- public void notifyUserAction(IVoidResultCallback resultCallback) {
- CallbackUtils.onResult(resultCallback, () -> mImms.notifyUserAction(mToken));
+ public void notifyUserActionAsync() {
+ mImms.notifyUserAction(mToken);
}
@BinderThread
@Override
- public void applyImeVisibility(IBinder windowToken, boolean setVisible,
- IVoidResultCallback resultCallback) {
- CallbackUtils.onResult(resultCallback,
- () -> mImms.applyImeVisibility(mToken, windowToken, setVisible));
+ public void applyImeVisibilityAsync(IBinder windowToken, boolean setVisible) {
+ mImms.applyImeVisibility(mToken, windowToken, setVisible);
}
}
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ad5be07..5b03989 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1311,7 +1311,7 @@
private void unlockKeystore(byte[] password, int userHandle) {
if (DEBUG) Slog.v(TAG, "Unlock keystore for user: " + userHandle);
- Authorization.onLockScreenEvent(false, userHandle, password);
+ Authorization.onLockScreenEvent(false, userHandle, password, null);
}
@VisibleForTesting /** Note: this method is overridden in unit tests */
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 90694d0..3f2b8ff 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -31,6 +31,7 @@
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Handler;
import android.os.SystemClock;
@@ -224,6 +225,12 @@
}
public boolean serverBasedResumeOnReboot() {
+ // Always use the server based RoR if the HAL isn't installed on device.
+ if (!mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_REBOOT_ESCROW)) {
+ return true;
+ }
+
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
"server_based_ror_enabled", false);
}
@@ -374,6 +381,7 @@
try {
escrowKey = getAndClearRebootEscrowKey(kk);
} catch (IOException e) {
+ Slog.i(TAG, "Failed to load escrow key, scheduling retry.", e);
scheduleLoadRebootEscrowDataOrFail(retryHandler, attemptNumber + 1, users,
rebootEscrowUsers);
return;
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index e7c0a50..431b009 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -382,8 +382,8 @@
// Migrate data usage over a VPN to the TUN network.
for (UnderlyingNetworkInfo info : vpnArray) {
- delta.migrateTun(info.getOwnerUid(), info.getIface(),
- info.getUnderlyingIfaces());
+ delta.migrateTun(info.getOwnerUid(), info.getInterface(),
+ info.getUnderlyingInterfaces());
// Filter out debug entries as that may lead to over counting.
delta.filterDebugEntries();
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8d6c145..9a3fe82 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4883,6 +4883,11 @@
return super.filterAppAccess(packageName, callingUid, userId);
}
}
+ public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
+ synchronized (mLock) {
+ super.dump(type, fd, pw, dumpState);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 678f046..7f18c4b 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -168,7 +168,7 @@
static final boolean DEBUG = false; // STOPSHIP if true
static final boolean DEBUG_LOAD = false; // STOPSHIP if true
static final boolean DEBUG_PROCSTATE = false; // STOPSHIP if true
- static final boolean DEBUG_REBOOT = true;
+ static final boolean DEBUG_REBOOT = false; // STOPSHIP if true
@VisibleForTesting
static final long DEFAULT_RESET_INTERVAL_SEC = 24 * 60 * 60; // 1 day
@@ -2241,6 +2241,8 @@
packageShortcutsChanged(packageName, userId, changedShortcuts, removedShortcuts);
+ reportShortcutUsedInternal(packageName, shortcut.getId(), userId);
+
verifyStates();
ret.complete(null);
@@ -2851,12 +2853,7 @@
}
}
- final long token = injectClearCallingIdentity();
- try {
- mUsageStatsManagerInternal.reportShortcutUsage(packageName, shortcutId, userId);
- } finally {
- injectRestoreCallingIdentity(token);
- }
+ reportShortcutUsedInternal(packageName, shortcutId, userId);
ret.complete(true);
} catch (Exception e) {
ret.completeExceptionally(e);
@@ -2865,6 +2862,15 @@
return ret;
}
+ private void reportShortcutUsedInternal(String packageName, String shortcutId, int userId) {
+ final long token = injectClearCallingIdentity();
+ try {
+ mUsageStatsManagerInternal.reportShortcutUsage(packageName, shortcutId, userId);
+ } finally {
+ injectRestoreCallingIdentity(token);
+ }
+ }
+
@Override
public boolean isRequestPinItemSupported(int callingUserId, int requestType) {
final long token = injectClearCallingIdentity();
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index 622b758..3a097a7 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -280,14 +280,15 @@
private static void updateAllowListedTagsForPackageLocked(int uid, String packageName,
Set<String> allowListedTags, ConcurrentHashMap<Integer, ArrayMap<String,
ArraySet<String>>> datastore) {
+ final int appId = UserHandle.getAppId(uid);
// 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);
+ ArrayMap<String, ArraySet<String>> appIdTags = datastore.get(appId);
+ if (appIdTags != null) {
+ appIdTags = new ArrayMap<>(appIdTags);
}
- ArraySet<String> packageTags = (uidTags != null) ? uidTags.get(packageName) : null;
+ ArraySet<String> packageTags = (appIdTags != null) ? appIdTags.get(packageName) : null;
if (packageTags != null) {
packageTags = new ArraySet<>(packageTags);
}
@@ -299,17 +300,17 @@
} else {
packageTags = new ArraySet<>(allowListedTags);
}
- if (uidTags == null) {
- uidTags = new ArrayMap<>();
+ if (appIdTags == null) {
+ appIdTags = new ArrayMap<>();
}
- uidTags.put(packageName, packageTags);
- datastore.put(uid, uidTags);
- } else if (uidTags != null) {
- uidTags.remove(packageName);
- if (!uidTags.isEmpty()) {
- datastore.put(uid, uidTags);
+ appIdTags.put(packageName, packageTags);
+ datastore.put(appId, appIdTags);
+ } else if (appIdTags != null) {
+ appIdTags.remove(packageName);
+ if (!appIdTags.isEmpty()) {
+ datastore.put(appId, appIdTags);
} else {
- datastore.remove(uid);
+ datastore.remove(appId);
}
}
}
@@ -318,9 +319,10 @@
@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);
+ final int appId = UserHandle.getAppId(uid);
+ final ArrayMap<String, ArraySet<String>> appIdTags = mappedOps.get(appId);
+ if (appIdTags != null) {
+ final ArraySet<String> packageTags = appIdTags.get(packageName);
if (packageTags != null && packageTags.contains(attributionTag)) {
return true;
}
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 6f6bdac..edd5f5f 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -83,6 +83,7 @@
private static final String TAG = "DeviceStateProviderImpl";
private static final BooleanSupplier TRUE_BOOLEAN_SUPPLIER = () -> true;
+ private static final BooleanSupplier FALSE_BOOLEAN_SUPPLIER = () -> false;
@VisibleForTesting
static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(MINIMUM_DEVICE_STATE,
@@ -152,7 +153,7 @@
private final DeviceState[] mOrderedStates;
// Map of state identifier to a boolean supplier that returns true when all required conditions
// are met for the device to be in the state.
- private final SparseArray<BooleanSupplier> mStateConditions;
+ private final SparseArray<BooleanSupplier> mStateConditions = new SparseArray<>();
@Nullable
@GuardedBy("mLock")
@@ -177,6 +178,11 @@
Arrays.sort(orderedStates, Comparator.comparingInt(DeviceState::getIdentifier));
mOrderedStates = orderedStates;
+ setStateConditions(deviceStates, stateConditions);
+ }
+
+ private void setStateConditions(@NonNull List<DeviceState> deviceStates,
+ @NonNull List<Conditions> stateConditions) {
// Whether or not this instance should register to receive lid switch notifications from
// InputManagerInternal. If there are no device state conditions that are based on the lid
// switch there is no need to register for a callback.
@@ -185,7 +191,6 @@
// The set of Sensor(s) that this instance should register to receive SensorEvent(s) from.
final ArraySet<Sensor> sensorsToListenTo = new ArraySet<>();
- mStateConditions = new SparseArray<>();
for (int i = 0; i < stateConditions.size(); i++) {
final int state = deviceStates.get(i).getIdentifier();
final Conditions conditions = stateConditions.get(i);
@@ -194,12 +199,20 @@
continue;
}
+ // Whether or not all the required hardware components could be found that match the
+ // requirements from the config.
+ boolean allRequiredComponentsFound = true;
+ // Whether or not this condition requires the lid switch.
+ boolean lidSwitchRequired = false;
+ // Set of sensors required for this condition.
+ ArraySet<Sensor> sensorsRequired = new ArraySet<>();
+
List<BooleanSupplier> suppliers = new ArrayList<>();
LidSwitchCondition lidSwitchCondition = conditions.getLidSwitch();
if (lidSwitchCondition != null) {
suppliers.add(new LidSwitchBooleanSupplier(lidSwitchCondition.getOpen()));
- shouldListenToLidSwitch = true;
+ lidSwitchRequired = true;
}
List<SensorCondition> sensorConditions = conditions.getSensor();
@@ -210,22 +223,33 @@
final Sensor foundSensor = findSensor(expectedSensorType, expectedSensorName);
if (foundSensor == null) {
- throw new IllegalStateException("Failed to find Sensor with type: "
- + expectedSensorType + " and name: " + expectedSensorName);
+ Slog.e(TAG, "Failed to find Sensor with type: " + expectedSensorType
+ + " and name: " + expectedSensorName);
+ allRequiredComponentsFound = false;
+ break;
}
suppliers.add(new SensorBooleanSupplier(foundSensor, sensorCondition.getValue()));
- sensorsToListenTo.add(foundSensor);
+ sensorsRequired.add(foundSensor);
}
- if (suppliers.size() > 1) {
- mStateConditions.put(state, new AndBooleanSupplier(suppliers));
- } else if (suppliers.size() > 0) {
- // No need to wrap with an AND supplier if there is only 1.
- mStateConditions.put(state, suppliers.get(0));
+ if (allRequiredComponentsFound) {
+ shouldListenToLidSwitch |= lidSwitchRequired;
+ sensorsToListenTo.addAll(sensorsRequired);
+
+ if (suppliers.size() > 1) {
+ mStateConditions.put(state, new AndBooleanSupplier(suppliers));
+ } else if (suppliers.size() > 0) {
+ // No need to wrap with an AND supplier if there is only 1.
+ mStateConditions.put(state, suppliers.get(0));
+ } else {
+ // There are no conditions for this state. Default to always true.
+ mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER);
+ }
} else {
- // There are no conditions for this state. Default to always true.
- mStateConditions.put(state, TRUE_BOOLEAN_SUPPLIER);
+ // Failed to setup this condition. This can happen if a sensor is missing. Default
+ // this state to always false.
+ mStateConditions.put(state, FALSE_BOOLEAN_SUPPLIER);
}
}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index f014b07..4b71742 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -41,6 +41,7 @@
import android.content.res.XmlResourceParser;
import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
import android.net.Uri;
import android.os.Binder;
@@ -188,8 +189,6 @@
private boolean mTrustAgentsCanRun = false;
private int mCurrentUser = UserHandle.USER_SYSTEM;
- private Authorization mAuthorizationService;
-
public TrustManagerService(Context context) {
super(context);
mContext = context;
@@ -199,7 +198,6 @@
mStrongAuthTracker = new StrongAuthTracker(context);
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
mSettingsObserver = new SettingsObserver(mHandler);
- mAuthorizationService = new Authorization();
}
@Override
@@ -701,13 +699,14 @@
}
if (changed) {
dispatchDeviceLocked(userId, locked);
-
- Authorization.onLockScreenEvent(locked, userId, null);
+ Authorization.onLockScreenEvent(locked, userId, null,
+ getBiometricSids(userId));
// Also update the user's profiles who have unified challenge, since they
// share the same unlocked state (see {@link #isDeviceLocked(int)})
for (int profileHandle : mUserManager.getEnabledProfileIds(userId)) {
if (mLockPatternUtils.isManagedProfileWithUnifiedChallenge(profileHandle)) {
- mAuthorizationService.onLockScreenEvent(locked, profileHandle, null);
+ Authorization.onLockScreenEvent(locked, profileHandle, null,
+ getBiometricSids(profileHandle));
}
}
}
@@ -1047,6 +1046,14 @@
}
}
+ private long[] getBiometricSids(int userId) {
+ BiometricManager biometricManager = mContext.getSystemService(BiometricManager.class);
+ if (biometricManager == null) {
+ return null;
+ }
+ return biometricManager.getAuthenticatorIds(userId);
+ }
+
// User lifecycle
@Override
@@ -1258,7 +1265,8 @@
mDeviceLockedForUser.put(userId, locked);
}
- Authorization.onLockScreenEvent(locked, userId, null);
+ Authorization.onLockScreenEvent(locked, userId, null,
+ getBiometricSids(userId));
if (locked) {
try {
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 8f60b09..65b947c 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -1558,8 +1558,22 @@
teardownAsynchronously();
} /* networkUnwantedCallback */,
(status) -> {
- if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
- clearFailedAttemptCounterAndSafeModeAlarm();
+ switch (status) {
+ case NetworkAgent.VALIDATION_STATUS_VALID:
+ clearFailedAttemptCounterAndSafeModeAlarm();
+ break;
+ case NetworkAgent.VALIDATION_STATUS_NOT_VALID:
+ // Will only set a new alarm if no safe mode alarm is
+ // currently scheduled.
+ setSafeModeAlarm();
+ break;
+ default:
+ Slog.wtf(
+ TAG,
+ "Unknown validation status "
+ + status
+ + "; ignoring");
+ break;
}
} /* validationStatusCallback */);
@@ -1837,7 +1851,7 @@
private long getNextRetryIntervalsMs() {
final int retryDelayIndex = mFailedAttempts - 1;
- final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMs();
+ final long[] retryIntervalsMs = mConnectionConfig.getRetryIntervalsMillis();
// Repeatedly use last item in retry timeout list.
if (retryDelayIndex >= retryIntervalsMs.length) {
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index efee0a1..eb9ab36 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -827,9 +827,9 @@
if (rootTask.inFreeformWindowingMode()) {
rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- } else if (!mService.mSupportsNonResizableMultiWindow && r.inSizeCompatMode()) {
- throw new IllegalStateException("Size-compat windows are currently not"
- + "freeform-enabled");
+ } else if (!r.supportsFreeform()) {
+ throw new IllegalStateException(
+ "This activity is currently not freeform-enabled");
} else if (rootTask.getParent().inFreeformWindowingMode()) {
// If the window is on a freeform display, set it to undefined. It will be
// resolved to freeform and it can adjust windowing mode when the display mode
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3e8bc5d..6957aa0 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2465,8 +2465,7 @@
if (windowingMode == WINDOWING_MODE_PINNED && info.supportsPictureInPicture()) {
return false;
}
- if (WindowConfiguration.inMultiWindowMode(windowingMode)
- && mAtmService.mSupportsNonResizableMultiWindow
+ if (WindowConfiguration.inMultiWindowMode(windowingMode) && supportsMultiWindow()
&& !mAtmService.mForceResizableActivities) {
// The non resizable app will be letterboxed instead of being forced resizable.
return false;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index aa993bf..9178a8d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -561,6 +561,14 @@
public abstract ActivityMetricsLaunchObserverRegistry getLaunchObserverRegistry();
/**
+ * Returns the URI permission owner associated with the given activity (see
+ * {@link ActivityRecord#getUriPermissionsLocked()}). If the passed-in activity token is
+ * invalid, returns null.
+ */
+ @Nullable
+ public abstract IBinder getUriPermissionOwnerForActivity(@NonNull IBinder activityToken);
+
+ /**
* Gets bitmap snapshot of the provided task id.
*
* <p>Warning! this may restore the snapshot from disk so can block, don't call in a latency
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0570f6c..6198573 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3340,11 +3340,6 @@
}
@Override
- public boolean supportsNonResizableMultiWindow() {
- return mSupportsNonResizableMultiWindow;
- }
-
- @Override
public boolean updateConfiguration(Configuration values) {
mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
@@ -6256,6 +6251,16 @@
}
}
+ @Nullable
+ @Override
+ public IBinder getUriPermissionOwnerForActivity(@NonNull IBinder activityToken) {
+ ActivityTaskManagerService.enforceNotIsolatedCaller("getUriPermissionOwnerForActivity");
+ synchronized (mGlobalLock) {
+ ActivityRecord r = ActivityRecord.isInRootTaskLocked(activityToken);
+ return (r == null) ? null : r.getUriPermissionsLocked().getExternalToken();
+ }
+ }
+
@Override
public TaskSnapshot getTaskSnapshotBlocking(
int taskId, boolean isLowResolution) {
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 43326df..d5a7619 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -1691,7 +1691,11 @@
static boolean isTaskTransitOld(@TransitionOldType int transit) {
return isTaskOpenTransitOld(transit)
- || transit == TRANSIT_OLD_TASK_CLOSE
+ || isTaskCloseTransitOld(transit);
+ }
+
+ static boolean isTaskCloseTransitOld(@TransitionOldType int transit) {
+ return transit == TRANSIT_OLD_TASK_CLOSE
|| transit == TRANSIT_OLD_TASK_TO_BACK;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e28ab26..a108478 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1538,6 +1538,11 @@
// to cover the activity configuration change.
return false;
}
+ if (r.mStartingData != null && r.mStartingData.hasImeSurface()) {
+ // Currently it is unknown that when will IME window be ready. Reject the case to
+ // avoid flickering by showing IME in inconsistent orientation.
+ return false;
+ }
if (checkOpening) {
if (!mAppTransition.isTransitionSet() || !mOpeningApps.contains(r)) {
// Apply normal rotation animation in case of the activity set different requested
@@ -2108,9 +2113,7 @@
}
// Check if input device can dispatch events to current display.
- // If display type is virtual, will follow the default display.
- if (!mWmService.mInputManager.canDispatchToDisplay(device.getId(),
- displayInfo.type == Display.TYPE_VIRTUAL ? DEFAULT_DISPLAY : mDisplayId)) {
+ if (!mWmService.mInputManager.canDispatchToDisplay(device.getId(), mDisplayId)) {
continue;
}
diff --git a/services/core/java/com/android/server/wm/DisplayHashController.java b/services/core/java/com/android/server/wm/DisplayHashController.java
index af0c3e3..94d81fb 100644
--- a/services/core/java/com/android/server/wm/DisplayHashController.java
+++ b/services/core/java/com/android/server/wm/DisplayHashController.java
@@ -129,10 +129,10 @@
private boolean mParsedXml;
/**
- * Specified throttle time in milliseconds. Don't allow an app to generate a display hash more
- * than once per throttleTime
+ * Specified duration between requests to generate a display hash in milliseconds. Requests
+ * faster than this delay will be throttled.
*/
- private int mThrottleDurationMillis = 0;
+ private int mDurationBetweenRequestMillis = 0;
/**
* The last time an app requested to generate a display hash in System time.
@@ -203,8 +203,8 @@
return true;
}
- int throttleDurationMs = getThrottleDurationMillis();
- if (currentTime - mLastRequestTimeMs < throttleDurationMs) {
+ int mDurationBetweenRequestsMs = getDurationBetweenRequestMillis();
+ if (currentTime - mLastRequestTimeMs < mDurationBetweenRequestsMs) {
return false;
}
@@ -233,7 +233,7 @@
(float) size.getHeight() / boundsInWindow.height());
}
- args.setGrayscale(displayHashParams.isUseGrayscale());
+ args.setGrayscale(displayHashParams.isGrayscaleBuffer());
SurfaceControl.ScreenshotHardwareBuffer screenshotHardwareBuffer =
SurfaceControl.captureLayers(args.build());
@@ -356,11 +356,11 @@
}
}
- private int getThrottleDurationMillis() {
+ private int getDurationBetweenRequestMillis() {
if (!parseXmlProperties()) {
return 0;
}
- return mThrottleDurationMillis;
+ return mDurationBetweenRequestMillis;
}
private boolean parseXmlProperties() {
@@ -406,8 +406,8 @@
}
TypedArray sa = res.obtainAttributes(attrs, R.styleable.DisplayHashingService);
- mThrottleDurationMillis = sa.getInt(
- R.styleable.DisplayHashingService_throttleDurationMillis, 0);
+ mDurationBetweenRequestMillis = sa.getInt(
+ R.styleable.DisplayHashingService_durationBetweenRequestsMillis, 0);
sa.recycle();
mParsedXml = true;
return true;
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 84616c0..aa7e6c9 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -116,10 +116,8 @@
/** Notifies that the input device configuration has changed. */
@Override
public void notifyConfigurationChanged() {
- // TODO(multi-display): Notify proper displays that are associated with this input device.
-
synchronized (mService.mGlobalLock) {
- mService.getDefaultDisplayContentLocked().sendNewConfiguration();
+ mService.mRoot.forAllDisplays(DisplayContent::sendNewConfiguration);
}
synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/java/com/android/server/wm/RootDisplayArea.java b/services/core/java/com/android/server/wm/RootDisplayArea.java
index cd20c82..1cda8d5 100644
--- a/services/core/java/com/android/server/wm/RootDisplayArea.java
+++ b/services/core/java/com/android/server/wm/RootDisplayArea.java
@@ -37,7 +37,7 @@
* of the whole logical display, or a {@link DisplayAreaGroup} as the root of a partition of the
* logical display.
*/
-class RootDisplayArea extends DisplayArea<DisplayArea> {
+class RootDisplayArea extends DisplayArea.Dimmable {
/** {@link Feature} that are supported in this {@link DisplayArea} hierarchy. */
List<DisplayAreaPolicyBuilder.Feature> mFeatures;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d9c5fa4..dc07988 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2657,9 +2657,12 @@
}
void addStartingWindowsForVisibleActivities() {
+ final ArrayList<Task> addedTasks = new ArrayList<>();
forAllActivities((r) -> {
- if (r.mVisibleRequested) {
+ final Task task = r.getTask();
+ if (r.mVisibleRequested && r.mStartingData == null && !addedTasks.contains(task)) {
r.showStartingWindow(true /*taskSwitch*/);
+ addedTasks.add(task);
}
});
}
diff --git a/services/core/java/com/android/server/wm/SnapshotStartingData.java b/services/core/java/com/android/server/wm/SnapshotStartingData.java
index 2124ed6..66ae0eb 100644
--- a/services/core/java/com/android/server/wm/SnapshotStartingData.java
+++ b/services/core/java/com/android/server/wm/SnapshotStartingData.java
@@ -39,4 +39,9 @@
return mService.mStartingSurfaceController.createTaskSnapshotSurface(activity,
mSnapshot);
}
+
+ @Override
+ boolean hasImeSurface() {
+ return mSnapshot.hasImeSurface();
+ }
}
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index a5bd797..59de43a 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -40,4 +40,9 @@
* {@link StartingSurface#remove}
*/
abstract StartingSurface createStartingSurface(ActivityRecord activity);
+
+ /** @see android.window.TaskSnapshot#hasImeSurface() */
+ boolean hasImeSurface() {
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index a9b06ca..c3815c1 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -118,7 +118,9 @@
return null;
}
if (topFullscreenActivity.getWindowConfiguration().getRotation()
- != taskSnapshot.getRotation()) {
+ != taskSnapshot.getRotation()
+ // Use normal rotation to avoid flickering of IME window in old orientation.
+ && !taskSnapshot.hasImeSurface()) {
// The snapshot should have been checked by ActivityRecord#isSnapshotCompatible
// that the activity will be updated to the same rotation as the snapshot. Since
// the transition is not started yet, fixed rotation transform needs to be applied
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2a0041a..c6478ee 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2836,14 +2836,13 @@
getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
}
- // Do not allow non-resizable tasks to be in a multi-window mode, unless it is in pinned
- // windowing mode or supports non-resizable tasks in multi-window mode.
- if (!isResizeable()) {
+ // Do not allow tasks not support multi window to be in a multi-window mode, unless it is in
+ // pinned windowing mode.
+ if (!supportsMultiWindow()) {
final int candidateWindowingMode =
windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode;
if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode)
- && candidateWindowingMode != WINDOWING_MODE_PINNED
- && !mTaskSupervisor.mService.mSupportsNonResizableMultiWindow) {
+ && candidateWindowingMode != WINDOWING_MODE_PINNED) {
getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
WINDOWING_MODE_FULLSCREEN);
}
@@ -4079,6 +4078,7 @@
info.lastActiveTime = lastActiveTime;
info.taskDescription = new ActivityManager.TaskDescription(getTaskDescription());
info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode();
+ info.supportsMultiWindow = supportsMultiWindow();
info.configuration.setTo(getConfiguration());
// Update to the task's current activity type and windowing mode which may differ from the
// window configuration
@@ -7176,8 +7176,11 @@
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task="
+ tr.mTaskId);
- mDisplayContent.prepareAppTransition(TRANSIT_TO_BACK);
- mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_TO_BACK, tr);
+ // Skip the transition for pinned task.
+ if (!inPinnedWindowingMode()) {
+ mDisplayContent.prepareAppTransition(TRANSIT_TO_BACK);
+ mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_TO_BACK, tr);
+ }
moveToBack("moveTaskToBackLocked", tr);
if (inPinnedWindowingMode()) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index cda8c4b..87f685e 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1274,7 +1274,15 @@
for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) {
if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) {
- return mLaunchRootTasks.get(i).task;
+ final Task launchRootTask = mLaunchRootTasks.get(i).task;
+ // Return the focusable root task for improving the UX with staged split screen.
+ final Task adjacentRootTask = launchRootTask != null
+ ? launchRootTask.mAdjacentTask : null;
+ if (adjacentRootTask != null && adjacentRootTask.isFocusedRootTaskOnDisplay()) {
+ return adjacentRootTask;
+ } else {
+ return launchRootTask;
+ }
}
}
return null;
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 29677b2..0bc7999 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -612,7 +612,7 @@
private boolean shouldLaunchUnresizableAppInFreeform(ActivityRecord activity,
TaskDisplayArea displayArea) {
- if (!mSupervisor.mService.mSupportsNonResizableMultiWindow || activity.isResizeable()) {
+ if (!activity.supportsFreeform() || activity.isResizeable()) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index bea733b..b1c7e19 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -37,6 +37,7 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.AppTransition.MAX_APP_TRANSITION_DURATION;
+import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -2696,7 +2697,14 @@
@Nullable ArrayList<WindowContainer> sources) {
final Task task = asTask();
if (task != null && !enter && !task.isHomeOrRecentsRootTask()) {
- mDisplayContent.showImeScreenshot();
+ final InsetsControlTarget imeTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);
+ final boolean isImeLayeringTarget = imeTarget != null && imeTarget.getWindow() != null
+ && imeTarget.getWindow().getTask() == task;
+ // Attach and show the IME screenshot when the task is the IME target and performing
+ // task closing transition to the next task.
+ if (isImeLayeringTarget && AppTransition.isTaskCloseTransitOld(transit)) {
+ mDisplayContent.showImeScreenshot();
+ }
}
final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
transit, enter, isVoiceInteraction);
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index d076434..51bc99a 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -27,3 +27,4 @@
per-file com_android_server_security_* = file:/core/java/android/security/OWNERS
per-file com_android_server_tv_* = file:/media/java/android/media/tv/OWNERS
per-file com_android_server_vibrator_* = file:/services/core/java/com/android/server/vibrator/OWNERS
+per-file com_android_server_am_CachedAppOptimizer.cpp = timmurray@google.com, edgararriaga@google.com, dualli@google.com, carmenjackson@google.com, philipcuadra@google.com
\ No newline at end of file
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d9fa471..24f73c3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -883,6 +883,12 @@
synchronized (getLockObject()) {
// Check whether the user is affiliated, *before* removing its data.
boolean isRemovedUserAffiliated = isUserAffiliatedWithDeviceLocked(userHandle);
+ if (isProfileOwnerOfOrganizationOwnedDevice(userHandle)) {
+ // Disable network and security logging
+ mInjector.securityLogSetLoggingEnabledProperty(false);
+ mSecurityLogMonitor.stop();
+ setNetworkLoggingActiveInternal(false);
+ }
removeUserData(userHandle);
if (!isRemovedUserAffiliated) {
// We discard the logs when unaffiliated users are deleted (so that the
@@ -8552,11 +8558,12 @@
synchronized (getLockObject()) {
enforceCanSetProfileOwnerLocked(
caller, who, userHandle, hasIncompatibleAccountsOrNonAdb);
- Preconditions.checkArgument(isPackageInstalledForUser(who.getPackageName(), userHandle),
- "Component " + who + " not installed for userId:" + userHandle);
final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
- Preconditions.checkArgument(admin != null && !getUserData(
- userHandle).mRemovingAdmins.contains(who), "Not active admin: " + who);
+ Preconditions.checkArgument(
+ isPackageInstalledForUser(who.getPackageName(), userHandle)
+ && admin != null
+ && !getUserData(userHandle).mRemovingAdmins.contains(who),
+ "Not active admin: " + who);
final int parentUserId = getProfileParentId(userHandle);
// When trying to set a profile owner on a new user, it may be that this user is
@@ -8896,17 +8903,17 @@
}
@Override
- public ComponentName getProfileOwnerAsUser(int userHandle) {
+ public ComponentName getProfileOwnerAsUser(int userId) {
if (!mHasFeature) {
return null;
}
- Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+ Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
- final CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(hasCrossUsersPermission(caller, userHandle));
+ CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasCrossUsersPermission(caller, userId));
synchronized (getLockObject()) {
- return mOwners.getProfileOwnerComponent(userHandle);
+ return mOwners.getProfileOwnerComponent(userId);
}
}
@@ -9350,19 +9357,20 @@
public List<UserHandle> listForegroundAffiliatedUsers() {
checkIsDeviceOwner(getCallerIdentity());
- int userId = mInjector.binderWithCleanCallingIdentity(() -> getCurrentForegroundUserId());
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ int userId = getCurrentForegroundUserId();
+ boolean isAffiliated;
+ synchronized (getLockObject()) {
+ isAffiliated = isUserAffiliatedWithDeviceLocked(userId);
+ }
- boolean isAffiliated;
- synchronized (getLockObject()) {
- isAffiliated = isUserAffiliatedWithDeviceLocked(userId);
- }
+ if (!isAffiliated) return Collections.emptyList();
- if (!isAffiliated) return Collections.emptyList();
+ List<UserHandle> users = new ArrayList<>(1);
+ users.add(UserHandle.of(userId));
- List<UserHandle> users = new ArrayList<>(1);
- users.add(UserHandle.of(userId));
-
- return users;
+ return users;
+ });
}
protected int getProfileParentId(int userHandle) {
@@ -11931,17 +11939,25 @@
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+ UserHandle userHandle = caller.getUserHandle();
+ if (mIsAutomotive) {
+ Slogf.v(LOG_TAG, "setLocationEnabled(%s, %b): ignoring for user %s on automotive build",
+ who.flattenToShortString(), locationEnabled, userHandle);
+ return;
+ }
+
mInjector.binderWithCleanCallingIdentity(() -> {
boolean wasLocationEnabled = mInjector.getLocationManager().isLocationEnabledForUser(
- caller.getUserHandle());
- mInjector.getLocationManager().setLocationEnabledForUser(locationEnabled,
- caller.getUserHandle());
+ userHandle);
+ Slogf.v(LOG_TAG, "calling locationManager.setLocationEnabledForUser(%b, %s)",
+ locationEnabled, userHandle);
+ mInjector.getLocationManager().setLocationEnabledForUser(locationEnabled, userHandle);
// make a best effort to only show the notification if the admin is actually enabling
// location. this is subject to race conditions with settings changes, but those are
// unlikely to realistically interfere
if (locationEnabled && !wasLocationEnabled) {
- showLocationSettingsEnabledNotification(caller.getUserHandle());
+ showLocationSettingsEnabledNotification(userHandle);
}
});
@@ -13311,12 +13327,10 @@
final CallerIdentity caller = getCallerIdentity();
final long ident = mInjector.binderClearCallingIdentity();
try {
- final int uidForPackage = mInjector.getPackageManager().getPackageUidAsUser(
- packageName, caller.getUserId());
- Preconditions.checkArgument(caller.getUid() == uidForPackage,
+ final List<String> callerUidPackageNames = Arrays.asList(
+ mInjector.getPackageManager().getPackagesForUid(caller.getUid()));
+ Preconditions.checkArgument(callerUidPackageNames.contains(packageName),
"Caller uid doesn't match the one for the provided package.");
- } catch (NameNotFoundException e) {
- throw new IllegalArgumentException("Invalid package provided " + packageName, e);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
@@ -14116,7 +14130,7 @@
}
}
- private boolean isUserAffiliatedWithDeviceLocked(int userId) {
+ private boolean isUserAffiliatedWithDeviceLocked(@UserIdInt int userId) {
if (!mOwners.hasDeviceOwner()) {
return false;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index 285ecfb..a2db6aac 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -15,8 +15,12 @@
*/
package com.android.server.devicepolicy;
+import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
import android.os.ShellCommand;
+import android.os.SystemClock;
+import android.os.UserHandle;
import com.android.server.devicepolicy.Owners.OwnerDto;
@@ -32,8 +36,23 @@
private static final String CMD_SET_SAFE_OPERATION = "set-operation-safe";
private static final String CMD_LIST_OWNERS = "list-owners";
private static final String CMD_LIST_POLICY_EXEMPT_APPS = "list-policy-exempt-apps";
+ private static final String CMD_SET_ACTIVE_ADMIN = "set-active-admin";
+ private static final String CMD_SET_DEVICE_OWNER = "set-device-owner";
+ private static final String CMD_SET_PROFILE_OWNER = "set-profile-owner";
+ private static final String CMD_REMOVE_ACTIVE_ADMIN = "remove-active-admin";
+ private static final String CMD_CLEAR_FREEZE_PERIOD_RECORD = "clear-freeze-period-record";
+ private static final String CMD_FORCE_NETWORK_LOGS = "force-network-logs";
+ private static final String CMD_FORCE_SECURITY_LOGS = "force-security-logs";
+ private static final String CMD_MARK_PO_ON_ORG_OWNED_DEVICE =
+ "mark-profile-owner-on-organization-owned-device";
+
+ private static final String USER_OPTION = "--user";
+ private static final String NAME_OPTION = "--name";
private final DevicePolicyManagerService mService;
+ private int mUserId = UserHandle.USER_SYSTEM;
+ private String mName = "";
+ private ComponentName mComponent;
DevicePolicyManagerServiceShellCommand(DevicePolicyManagerService service) {
mService = Objects.requireNonNull(service);
@@ -41,7 +60,7 @@
@Override
public void onHelp() {
- try (PrintWriter pw = getOutPrintWriter();) {
+ try (PrintWriter pw = getOutPrintWriter()) {
pw.printf("DevicePolicyManager Service (device_policy) commands:\n\n");
showHelp(pw);
}
@@ -52,7 +71,7 @@
if (cmd == null) {
return handleDefaultCommands(cmd);
}
- try (PrintWriter pw = getOutPrintWriter();) {
+ try (PrintWriter pw = getOutPrintWriter()) {
switch (cmd) {
case CMD_IS_SAFE_OPERATION:
return runIsSafeOperation(pw);
@@ -64,6 +83,22 @@
return runListOwners(pw);
case CMD_LIST_POLICY_EXEMPT_APPS:
return runListPolicyExemptApps(pw);
+ case CMD_SET_ACTIVE_ADMIN:
+ return runSetActiveAdmin(pw);
+ case CMD_SET_DEVICE_OWNER:
+ return runSetDeviceOwner(pw);
+ case CMD_SET_PROFILE_OWNER:
+ return runSetProfileOwner(pw);
+ case CMD_REMOVE_ACTIVE_ADMIN:
+ return runRemoveActiveAdmin(pw);
+ case CMD_CLEAR_FREEZE_PERIOD_RECORD:
+ return runClearFreezePeriodRecord(pw);
+ case CMD_FORCE_NETWORK_LOGS:
+ return runForceNetworkLogs(pw);
+ case CMD_FORCE_SECURITY_LOGS:
+ return runForceSecurityLogs(pw);
+ case CMD_MARK_PO_ON_ORG_OWNED_DEVICE:
+ return runMarkProfileOwnerOnOrganizationOwnedDevice(pw);
default:
return onInvalidCommand(pw, cmd);
}
@@ -75,7 +110,7 @@
return 0;
}
- pw.println("Usage: ");
+ pw.printf("Usage: \n");
showHelp(pw);
return -1;
}
@@ -94,6 +129,37 @@
pw.printf(" Lists the device / profile owners per user \n\n");
pw.printf(" %s\n", CMD_LIST_POLICY_EXEMPT_APPS);
pw.printf(" Lists the apps that are exempt from policies\n\n");
+ pw.printf(" %s [ %s <USER_ID> | current ] <COMPONENT>\n",
+ CMD_SET_ACTIVE_ADMIN, USER_OPTION);
+ pw.printf(" Sets the given component as active admin for an existing user.\n\n");
+ pw.printf(" %s [ %s <USER_ID> | current *EXPERIMENTAL* ] [ %s <NAME> ] "
+ + "<COMPONENT>\n", CMD_SET_DEVICE_OWNER, USER_OPTION, NAME_OPTION);
+ pw.printf(" Sets the given component as active admin, and its package as device owner."
+ + "\n\n");
+ pw.printf(" %s [ %s <USER_ID> | current ] [ %s <NAME> ] <COMPONENT>\n",
+ CMD_SET_PROFILE_OWNER, USER_OPTION, NAME_OPTION);
+ pw.printf(" Sets the given component as active admin and profile owner for an existing "
+ + "user.\n\n");
+ pw.printf(" %s [ %s <USER_ID> | current ] [ %s <NAME> ] <COMPONENT>\n",
+ CMD_REMOVE_ACTIVE_ADMIN, USER_OPTION, NAME_OPTION);
+ pw.printf(" Disables an active admin, the admin must have declared android:testOnly in "
+ + "the application in its manifest. This will also remove device and profile "
+ + "owners.\n\n");
+ pw.printf(" %s\n", CMD_CLEAR_FREEZE_PERIOD_RECORD);
+ pw.printf(" Clears framework-maintained record of past freeze periods that the device "
+ + "went through. For use during feature development to prevent triggering "
+ + "restriction on setting freeze periods.\n\n");
+ pw.printf(" %s\n", CMD_FORCE_NETWORK_LOGS);
+ pw.printf(" Makes all network logs available to the DPC and triggers "
+ + "DeviceAdminReceiver.onNetworkLogsAvailable() if needed.\n\n");
+ pw.printf(" %s\n", CMD_FORCE_SECURITY_LOGS);
+ pw.printf(" Makes all security logs available to the DPC and triggers "
+ + "DeviceAdminReceiver.onSecurityLogsAvailable() if needed.\n\n");
+ pw.printf(" %s [ %s <USER_ID> | current ] <COMPONENT>\n",
+ CMD_MARK_PO_ON_ORG_OWNED_DEVICE, USER_OPTION);
+ pw.printf(" Marks the profile owner of the given user as managing an organization-owned"
+ + "device. That will give it access to device identifiers (such as serial number, "
+ + "IMEI and MEID), as well as other privileges.\n\n");
}
private int runIsSafeOperation(PrintWriter pw) {
@@ -161,7 +227,6 @@
return 0;
}
-
private int runListPolicyExemptApps(PrintWriter pw) {
List<String> apps = mService.listPolicyExemptApps();
int size = printAndGetSize(pw, apps, "policy exempt app");
@@ -174,4 +239,131 @@
}
return 0;
}
+
+ private int runSetActiveAdmin(PrintWriter pw) {
+ parseArgs(/* canHaveName= */ false);
+ mService.setActiveAdmin(mComponent, /* refreshing= */ true, mUserId);
+
+ pw.printf("Success: Active admin set to component %s\n", mComponent.flattenToShortString());
+ return 0;
+ }
+
+ private int runSetDeviceOwner(PrintWriter pw) {
+ parseArgs(/* canHaveName= */ true);
+ mService.setActiveAdmin(mComponent, /* refreshing= */ true, mUserId);
+
+ try {
+ if (!mService.setDeviceOwner(mComponent, mName, mUserId)) {
+ throw new RuntimeException(
+ "Can't set package " + mComponent + " as device owner.");
+ }
+ } catch (Exception e) {
+ // Need to remove the admin that we just added.
+ mService.removeActiveAdmin(mComponent, UserHandle.USER_SYSTEM);
+ throw e;
+ }
+
+ mService.setUserProvisioningState(
+ DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId);
+
+ pw.printf("Success: Device owner set to package %s\n", mComponent.flattenToShortString());
+ pw.printf("Active admin set to component %s\n", mComponent.flattenToShortString());
+ return 0;
+ }
+
+ private int runRemoveActiveAdmin(PrintWriter pw) {
+ parseArgs(/* canHaveName= */ false);
+ mService.forceRemoveActiveAdmin(mComponent, mUserId);
+ pw.printf("Success: Admin removed %s\n", mComponent);
+ return 0;
+ }
+
+ private int runSetProfileOwner(PrintWriter pw) {
+ parseArgs(/* canHaveName= */ true);
+ mService.setActiveAdmin(mComponent, /* refreshing= */ true, mUserId);
+
+ try {
+ if (!mService.setProfileOwner(mComponent, mName, mUserId)) {
+ throw new RuntimeException("Can't set component "
+ + mComponent.flattenToShortString() + " as profile owner for user "
+ + mUserId);
+ }
+ } catch (Exception e) {
+ // Need to remove the admin that we just added.
+ mService.removeActiveAdmin(mComponent, mUserId);
+ throw e;
+ }
+
+ mService.setUserProvisioningState(
+ DevicePolicyManager.STATE_USER_SETUP_FINALIZED, mUserId);
+
+ pw.printf("Success: Active admin and profile owner set to %s for user %d\n",
+ mComponent.flattenToShortString(), mUserId);
+ return 0;
+ }
+
+ private int runClearFreezePeriodRecord(PrintWriter pw) {
+ mService.clearSystemUpdatePolicyFreezePeriodRecord();
+ pw.printf("Success\n");
+ return 0;
+ }
+
+ private int runForceNetworkLogs(PrintWriter pw) {
+ while (true) {
+ long toWait = mService.forceNetworkLogs();
+ if (toWait == 0) {
+ break;
+ }
+ pw.printf("We have to wait for %d milliseconds...\n", toWait);
+ SystemClock.sleep(toWait);
+ }
+ pw.printf("Success\n");
+ return 0;
+ }
+
+ private int runForceSecurityLogs(PrintWriter pw) {
+ while (true) {
+ long toWait = mService.forceSecurityLogs();
+ if (toWait == 0) {
+ break;
+ }
+ pw.printf("We have to wait for %d milliseconds...\n", toWait);
+ SystemClock.sleep(toWait);
+ }
+ pw.printf("Success\n");
+ return 0;
+ }
+
+ private int runMarkProfileOwnerOnOrganizationOwnedDevice(PrintWriter pw) {
+ parseArgs(/* canHaveName= */ false);
+ mService.markProfileOwnerOnOrganizationOwnedDevice(mComponent, mUserId);
+ pw.printf("Success\n");
+ return 0;
+ }
+
+ private void parseArgs(boolean canHaveName) {
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (USER_OPTION.equals(opt)) {
+ String arg = getNextArgRequired();
+ mUserId = UserHandle.parseUserArg(arg);
+ if (mUserId == UserHandle.USER_CURRENT) {
+ mUserId = ActivityManager.getCurrentUser();
+ }
+ } else if (canHaveName && NAME_OPTION.equals(opt)) {
+ mName = getNextArgRequired();
+ } else {
+ throw new IllegalArgumentException("Unknown option: " + opt);
+ }
+ }
+ mComponent = parseComponentName(getNextArgRequired());
+ }
+
+ private ComponentName parseComponentName(String component) {
+ ComponentName cn = ComponentName.unflattenFromString(component);
+ if (cn == null) {
+ throw new IllegalArgumentException("Invalid component " + component);
+ }
+ return cn;
+ }
}
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 1208ecc..9706d7f 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -23,11 +23,13 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ResolveInfo;
import android.os.Handler;
import android.os.IBinder.DeathRecipient;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.os.UpdateEngine;
import android.os.UpdateEngineCallback;
import android.os.UserHandle;
@@ -42,6 +44,9 @@
import com.android.server.wm.ActivityMetricsLaunchObserverRegistry;
import com.android.server.wm.ActivityTaskManagerInternal;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
@@ -75,7 +80,7 @@
*/
public static boolean enabled() {
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT, "enabled",
- false);
+ false) || SystemProperties.getBoolean("persist.profcollectd.enabled_override", false);
}
@Override
@@ -297,24 +302,20 @@
return;
}
- final boolean uploadReport =
- DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
- "upload_report", false);
-
new Thread(() -> {
try {
String reportUuid = mIProfcollect.report();
- if (!uploadReport) {
+ final int profileId = getBBProfileId();
+ String reportDir = "/data/user/" + profileId
+ + "/com.google.android.apps.internal.betterbug/cache/";
+ String reportPath = reportDir + reportUuid + ".zip";
+
+ if (!Files.exists(Paths.get(reportDir))) {
+ Log.i(LOG_TAG, "Destination directory does not exist, abort upload.");
return;
}
- final int profileId = getBBProfileId();
- mIProfcollect.copy_report_to_bb(profileId, reportUuid);
- String reportPath =
- "/data/user/" + profileId
- + "/com.google.android.apps.internal.betterbug/cache/"
- + reportUuid + ".zip";
Intent uploadIntent =
new Intent("com.google.android.apps.betterbug.intent.action.UPLOAD_PROFILE")
.setPackage("com.google.android.apps.internal.betterbug")
@@ -323,9 +324,15 @@
.putExtra("EXTRA_PROFILE_PATH", reportPath)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
Context context = getContext();
- if (context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0) != null) {
- context.sendBroadcast(uploadIntent);
+
+ List<ResolveInfo> receivers =
+ context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0);
+ if (receivers == null || receivers.isEmpty()) {
+ Log.i(LOG_TAG, "No one to receive upload intent, abort upload.");
+ return;
}
+ mIProfcollect.copy_report_to_bb(profileId, reportUuid);
+ context.sendBroadcast(uploadIntent);
mIProfcollect.delete_report(reportUuid);
} catch (RemoteException e) {
Log.e(LOG_TAG, e.getMessage());
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 3bc1c8a..64dad7f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -80,6 +80,7 @@
import static com.android.server.alarm.AlarmManagerService.FREQUENT_INDEX;
import static com.android.server.alarm.AlarmManagerService.INDEFINITE_DELAY;
import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK;
+import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_UNDEFINED;
import static com.android.server.alarm.AlarmManagerService.TIME_CHANGED_MASK;
import static com.android.server.alarm.AlarmManagerService.WORKING_INDEX;
import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
@@ -771,10 +772,10 @@
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, pi6);
assertEquals(mNowElapsedTest + 6, mTestTimer.getElapsed());
- mService.removeLocked(pi6, null);
+ mService.removeLocked(pi6, null, REMOVE_REASON_UNDEFINED);
assertEquals(mNowElapsedTest + 8, mTestTimer.getElapsed());
- mService.removeLocked(pi8, null);
+ mService.removeLocked(pi8, null, REMOVE_REASON_UNDEFINED);
assertEquals(mNowElapsedTest + 9, mTestTimer.getElapsed());
}
@@ -1252,7 +1253,8 @@
setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 10, getNewMockPendingIntent());
}
assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
- mService.removeLocked(TEST_CALLING_UID);
+ mService.removeLocked(TEST_CALLING_UID,
+ REMOVE_REASON_UNDEFINED);
assertEquals(0, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
}
@@ -1292,7 +1294,7 @@
}
assertEquals(numAlarms, mService.mAlarmsPerUid.get(TEST_CALLING_UID));
for (int i = 0; i < numAlarms; i++) {
- mService.removeLocked(pis[i], null);
+ mService.removeLocked(pis[i], null, REMOVE_REASON_UNDEFINED);
assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
}
}
@@ -1422,7 +1424,7 @@
mTestTimer.expire();
assertEquals(numAlarms, mService.mPendingNonWakeupAlarms.size());
for (int i = 0; i < numAlarms; i++) {
- mService.removeLocked(pis[i], null);
+ mService.removeLocked(pis[i], null, REMOVE_REASON_UNDEFINED);
assertEquals(numAlarms - i - 1, mService.mAlarmsPerUid.get(TEST_CALLING_UID, 0));
}
}
@@ -1492,13 +1494,13 @@
assertEquals(trigger6, mTestTimer.getElapsed());
assertEquals(trigger6, mService.mNextWakeFromIdle.getWhenElapsed());
- mService.removeLocked(wakeFromIdle6, null);
+ mService.removeLocked(wakeFromIdle6, null, REMOVE_REASON_UNDEFINED);
assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle10, null));
assertEquals(trigger10, mTestTimer.getElapsed());
assertEquals(trigger10, mService.mNextWakeFromIdle.getWhenElapsed());
- mService.removeLocked(wakeFromIdle10, null);
+ mService.removeLocked(wakeFromIdle10, null, REMOVE_REASON_UNDEFINED);
assertNull(mService.mNextWakeFromIdle);
}
@@ -1524,13 +1526,13 @@
setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 12, wakeFromIdle12);
assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed());
- mService.removeLocked(wakeFromIdle5, null);
+ mService.removeLocked(wakeFromIdle5, null, REMOVE_REASON_UNDEFINED);
assertEquals(mNowElapsedTest + 8, mService.mPendingIdleUntil.getWhenElapsed());
- mService.removeLocked(wakeFromIdle8, null);
+ mService.removeLocked(wakeFromIdle8, null, REMOVE_REASON_UNDEFINED);
assertEquals(requestedIdleUntil, mService.mPendingIdleUntil.getWhenElapsed());
- mService.removeLocked(idleUntilPi, null);
+ mService.removeLocked(idleUntilPi, null, REMOVE_REASON_UNDEFINED);
assertNull(mService.mPendingIdleUntil);
setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 15, idleUntilPi);
@@ -1641,7 +1643,7 @@
verify(pis[0], never()).send(eq(mMockContext), eq(0), any(Intent.class), any(),
any(Handler.class), isNull(), any());
- mService.removeLocked(idleUntil, null);
+ mService.removeLocked(idleUntil, null, REMOVE_REASON_UNDEFINED);
mTestTimer.expire();
// Now, the first 5 alarms (upto i = 4) should expire.
for (int i = 0; i < 5; i++) {
@@ -1688,14 +1690,16 @@
getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
// Refresh the state
- mService.removeLocked(TEST_CALLING_UID);
+ mService.removeLocked(TEST_CALLING_UID,
+ REMOVE_REASON_UNDEFINED);
mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
testQuotasDeferralOnExpiration(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP,
trigger, getNewMockPendingIntent(), false), quota, mAllowWhileIdleWindow);
// Refresh the state
- mService.removeLocked(TEST_CALLING_UID);
+ mService.removeLocked(TEST_CALLING_UID,
+ REMOVE_REASON_UNDEFINED);
mService.mAllowWhileIdleHistory.removeForPackage(TEST_CALLING_PACKAGE, TEST_CALLING_USER);
testQuotasNoDeferral(trigger -> setAllowWhileIdleAlarm(ELAPSED_REALTIME_WAKEUP, trigger,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
index 45f43e8..ee00cb2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -43,9 +43,12 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.InstallSourceInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.SigningInfo;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
@@ -81,8 +84,10 @@
private static final int APP_PID = 2000;
private static final int SYSTEM_PID = 558;
private static final int TEST_USER_ID = UserHandle.USER_SYSTEM;
+ private static final String TEST_PACKAGE_NAME = "com.android.server.accessibility";
private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(
- "com.android.server.accessibility", "AccessibilitySecurityPolicyTest");
+ TEST_PACKAGE_NAME, "AccessibilitySecurityPolicyTest");
+ private static final String ALLOWED_INSTALL_PACKAGE_NAME = "com.allowed.install.package";
private static final int[] ALWAYS_DISPATCH_EVENTS = {
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
@@ -142,19 +147,40 @@
@Mock
private AccessibilityServiceInfo mMockA11yServiceInfo;
@Mock
+ private ResolveInfo mMockResolveInfo;
+ @Mock
+ private ServiceInfo mMockServiceInfo;
+ @Mock
+ private ApplicationInfo mMockApplicationInfo;
+ @Mock
+ private ApplicationInfo mMockSourceApplicationInfo;
+ @Mock
+ private PackageInfo mMockSourcePackageInfo;
+ @Mock
private PolicyWarningUIController mPolicyWarningUIController;
@Before
- public void setUp() {
+ public void setUp() throws PackageManager.NameNotFoundException {
MockitoAnnotations.initMocks(this);
mContext.setMockPackageManager(mMockPackageManager);
mContext.addMockSystemService(Context.USER_SERVICE, mMockUserManager);
mContext.addMockSystemService(Context.APP_OPS_SERVICE, mMockAppOpsManager);
mContext.getOrCreateTestableResources().addOverride(
R.dimen.accessibility_focus_highlight_stroke_width, 1);
+ mContext.getOrCreateTestableResources().addOverride(R.array
+ .config_accessibility_allowed_install_source,
+ new String[]{ALLOWED_INSTALL_PACKAGE_NAME});
+ when(mMockA11yServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo);
when(mMockA11yServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME);
when(mMockA11yServiceConnection.getServiceInfo()).thenReturn(mMockA11yServiceInfo);
+ when(mMockPackageManager.getPackageInfo(ALLOWED_INSTALL_PACKAGE_NAME, 0)).thenReturn(
+ mMockSourcePackageInfo);
+
+ mMockResolveInfo.serviceInfo = mMockServiceInfo;
+ mMockServiceInfo.applicationInfo = mMockApplicationInfo;
+ mMockServiceInfo.packageName = TEST_PACKAGE_NAME;
+ mMockSourcePackageInfo.applicationInfo = mMockSourceApplicationInfo;
mA11ySecurityPolicy = new AccessibilitySecurityPolicy(
mPolicyWarningUIController, mContext, mMockA11yUserManager);
@@ -595,28 +621,7 @@
}
@Test
- public void onBoundServicesChanged_bindA11yCategoryService_noUIControllerAction() {
- final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
- boundServices.add(mMockA11yServiceConnection);
- when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
-
- mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
-
- verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
- }
-
- @Test
- public void onBoundServicesChanged_unbindA11yCategoryService_noUIControllerAction() {
- onBoundServicesChanged_bindA11yCategoryService_noUIControllerAction();
-
- mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
-
- verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(),
- any());
- }
-
- @Test
- public void onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction() {
+ public void onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction() {
final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
boundServices.add(mMockA11yServiceConnection);
when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(false);
@@ -628,8 +633,8 @@
}
@Test
- public void onBoundServicesChanged_unbindNonA11yCategoryService_activateUIControllerAction() {
- onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction();
+ public void onBoundServicesChanged_unbindNonA11yToolService_activateUIControllerAction() {
+ onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction();
mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
@@ -638,8 +643,65 @@
}
@Test
+ public void onBoundServicesChanged_bindSystemA11yToolService_noUIControllerAction() {
+ final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
+ boundServices.add(mMockA11yServiceConnection);
+ when(mMockApplicationInfo.isSystemApp()).thenReturn(true);
+ when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
+
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+
+ verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
+ }
+
+ @Test
+ public void onBoundServicesChanged_unbindSystemA11yToolService_noUIControllerAction() {
+ onBoundServicesChanged_bindSystemA11yToolService_noUIControllerAction();
+
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, new ArrayList<>());
+
+ verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceUnbound(anyInt(),
+ any());
+ }
+
+ @Test
+ public void onBoundServicesChanged_bindAllowedSourceA11yToolService_noUIControllerAction()
+ throws PackageManager.NameNotFoundException {
+ final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
+ boundServices.add(mMockA11yServiceConnection);
+ when(mMockApplicationInfo.isSystemApp()).thenReturn(false);
+ final InstallSourceInfo installSourceInfo = new InstallSourceInfo(
+ ALLOWED_INSTALL_PACKAGE_NAME, new SigningInfo(), null,
+ ALLOWED_INSTALL_PACKAGE_NAME);
+ when(mMockPackageManager.getInstallSourceInfo(TEST_PACKAGE_NAME)).thenReturn(
+ installSourceInfo);
+ when(mMockSourceApplicationInfo.isSystemApp()).thenReturn(true);
+ when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
+
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+
+ verify(mPolicyWarningUIController, never()).onNonA11yCategoryServiceBound(anyInt(), any());
+ }
+
+ @Test
+ public void onBoundServicesChanged_bindUnknownSourceA11yToolService_activateUIControllerAction()
+ throws PackageManager.NameNotFoundException {
+ final ArrayList<AccessibilityServiceConnection> boundServices = new ArrayList<>();
+ boundServices.add(mMockA11yServiceConnection);
+ when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(true);
+ final InstallSourceInfo installSourceInfo = new InstallSourceInfo(null, null, null, null);
+ when(mMockPackageManager.getInstallSourceInfo(TEST_PACKAGE_NAME)).thenReturn(
+ installSourceInfo);
+
+ mA11ySecurityPolicy.onBoundServicesChangedLocked(TEST_USER_ID, boundServices);
+
+ verify(mPolicyWarningUIController).onNonA11yCategoryServiceBound(eq(TEST_USER_ID),
+ eq(TEST_COMPONENT_NAME));
+ }
+
+ @Test
public void onSwitchUser_differentUser_activateUIControllerAction() {
- onBoundServicesChanged_bindNonA11yCategoryService_activateUIControllerAction();
+ onBoundServicesChanged_bindNonA11yToolService_activateUIControllerAction();
mA11ySecurityPolicy.onSwitchUserLocked(2, new HashSet<>());
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
index 4a6c9be..5c7a580 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -16,6 +16,9 @@
package com.android.server.apphibernation;
+import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED;
+import static android.app.usage.UsageEvents.Event.APP_COMPONENT_USED;
+import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static org.junit.Assert.assertEquals;
@@ -34,6 +37,9 @@
import static org.mockito.Mockito.verify;
import android.app.IActivityManager;
+import android.app.usage.UsageEvents.Event;
+import android.app.usage.UsageStatsManagerInternal;
+import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -81,6 +87,8 @@
private AppHibernationService mAppHibernationService;
private BroadcastReceiver mBroadcastReceiver;
+ private UsageEventListener mUsageEventListener;
+
@Mock
private Context mContext;
@Mock
@@ -93,8 +101,14 @@
private UserManager mUserManager;
@Mock
private HibernationStateDiskStore<UserLevelState> mUserLevelDiskStore;
+ @Mock
+ private UsageStatsManagerInternal mUsageStatsManagerInternal;
+ @Mock
+ private HibernationStateDiskStore<UserLevelState> mHibernationStateDiskStore;
@Captor
private ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
+ @Captor
+ private ArgumentCaptor<UsageEventListener> mUsageEventListenerCaptor;
@Before
public void setUp() throws RemoteException {
@@ -108,6 +122,8 @@
verify(mContext).registerReceiver(mReceiverCaptor.capture(), any());
mBroadcastReceiver = mReceiverCaptor.getValue();
+ verify(mUsageStatsManagerInternal).registerListener(mUsageEventListenerCaptor.capture());
+ mUsageEventListener = mUsageEventListenerCaptor.getValue();
doReturn(mUserInfos).when(mUserManager).getUsers();
@@ -284,6 +300,89 @@
assertEquals(capturedIntents.get(1).getAction(), Intent.ACTION_BOOT_COMPLETED);
}
+ @Test
+ public void testHibernatingPackageIsUnhibernatedForUserWhenUserInteracted() {
+ // GIVEN a package that is currently hibernated for a user
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
+
+ // WHEN the package is interacted with by user
+ generateUsageEvent(USER_INTERACTION);
+
+ // THEN the package is not hibernating anymore
+ assertFalse(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1));
+ }
+
+ @Test
+ public void testHibernatingPackageIsUnhibernatedForUserWhenActivityResumed() {
+ // GIVEN a package that is currently hibernated for a user
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
+
+ // WHEN the package has activity resumed
+ generateUsageEvent(ACTIVITY_RESUMED);
+
+ // THEN the package is not hibernating anymore
+ assertFalse(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1));
+ }
+
+ @Test
+ public void testHibernatingPackageIsUnhibernatedForUserWhenComponentUsed() {
+ // GIVEN a package that is currently hibernated for a user
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
+
+ // WHEN a package component is used
+ generateUsageEvent(APP_COMPONENT_USED);
+
+ // THEN the package is not hibernating anymore
+ assertFalse(mAppHibernationService.isHibernatingForUser(PACKAGE_NAME_1, USER_ID_1));
+ }
+
+ @Test
+ public void testHibernatingPackageIsUnhibernatedGloballyWhenUserInteracted() {
+ // GIVEN a package that is currently hibernated globally
+ mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
+
+ // WHEN the user interacts with the package
+ generateUsageEvent(USER_INTERACTION);
+
+ // THEN the package is not hibernating globally anymore
+ assertFalse(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
+ }
+
+ @Test
+ public void testHibernatingPackageIsUnhibernatedGloballyWhenActivityResumed() {
+ // GIVEN a package that is currently hibernated globally
+ mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
+
+ // WHEN activity in package resumed
+ generateUsageEvent(ACTIVITY_RESUMED);
+
+ // THEN the package is not hibernating globally anymore
+ assertFalse(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
+ }
+
+ @Test
+ public void testHibernatingPackageIsUnhibernatedGloballyWhenComponentUsed() {
+ // GIVEN a package that is currently hibernated globally
+ mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
+
+ // WHEN a package component is used
+ generateUsageEvent(APP_COMPONENT_USED);
+
+ // THEN the package is not hibernating globally anymore
+ assertFalse(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1));
+ }
+
+ /**
+ * Mock a usage event occurring.
+ *
+ * @param usageEventId id of a usage event
+ */
+ private void generateUsageEvent(int usageEventId) {
+ Event event = new Event(usageEventId, 0 /* timestamp */);
+ event.mPackage = PACKAGE_NAME_1;
+ mUsageEventListener.onUsageEvent(USER_ID_1, event);
+ }
+
/**
* Add a mock user with one package.
*/
@@ -360,6 +459,11 @@
}
@Override
+ public UsageStatsManagerInternal getUsageStatsManagerInternal() {
+ return mUsageStatsManagerInternal;
+ }
+
+ @Override
public Executor getBackgroundExecutor() {
// Just execute immediately in tests.
return r -> r.run();
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
index b552fd5..79e5865 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
@@ -70,7 +70,8 @@
mTemporaryFolder.newFolder(),
mContext,
mContext.getUserId(),
- mContext.getPackageName());
+ mContext.getPackageName(),
+ /*logger=*/ null);
mGlobalQuerierUid =
mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
}
@@ -117,9 +118,8 @@
// No query filters specified, global query can retrieve all documents.
SearchSpec searchSpec =
new SearchSpec.Builder().setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY).build();
- SearchResultPage searchResultPage =
- mAppSearchImpl.globalQuery(
- "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
+ SearchResultPage searchResultPage = mAppSearchImpl.globalQuery(
+ "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid, /*logger=*/ null);
assertThat(searchResultPage.getResults()).hasSize(2);
// Document2 will be first since it got indexed later and has a "better", aka more recent
@@ -174,9 +174,8 @@
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
.addFilterPackageNames("package1")
.build();
- SearchResultPage searchResultPage =
- mAppSearchImpl.globalQuery(
- "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
+ SearchResultPage searchResultPage = mAppSearchImpl.globalQuery(
+ "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid, /*logger=*/ null);
assertThat(searchResultPage.getResults()).hasSize(1);
assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1);
@@ -186,9 +185,8 @@
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
.addFilterPackageNames("package2")
.build();
- searchResultPage =
- mAppSearchImpl.globalQuery(
- "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid);
+ searchResultPage = mAppSearchImpl.globalQuery(
+ "", searchSpec, mContext.getPackageName(), mGlobalQuerierUid, /*logger=*/ null);
assertThat(searchResultPage.getResults()).hasSize(1);
assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java b/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java
index 11ae76b..28955d6 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java
@@ -67,7 +67,8 @@
mTemporaryFolder.newFolder(),
mContext,
mContext.getUserId(),
- /*globalQuerierPackage=*/ mContext.getPackageName());
+ /*globalQuerierPackage=*/ mContext.getPackageName(),
+ /*logger=*/ null);
mGlobalQuerierUid =
mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
@@ -163,7 +164,8 @@
mTemporaryFolder.newFolder(),
mContext,
mContext.getUserId(),
- /*globalQuerierPackage=*/ mContext.getPackageName());
+ /*globalQuerierPackage=*/ mContext.getPackageName(),
+ /*logger=*/ null);
VisibilityStore visibilityStore = appSearchImpl.getVisibilityStoreLocked();
// Use some arbitrary callerUid. If we can't find the global querier's uid though,
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
index 9a7cf80..b367203 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
@@ -82,7 +82,8 @@
mTemporaryFolder.newFolder(),
context,
VisibilityStore.NO_OP_USER_ID,
- /*globalQuerierPackage=*/ context.getPackageName());
+ /*globalQuerierPackage=*/ context.getPackageName(),
+ /*logger=*/ null);
}
// TODO(b/175430168) add test to verify reset is working properly.
@@ -604,7 +605,7 @@
SearchSpec searchSpec =
new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
SearchResultPage searchResultPage =
- mAppSearchImpl.query("package", "EmptyDatabase", "", searchSpec);
+ mAppSearchImpl.query("package", "EmptyDatabase", "", searchSpec, /*logger=*/ null);
assertThat(searchResultPage.getResults()).isEmpty();
}
@@ -647,7 +648,7 @@
SearchSpec searchSpec =
new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
SearchResultPage searchResultPage =
- mAppSearchImpl.query("package2", "database2", "", searchSpec);
+ mAppSearchImpl.query("package2", "database2", "", searchSpec, /*logger=*/ null);
assertThat(searchResultPage.getResults()).isEmpty();
// Insert package2 document
@@ -655,7 +656,9 @@
mAppSearchImpl.putDocument("package2", "database2", document, /*logger=*/ null);
// No query filters specified. package2 should only get its own documents back.
- searchResultPage = mAppSearchImpl.query("package2", "database2", "", searchSpec);
+ searchResultPage =
+ mAppSearchImpl.query("package2", "database2", "", searchSpec, /*logger=
+ */ null);
assertThat(searchResultPage.getResults()).hasSize(1);
assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
}
@@ -703,7 +706,7 @@
.addFilterPackageNames("package1")
.build();
SearchResultPage searchResultPage =
- mAppSearchImpl.query("package2", "database2", "", searchSpec);
+ mAppSearchImpl.query("package2", "database2", "", searchSpec, /*logger=*/ null);
assertThat(searchResultPage.getResults()).isEmpty();
// Insert package2 document
@@ -716,7 +719,9 @@
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
.addFilterPackageNames("package2")
.build();
- searchResultPage = mAppSearchImpl.query("package2", "database2", "", searchSpec);
+ searchResultPage =
+ mAppSearchImpl.query("package2", "database2", "", searchSpec, /*logger=
+ */ null);
assertThat(searchResultPage.getResults()).hasSize(1);
assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
}
@@ -727,7 +732,11 @@
new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
SearchResultPage searchResultPage =
mAppSearchImpl.globalQuery(
- "", searchSpec, /*callerPackageName=*/ "", /*callerUid=*/ 0);
+ "",
+ searchSpec,
+ /*callerPackageName=*/ "",
+ /*callerUid=*/ 0,
+ /*logger=*/ null);
assertThat(searchResultPage.getResults()).isEmpty();
}
@@ -1033,7 +1042,12 @@
SearchSpec searchSpec =
new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
SearchResultPage searchResultPage =
- mAppSearchImpl.query("package", "database", /*queryExpression=*/ "", searchSpec);
+ mAppSearchImpl.query(
+ "package",
+ "database",
+ /*queryExpression=*/ "",
+ searchSpec,
+ /*logger=*/ null);
assertThat(searchResultPage.getResults()).hasSize(1);
assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
@@ -1042,7 +1056,12 @@
// Verify the document is cleared.
searchResultPage =
- mAppSearchImpl.query("package2", "database2", /*queryExpression=*/ "", searchSpec);
+ mAppSearchImpl.query(
+ "package2",
+ "database2",
+ /*queryExpression=*/ "",
+ searchSpec,
+ /*logger=*/ null);
assertThat(searchResultPage.getResults()).isEmpty();
// Verify the schema is cleared.
@@ -1244,7 +1263,8 @@
new SearchSpec.Builder()
.setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
.setRankingStrategy(SearchSpec.RANKING_STRATEGY_USAGE_COUNT)
- .build())
+ .build(),
+ /*logger=*/ null)
.getResults();
assertThat(page).hasSize(2);
assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id1");
@@ -1262,7 +1282,8 @@
.setRankingStrategy(
SearchSpec
.RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP)
- .build())
+ .build(),
+ /*logger=*/ null)
.getResults();
assertThat(page).hasSize(2);
assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id2");
@@ -1279,7 +1300,8 @@
.setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
.setRankingStrategy(
SearchSpec.RANKING_STRATEGY_SYSTEM_USAGE_COUNT)
- .build())
+ .build(),
+ /*logger=*/ null)
.getResults();
assertThat(page).hasSize(2);
assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id2");
@@ -1297,7 +1319,8 @@
.setRankingStrategy(
SearchSpec
.RANKING_STRATEGY_SYSTEM_USAGE_LAST_USED_TIMESTAMP)
- .build())
+ .build(),
+ /*logger=*/ null)
.getResults();
assertThat(page).hasSize(2);
assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id1");
@@ -1499,7 +1522,9 @@
mTemporaryFolder.newFolder(),
context,
VisibilityStore.NO_OP_USER_ID,
- /*globalQuerierPackage=*/ "");
+ /*globalQuerierPackage=*/ "",
+ /*logger
+ =*/ null);
// Initial check that we could do something at first.
List<AppSearchSchema> schemas =
@@ -1561,7 +1586,8 @@
"query",
new SearchSpec.Builder()
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
- .build());
+ .build(),
+ /*logger=*/ null);
});
expectThrows(
@@ -1573,7 +1599,8 @@
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
.build(),
"package",
- /*callerUid=*/ 1);
+ /*callerUid=*/ 1,
+ /*logger=*/ null);
});
expectThrows(
@@ -1647,7 +1674,8 @@
appsearchDir,
context,
VisibilityStore.NO_OP_USER_ID,
- /*globalQuerierPackage=*/ "");
+ /*globalQuerierPackage=*/ "",
+ /*logger=*/ null);
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
@@ -1677,7 +1705,8 @@
appsearchDir,
context,
VisibilityStore.NO_OP_USER_ID,
- /*globalQuerierPackage=*/ "");
+ /*globalQuerierPackage=*/ "",
+ /*logger=*/ null);
getResult =
appSearchImpl2.getDocument(
"package", "database", "namespace1", "id1", Collections.emptyMap());
@@ -1694,7 +1723,8 @@
appsearchDir,
context,
VisibilityStore.NO_OP_USER_ID,
- /*globalQuerierPackage=*/ "");
+ /*globalQuerierPackage=*/ "",
+ /*logger=*/ null);
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
@@ -1748,7 +1778,8 @@
appsearchDir,
context,
VisibilityStore.NO_OP_USER_ID,
- /*globalQuerierPackage=*/ "");
+ /*globalQuerierPackage=*/ "",
+ /*logger=*/ null);
expectThrows(
AppSearchException.class,
() ->
@@ -1774,7 +1805,8 @@
appsearchDir,
context,
VisibilityStore.NO_OP_USER_ID,
- /*globalQuerierPackage=*/ "");
+ /*globalQuerierPackage=*/ "",
+ /*logger=*/ null);
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
@@ -1835,7 +1867,8 @@
appsearchDir,
context,
VisibilityStore.NO_OP_USER_ID,
- /*globalQuerierPackage=*/ "");
+ /*globalQuerierPackage=*/ "",
+ /*logger=*/ null);
expectThrows(
AppSearchException.class,
() ->
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
index 1194e76..5989bb6 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
@@ -23,13 +23,21 @@
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.GenericDocument;
+import android.app.appsearch.SearchResultPage;
+import android.app.appsearch.SearchSpec;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import com.android.server.appsearch.external.localstorage.stats.CallStats;
+import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
+import com.android.server.appsearch.external.localstorage.stats.SearchStats;
+import com.android.server.appsearch.proto.InitializeStatsProto;
import com.android.server.appsearch.proto.PutDocumentStatsProto;
+import com.android.server.appsearch.proto.QueryStatsProto;
+import com.android.server.appsearch.proto.ScoringSpecProto;
+import com.android.server.appsearch.proto.TermMatchType;
import org.junit.Before;
import org.junit.Rule;
@@ -54,23 +62,93 @@
mTemporaryFolder.newFolder(),
context,
VisibilityStore.NO_OP_USER_ID,
- /*globalQuerierPackage=*/ context.getPackageName());
+ /*globalQuerierPackage=*/ context.getPackageName(),
+ /*logger=*/ null);
mLogger = new TestLogger();
}
// Test only not thread safe.
public class TestLogger implements AppSearchLogger {
+ @Nullable CallStats mCallStats;
@Nullable PutDocumentStats mPutDocumentStats;
+ @Nullable InitializeStats mInitializeStats;
+ @Nullable SearchStats mSearchStats;
@Override
public void logStats(@NonNull CallStats stats) {
- throw new UnsupportedOperationException();
+ mCallStats = stats;
}
@Override
public void logStats(@NonNull PutDocumentStats stats) {
mPutDocumentStats = stats;
}
+
+ @Override
+ public void logStats(@NonNull InitializeStats stats) {
+ mInitializeStats = stats;
+ }
+
+ @Override
+ public void logStats(@NonNull SearchStats stats) {
+ mSearchStats = stats;
+ }
+ }
+
+ @Test
+ public void testAppSearchLoggerHelper_testCopyNativeStats_initialize() {
+ int nativeLatencyMillis = 3;
+ int nativeDocumentStoreRecoveryCause = InitializeStatsProto.RecoveryCause.DATA_LOSS_VALUE;
+ int nativeIndexRestorationCause =
+ InitializeStatsProto.RecoveryCause.INCONSISTENT_WITH_GROUND_TRUTH_VALUE;
+ int nativeSchemaStoreRecoveryCause =
+ InitializeStatsProto.RecoveryCause.TOTAL_CHECKSUM_MISMATCH_VALUE;
+ int nativeDocumentStoreRecoveryLatencyMillis = 7;
+ int nativeIndexRestorationLatencyMillis = 8;
+ int nativeSchemaStoreRecoveryLatencyMillis = 9;
+ int nativeDocumentStoreDataStatus =
+ InitializeStatsProto.DocumentStoreDataStatus.NO_DATA_LOSS_VALUE;
+ int nativeNumDocuments = 11;
+ int nativeNumSchemaTypes = 12;
+ InitializeStatsProto.Builder nativeInitBuilder =
+ InitializeStatsProto.newBuilder()
+ .setLatencyMs(nativeLatencyMillis)
+ .setDocumentStoreRecoveryCause(
+ InitializeStatsProto.RecoveryCause.forNumber(
+ nativeDocumentStoreRecoveryCause))
+ .setIndexRestorationCause(
+ InitializeStatsProto.RecoveryCause.forNumber(
+ nativeIndexRestorationCause))
+ .setSchemaStoreRecoveryCause(
+ InitializeStatsProto.RecoveryCause.forNumber(
+ nativeSchemaStoreRecoveryCause))
+ .setDocumentStoreRecoveryLatencyMs(nativeDocumentStoreRecoveryLatencyMillis)
+ .setIndexRestorationLatencyMs(nativeIndexRestorationLatencyMillis)
+ .setSchemaStoreRecoveryLatencyMs(nativeSchemaStoreRecoveryLatencyMillis)
+ .setDocumentStoreDataStatus(
+ InitializeStatsProto.DocumentStoreDataStatus.forNumber(
+ nativeDocumentStoreDataStatus))
+ .setNumDocuments(nativeNumDocuments)
+ .setNumSchemaTypes(nativeNumSchemaTypes);
+ InitializeStats.Builder initBuilder = new InitializeStats.Builder();
+
+ AppSearchLoggerHelper.copyNativeStats(nativeInitBuilder.build(), initBuilder);
+
+ InitializeStats iStats = initBuilder.build();
+ assertThat(iStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
+ assertThat(iStats.getDocumentStoreRecoveryCause())
+ .isEqualTo(nativeDocumentStoreRecoveryCause);
+ assertThat(iStats.getIndexRestorationCause()).isEqualTo(nativeIndexRestorationCause);
+ assertThat(iStats.getSchemaStoreRecoveryCause()).isEqualTo(nativeSchemaStoreRecoveryCause);
+ assertThat(iStats.getDocumentStoreRecoveryLatencyMillis())
+ .isEqualTo(nativeDocumentStoreRecoveryLatencyMillis);
+ assertThat(iStats.getIndexRestorationLatencyMillis())
+ .isEqualTo(nativeIndexRestorationLatencyMillis);
+ assertThat(iStats.getSchemaStoreRecoveryLatencyMillis())
+ .isEqualTo(nativeSchemaStoreRecoveryLatencyMillis);
+ assertThat(iStats.getDocumentStoreDataStatus()).isEqualTo(nativeDocumentStoreDataStatus);
+ assertThat(iStats.getDocumentCount()).isEqualTo(nativeNumDocuments);
+ assertThat(iStats.getSchemaTypeCount()).isEqualTo(nativeNumSchemaTypes);
}
@Test
@@ -111,10 +189,97 @@
assertThat(pStats.getNativeExceededMaxNumTokens()).isEqualTo(nativeExceededMaxNumTokens);
}
+ @Test
+ public void testAppSearchLoggerHelper_testCopyNativeStats_search() {
+ int nativeLatencyMillis = 4;
+ int nativeNumTerms = 5;
+ // TODO(b/185804196) query length needs to be added in the native stats.
+ // int nativeQueryLength = 6;
+ int nativeNumNamespacesFiltered = 7;
+ int nativeNumSchemaTypesFiltered = 8;
+ int nativeRequestedPageSize = 9;
+ int nativeNumResultsReturnedCurrentPage = 10;
+ boolean nativeIsFirstPage = true;
+ int nativeParseQueryLatencyMillis = 11;
+ int nativeRankingStrategy = ScoringSpecProto.RankingStrategy.Code.CREATION_TIMESTAMP_VALUE;
+ int nativeNumDocumentsScored = 13;
+ int nativeScoringLatencyMillis = 14;
+ int nativeRankingLatencyMillis = 15;
+ int nativeNumResultsWithSnippets = 16;
+ int nativeDocumentRetrievingLatencyMillis = 17;
+ QueryStatsProto nativeQueryStats =
+ QueryStatsProto.newBuilder()
+ .setLatencyMs(nativeLatencyMillis)
+ .setNumTerms(nativeNumTerms)
+ .setNumNamespacesFiltered(nativeNumNamespacesFiltered)
+ .setNumSchemaTypesFiltered(nativeNumSchemaTypesFiltered)
+ .setRequestedPageSize(nativeRequestedPageSize)
+ .setNumResultsReturnedCurrentPage(nativeNumResultsReturnedCurrentPage)
+ .setIsFirstPage(nativeIsFirstPage)
+ .setParseQueryLatencyMs(nativeParseQueryLatencyMillis)
+ .setRankingStrategy(
+ ScoringSpecProto.RankingStrategy.Code.forNumber(
+ nativeRankingStrategy))
+ .setNumDocumentsScored(nativeNumDocumentsScored)
+ .setScoringLatencyMs(nativeScoringLatencyMillis)
+ .setRankingLatencyMs(nativeRankingLatencyMillis)
+ .setNumResultsWithSnippets(nativeNumResultsWithSnippets)
+ .setDocumentRetrievalLatencyMs(nativeDocumentRetrievingLatencyMillis)
+ .build();
+ SearchStats.Builder qBuilder =
+ new SearchStats.Builder(SearchStats.VISIBILITY_SCOPE_LOCAL, "packageName")
+ .setDatabase("database");
+
+ AppSearchLoggerHelper.copyNativeStats(nativeQueryStats, qBuilder);
+
+ SearchStats sStats = qBuilder.build();
+ assertThat(sStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
+ assertThat(sStats.getTermCount()).isEqualTo(nativeNumTerms);
+ // assertThat(sStats.getNativeQueryLength()).isEqualTo(nativeQueryLength);
+ assertThat(sStats.getFilteredNamespaceCount()).isEqualTo(nativeNumNamespacesFiltered);
+ assertThat(sStats.getFilteredSchemaTypeCount()).isEqualTo(nativeNumSchemaTypesFiltered);
+ assertThat(sStats.getRequestedPageSize()).isEqualTo(nativeRequestedPageSize);
+ assertThat(sStats.getCurrentPageReturnedResultCount())
+ .isEqualTo(nativeNumResultsReturnedCurrentPage);
+ assertThat(sStats.isFirstPage()).isTrue();
+ assertThat(sStats.getParseQueryLatencyMillis()).isEqualTo(nativeParseQueryLatencyMillis);
+ assertThat(sStats.getRankingStrategy()).isEqualTo(nativeRankingStrategy);
+ assertThat(sStats.getScoredDocumentCount()).isEqualTo(nativeNumDocumentsScored);
+ assertThat(sStats.getScoringLatencyMillis()).isEqualTo(nativeScoringLatencyMillis);
+ assertThat(sStats.getRankingLatencyMillis()).isEqualTo(nativeRankingLatencyMillis);
+ assertThat(sStats.getResultWithSnippetsCount()).isEqualTo(nativeNumResultsWithSnippets);
+ assertThat(sStats.getDocumentRetrievingLatencyMillis())
+ .isEqualTo(nativeDocumentRetrievingLatencyMillis);
+ }
+
//
// Testing actual logging
//
@Test
+ public void testLoggingStats_initialize() throws Exception {
+ Context context = ApplicationProvider.getApplicationContext();
+
+ AppSearchImpl appSearchImpl =
+ AppSearchImpl.create(
+ mTemporaryFolder.newFolder(),
+ context,
+ VisibilityStore.NO_OP_USER_ID,
+ /*globalQuerierPackage=*/ context.getPackageName(),
+ mLogger);
+
+ InitializeStats iStats = mLogger.mInitializeStats;
+ assertThat(iStats).isNotNull();
+ assertThat(iStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
+ assertThat(iStats.getTotalLatencyMillis()).isGreaterThan(0);
+ assertThat(iStats.hasDeSync()).isFalse();
+ assertThat(iStats.getNativeLatencyMillis()).isGreaterThan(0);
+ assertThat(iStats.getDocumentStoreDataStatus())
+ .isEqualTo(InitializeStatsProto.DocumentStoreDataStatus.NO_DATA_LOSS_VALUE);
+ assertThat(iStats.getDocumentCount()).isEqualTo(0);
+ assertThat(iStats.getSchemaTypeCount()).isEqualTo(0);
+ }
+
+ @Test
public void testLoggingStats_putDocument() throws Exception {
// Insert schema
final String testPackageName = "testPackage";
@@ -141,4 +306,53 @@
// The rest of native stats have been tested in testCopyNativeStats
assertThat(pStats.getNativeDocumentSizeBytes()).isGreaterThan(0);
}
+
+ @Test
+ public void testLoggingStats_search() throws Exception {
+ // Insert schema
+ final String testPackageName = "testPackage";
+ final String testDatabase = "testDatabase";
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ mAppSearchImpl.setSchema(
+ testPackageName,
+ testDatabase,
+ schemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+ GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build();
+ mAppSearchImpl.putDocument(testPackageName, testDatabase, document, mLogger);
+
+ // No query filters specified. package2 should only get its own documents back.
+ SearchSpec searchSpec =
+ new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
+ SearchResultPage searchResultPage =
+ mAppSearchImpl.query(
+ testPackageName,
+ testDatabase,
+ /*QueryExpression=*/ "",
+ searchSpec,
+ /*logger=*/ mLogger);
+
+ assertThat(searchResultPage.getResults()).hasSize(1);
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
+
+ SearchStats sStats = mLogger.mSearchStats;
+
+ assertThat(sStats).isNotNull();
+ assertThat(sStats.getPackageName()).isEqualTo(testPackageName);
+ assertThat(sStats.getDatabase()).isEqualTo(testDatabase);
+ assertThat(sStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
+ assertThat(sStats.getTotalLatencyMillis()).isGreaterThan(0);
+ assertThat(sStats.getVisibilityScope()).isEqualTo(SearchStats.VISIBILITY_SCOPE_LOCAL);
+ assertThat(sStats.getTermCount()).isEqualTo(0);
+ // assertThat(sStats.getNativeQueryLength()).isEqualTo(0);
+ assertThat(sStats.getFilteredNamespaceCount()).isEqualTo(1);
+ assertThat(sStats.getFilteredSchemaTypeCount()).isEqualTo(1);
+ assertThat(sStats.getCurrentPageReturnedResultCount()).isEqualTo(1);
+ assertThat(sStats.isFirstPage()).isTrue();
+ assertThat(sStats.getScoredDocumentCount()).isEqualTo(1);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
index 8dbf249..5c7ccfc 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
@@ -134,4 +134,135 @@
assertThat(pStats.getNativeNumTokensIndexed()).isEqualTo(nativeNumTokensIndexed);
assertThat(pStats.getNativeExceededMaxNumTokens()).isEqualTo(nativeExceededMaxNumTokens);
}
+
+ @Test
+ public void testAppSearchStats_InitializeStats() {
+ int prepareSchemaAndNamespacesLatencyMillis = 1;
+ int prepareVisibilityFileLatencyMillis = 2;
+ int nativeLatencyMillis = 3;
+ int nativeDocumentStoreRecoveryCause = 4;
+ int nativeIndexRestorationCause = 5;
+ int nativeSchemaStoreRecoveryCause = 6;
+ int nativeDocumentStoreRecoveryLatencyMillis = 7;
+ int nativeIndexRestorationLatencyMillis = 8;
+ int nativeSchemaStoreRecoveryLatencyMillis = 9;
+ int nativeDocumentStoreDataStatus = 10;
+ int nativeNumDocuments = 11;
+ int nativeNumSchemaTypes = 12;
+
+ final InitializeStats.Builder iStatsBuilder =
+ new InitializeStats.Builder()
+ .setStatusCode(TEST_STATUS_CODE)
+ .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
+ .setHasDeSync(/* hasDeSyncs= */ true)
+ .setPrepareSchemaAndNamespacesLatencyMillis(
+ prepareSchemaAndNamespacesLatencyMillis)
+ .setPrepareVisibilityStoreLatencyMillis(prepareVisibilityFileLatencyMillis)
+ .setNativeLatencyMillis(nativeLatencyMillis)
+ .setDocumentStoreRecoveryCause(nativeDocumentStoreRecoveryCause)
+ .setIndexRestorationCause(nativeIndexRestorationCause)
+ .setSchemaStoreRecoveryCause(nativeSchemaStoreRecoveryCause)
+ .setDocumentStoreRecoveryLatencyMillis(
+ nativeDocumentStoreRecoveryLatencyMillis)
+ .setIndexRestorationLatencyMillis(nativeIndexRestorationLatencyMillis)
+ .setSchemaStoreRecoveryLatencyMillis(nativeSchemaStoreRecoveryLatencyMillis)
+ .setDocumentStoreDataStatus(nativeDocumentStoreDataStatus)
+ .setDocumentCount(nativeNumDocuments)
+ .setSchemaTypeCount(nativeNumSchemaTypes);
+ final InitializeStats iStats = iStatsBuilder.build();
+
+ assertThat(iStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
+ assertThat(iStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
+ assertThat(iStats.hasDeSync()).isTrue();
+ assertThat(iStats.getPrepareSchemaAndNamespacesLatencyMillis())
+ .isEqualTo(prepareSchemaAndNamespacesLatencyMillis);
+ assertThat(iStats.getPrepareVisibilityStoreLatencyMillis())
+ .isEqualTo(prepareVisibilityFileLatencyMillis);
+ assertThat(iStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
+ assertThat(iStats.getDocumentStoreRecoveryCause())
+ .isEqualTo(nativeDocumentStoreRecoveryCause);
+ assertThat(iStats.getIndexRestorationCause()).isEqualTo(nativeIndexRestorationCause);
+ assertThat(iStats.getSchemaStoreRecoveryCause()).isEqualTo(nativeSchemaStoreRecoveryCause);
+ assertThat(iStats.getDocumentStoreRecoveryLatencyMillis())
+ .isEqualTo(nativeDocumentStoreRecoveryLatencyMillis);
+ assertThat(iStats.getIndexRestorationLatencyMillis())
+ .isEqualTo(nativeIndexRestorationLatencyMillis);
+ assertThat(iStats.getSchemaStoreRecoveryLatencyMillis())
+ .isEqualTo(nativeSchemaStoreRecoveryLatencyMillis);
+ assertThat(iStats.getDocumentStoreDataStatus()).isEqualTo(nativeDocumentStoreDataStatus);
+ assertThat(iStats.getDocumentCount()).isEqualTo(nativeNumDocuments);
+ assertThat(iStats.getSchemaTypeCount()).isEqualTo(nativeNumSchemaTypes);
+ }
+
+ @Test
+ public void testAppSearchStats_SearchStats() {
+ int rewriteSearchSpecLatencyMillis = 1;
+ int rewriteSearchResultLatencyMillis = 2;
+ int visibilityScope = SearchStats.VISIBILITY_SCOPE_LOCAL;
+ int nativeLatencyMillis = 4;
+ int nativeNumTerms = 5;
+ int nativeQueryLength = 6;
+ int nativeNumNamespacesFiltered = 7;
+ int nativeNumSchemaTypesFiltered = 8;
+ int nativeRequestedPageSize = 9;
+ int nativeNumResultsReturnedCurrentPage = 10;
+ boolean nativeIsFirstPage = true;
+ int nativeParseQueryLatencyMillis = 11;
+ int nativeRankingStrategy = 12;
+ int nativeNumDocumentsScored = 13;
+ int nativeScoringLatencyMillis = 14;
+ int nativeRankingLatencyMillis = 15;
+ int nativeNumResultsSnippeted = 16;
+ int nativeDocumentRetrievingLatencyMillis = 17;
+ final SearchStats.Builder sStatsBuilder =
+ new SearchStats.Builder(visibilityScope, TEST_PACKAGE_NAME)
+ .setDatabase(TEST_DATA_BASE)
+ .setStatusCode(TEST_STATUS_CODE)
+ .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
+ .setRewriteSearchSpecLatencyMillis(rewriteSearchSpecLatencyMillis)
+ .setRewriteSearchResultLatencyMillis(rewriteSearchResultLatencyMillis)
+ .setNativeLatencyMillis(nativeLatencyMillis)
+ .setTermCount(nativeNumTerms)
+ .setQueryLength(nativeQueryLength)
+ .setFilteredNamespaceCount(nativeNumNamespacesFiltered)
+ .setFilteredSchemaTypeCount(nativeNumSchemaTypesFiltered)
+ .setRequestedPageSize(nativeRequestedPageSize)
+ .setCurrentPageReturnedResultCount(nativeNumResultsReturnedCurrentPage)
+ .setIsFirstPage(nativeIsFirstPage)
+ .setParseQueryLatencyMillis(nativeParseQueryLatencyMillis)
+ .setRankingStrategy(nativeRankingStrategy)
+ .setScoredDocumentCount(nativeNumDocumentsScored)
+ .setScoringLatencyMillis(nativeScoringLatencyMillis)
+ .setRankingLatencyMillis(nativeRankingLatencyMillis)
+ .setResultWithSnippetsCount(nativeNumResultsSnippeted)
+ .setDocumentRetrievingLatencyMillis(nativeDocumentRetrievingLatencyMillis);
+ final SearchStats sStats = sStatsBuilder.build();
+
+ assertThat(sStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+ assertThat(sStats.getDatabase()).isEqualTo(TEST_DATA_BASE);
+ assertThat(sStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
+ assertThat(sStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
+ assertThat(sStats.getRewriteSearchSpecLatencyMillis())
+ .isEqualTo(rewriteSearchSpecLatencyMillis);
+ assertThat(sStats.getRewriteSearchResultLatencyMillis())
+ .isEqualTo(rewriteSearchResultLatencyMillis);
+ assertThat(sStats.getVisibilityScope()).isEqualTo(visibilityScope);
+ assertThat(sStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
+ assertThat(sStats.getTermCount()).isEqualTo(nativeNumTerms);
+ assertThat(sStats.getQueryLength()).isEqualTo(nativeQueryLength);
+ assertThat(sStats.getFilteredNamespaceCount()).isEqualTo(nativeNumNamespacesFiltered);
+ assertThat(sStats.getFilteredSchemaTypeCount()).isEqualTo(nativeNumSchemaTypesFiltered);
+ assertThat(sStats.getRequestedPageSize()).isEqualTo(nativeRequestedPageSize);
+ assertThat(sStats.getCurrentPageReturnedResultCount())
+ .isEqualTo(nativeNumResultsReturnedCurrentPage);
+ assertThat(sStats.isFirstPage()).isTrue();
+ assertThat(sStats.getParseQueryLatencyMillis()).isEqualTo(nativeParseQueryLatencyMillis);
+ assertThat(sStats.getRankingStrategy()).isEqualTo(nativeRankingStrategy);
+ assertThat(sStats.getScoredDocumentCount()).isEqualTo(nativeNumDocumentsScored);
+ assertThat(sStats.getScoringLatencyMillis()).isEqualTo(nativeScoringLatencyMillis);
+ assertThat(sStats.getRankingLatencyMillis()).isEqualTo(nativeRankingLatencyMillis);
+ assertThat(sStats.getResultWithSnippetsCount()).isEqualTo(nativeNumResultsSnippeted);
+ assertThat(sStats.getDocumentRetrievingLatencyMillis())
+ .isEqualTo(nativeDocumentRetrievingLatencyMillis);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 81570a1..fe0df58 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -253,6 +253,8 @@
doReturn(new String[] {admin.getPackageName()}).when(mServices.ipackageManager)
.getPackagesForUid(eq(packageUid));
+ doReturn(new String[] {admin.getPackageName()}).when(mServices.packageManager)
+ .getPackagesForUid(eq(packageUid));
// Set up getPackageInfo().
markPackageAsInstalled(admin.getPackageName(), ai, UserHandle.getUserId(packageUid));
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index c16e498..ec5228f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -108,6 +108,7 @@
import com.android.server.pm.ShortcutUser.PackageWithUser;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -416,8 +417,11 @@
mManager.pushDynamicShortcut(s1);
assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()), "s1");
assertEquals(0, getCallerShortcut("s1").getRank());
+ verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+ eq(CALLING_PACKAGE_1), eq("s1"), eq(USER_0));
// Test push when other shortcuts exist
+ Mockito.reset(mMockUsageStatsManagerInternal);
assertTrue(mManager.setDynamicShortcuts(list(s1, s2)));
assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()), "s1", "s2");
mManager.pushDynamicShortcut(s3);
@@ -426,25 +430,38 @@
assertEquals(0, getCallerShortcut("s3").getRank());
assertEquals(1, getCallerShortcut("s1").getRank());
assertEquals(2, getCallerShortcut("s2").getRank());
+ verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage(
+ eq(CALLING_PACKAGE_1), eq("s1"), eq(USER_0));
+ verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage(
+ eq(CALLING_PACKAGE_1), eq("s2"), eq(USER_0));
+ verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+ eq(CALLING_PACKAGE_1), eq("s3"), eq(USER_0));
mInjectedCurrentTimeMillis += INTERVAL; // reset
// Push with set rank
+ Mockito.reset(mMockUsageStatsManagerInternal);
s4.setRank(2);
mManager.pushDynamicShortcut(s4);
assertEquals(2, getCallerShortcut("s4").getRank());
assertEquals(3, getCallerShortcut("s2").getRank());
+ verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+ eq(CALLING_PACKAGE_1), eq("s4"), eq(USER_0));
// Push existing shortcut with set rank
+ Mockito.reset(mMockUsageStatsManagerInternal);
final ShortcutInfo s4_2 = makeShortcut("s4");
s4_2.setRank(4);
mManager.pushDynamicShortcut(s4_2);
assertEquals(2, getCallerShortcut("s2").getRank());
assertEquals(3, getCallerShortcut("s4").getRank());
+ verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+ eq(CALLING_PACKAGE_1), eq("s4"), eq(USER_0));
mInjectedCurrentTimeMillis += INTERVAL; // reset
// Test push as last
+ Mockito.reset(mMockUsageStatsManagerInternal);
mManager.pushDynamicShortcut(s5);
assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()),
"s1", "s2", "s3", "s4", "s5");
@@ -453,25 +470,34 @@
assertEquals(2, getCallerShortcut("s1").getRank());
assertEquals(3, getCallerShortcut("s2").getRank());
assertEquals(4, getCallerShortcut("s4").getRank());
+ verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+ eq(CALLING_PACKAGE_1), eq("s5"), eq(USER_0));
// Push when max has already reached
+ Mockito.reset(mMockUsageStatsManagerInternal);
mManager.pushDynamicShortcut(s6);
assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()),
"s1", "s2", "s3", "s5", "s6");
assertEquals(0, getCallerShortcut("s6").getRank());
assertEquals(1, getCallerShortcut("s5").getRank());
assertEquals(4, getCallerShortcut("s2").getRank());
+ verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+ eq(CALLING_PACKAGE_1), eq("s6"), eq(USER_0));
mInjectedCurrentTimeMillis += INTERVAL; // reset
// Push with different activity
+ Mockito.reset(mMockUsageStatsManagerInternal);
s7.setActivity(makeComponent(ShortcutActivity2.class));
mManager.pushDynamicShortcut(s7);
assertEquals(makeComponent(ShortcutActivity2.class),
getCallerShortcut("s7").getActivity());
assertEquals(0, getCallerShortcut("s7").getRank());
+ verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+ eq(CALLING_PACKAGE_1), eq("s7"), eq(USER_0));
// Push to update shortcut with different activity
+ Mockito.reset(mMockUsageStatsManagerInternal);
final ShortcutInfo s1_2 = makeShortcut("s1");
s1_2.setActivity(makeComponent(ShortcutActivity2.class));
s1_2.setRank(1);
@@ -482,10 +508,13 @@
assertEquals(1, getCallerShortcut("s5").getRank());
assertEquals(2, getCallerShortcut("s3").getRank());
assertEquals(3, getCallerShortcut("s2").getRank());
+ verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+ eq(CALLING_PACKAGE_1), eq("s1"), eq(USER_0));
mInjectedCurrentTimeMillis += INTERVAL; // reset
// Test push when dropped shortcut is cached
+ Mockito.reset(mMockUsageStatsManagerInternal);
s8.setLongLived();
s8.setRank(100);
mManager.pushDynamicShortcut(s8);
@@ -494,14 +523,19 @@
mInjectCheckAccessShortcutsPermission = true;
mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s8"), HANDLE_USER_0,
CACHE_OWNER_0);
+ verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+ eq(CALLING_PACKAGE_1), eq("s8"), eq(USER_0));
});
+ Mockito.reset(mMockUsageStatsManagerInternal);
mManager.pushDynamicShortcut(s9);
assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()),
"s1", "s2", "s3", "s5", "s6", "s7", "s9");
// Verify s13 stayed as cached
assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
"s8");
+ verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
+ eq(CALLING_PACKAGE_1), eq("s9"), eq(USER_0));
}
public void testUnlimitedCalls() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index ca77049..7241fa0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -2138,7 +2138,6 @@
mManager.reportShortcutUsed("s2");
verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
eq(CALLING_PACKAGE_1), eq("s2"), eq(USER_10));
-
});
runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
// Try with a different package.
@@ -2158,7 +2157,6 @@
mManager.reportShortcutUsed("s3");
verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
eq(CALLING_PACKAGE_2), eq("s3"), eq(USER_10));
-
});
}
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index d13687c..4d2d2f1 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -307,6 +307,56 @@
assertEquals(1, mIntegerCaptor.getValue().intValue());
}
+ @Test
+ public void create_invalidSensor() throws Exception {
+ Sensor sensor = newSensor("sensor", Sensor.STRING_TYPE_HINGE_ANGLE);
+ when(mSensorManager.getSensorList(anyInt())).thenReturn(List.of());
+
+ String configString = "<device-state-config>\n"
+ + " <device-state>\n"
+ + " <identifier>1</identifier>\n"
+ + " <name>CLOSED</name>\n"
+ + " <conditions>\n"
+ + " <sensor>\n"
+ + " <type>" + sensor.getStringType() + "</type>\n"
+ + " <name>" + sensor.getName() + "</name>\n"
+ + " <value>\n"
+ + " <max>90</max>\n"
+ + " </value>\n"
+ + " </sensor>\n"
+ + " </conditions>\n"
+ + " </device-state>\n"
+ + " <device-state>\n"
+ + " <identifier>2</identifier>\n"
+ + " <name>HALF_OPENED</name>\n"
+ + " <conditions>\n"
+ + " <sensor>\n"
+ + " <type>" + sensor.getStringType() + "</type>\n"
+ + " <name>" + sensor.getName() + "</name>\n"
+ + " <value>\n"
+ + " <min-inclusive>90</min-inclusive>\n"
+ + " <max>180</max>\n"
+ + " </value>\n"
+ + " </sensor>\n"
+ + " </conditions>\n"
+ + " </device-state>\n"
+ + "</device-state-config>\n";
+ DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
+ DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext,
+ config);
+
+ DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
+ provider.setListener(listener);
+
+ verify(listener).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
+ assertArrayEquals(
+ new DeviceState[]{ new DeviceState(1, "CLOSED"), new DeviceState(2, "HALF_OPENED"),
+ }, mDeviceStateArrayCaptor.getValue());
+ // onStateChanged() should be called because the provider could not find the sensor.
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(1, mIntegerCaptor.getValue().intValue());
+ }
+
private static Sensor newSensor(String name, String type) throws Exception {
Constructor<Sensor> constructor = Sensor.class.getDeclaredConstructor();
constructor.setAccessible(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index e1eef76..e09606e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -54,6 +54,9 @@
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -1909,6 +1912,32 @@
verify(t).show(mDisplayContent.mImeScreenshot);
}
+ @UseTestDisplay(addWindows = {W_INPUT_METHOD}, addAllCommonWindows = true)
+ @Test
+ public void testShowImeScreenshot() {
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
+ task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
+ doReturn(true).when(task).okToAnimate();
+ ArrayList<WindowContainer> sources = new ArrayList<>();
+ sources.add(activity);
+
+ mDisplayContent.setImeLayeringTarget(win);
+ spyOn(mDisplayContent);
+
+ // Expecting the IME screenshot only be attached when performing task closing transition.
+ task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */,
+ false /* isVoiceInteraction */, sources);
+ verify(mDisplayContent).showImeScreenshot();
+
+ clearInvocations(mDisplayContent);
+ activity.applyAnimation(null, TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE, false /* enter */,
+ false /* isVoiceInteraction */, sources);
+ verify(mDisplayContent, never()).showImeScreenshot();
+ }
+
@Test
public void testRotateBounds_keepSamePhysicalPosition() {
final DisplayContent dc =
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 9289ce4..67b273a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -101,6 +101,29 @@
}
@Test
+ public void getLaunchRootTask_checksFocusedRootTask() {
+ final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
+ final Task rootTask = createTaskWithActivity(
+ taskDisplayArea,
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, ON_TOP, true);
+ rootTask.mCreatedByOrganizer = true;
+
+ final Task adjacentRootTask = createTask(
+ mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+ adjacentRootTask.mCreatedByOrganizer = true;
+ adjacentRootTask.mAdjacentTask = rootTask;
+ rootTask.mAdjacentTask = adjacentRootTask;
+
+ taskDisplayArea.setLaunchRootTask(rootTask,
+ new int[]{WINDOWING_MODE_MULTI_WINDOW}, new int[]{ACTIVITY_TYPE_STANDARD});
+
+ Task actualRootTask = taskDisplayArea.getLaunchRootTask(
+ WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, null /* options */,
+ null /* sourceTask */, 0 /*launchFlags*/);
+ assertTrue(actualRootTask.isFocusedRootTaskOnDisplay());
+ }
+
+ @Test
public void getLaunchRootTask_fromLaunchAdjacentFlagRoot_checksAdjacentRoot() {
final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
final Task rootTask = createTask(
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 2389d2d..13ef998 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -80,6 +80,7 @@
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
+import android.view.Display;
import android.view.DisplayInfo;
import androidx.test.filters.MediumTest;
@@ -1338,6 +1339,16 @@
verify(display).onDescendantOrientationChanged(same(task));
}
+ @Test
+ public void testGetNonNullDimmerOnUntrustedDisplays() {
+ final DisplayInfo untrustedDisplayInfo = new DisplayInfo(mDisplayInfo);
+ untrustedDisplayInfo.flags &= ~Display.FLAG_TRUSTED;
+ final DisplayContent untrustedDisplay = createNewDisplay(untrustedDisplayInfo);
+ final ActivityRecord activity = createActivityRecord(untrustedDisplay);
+ activity.setOccludesParent(false);
+ assertNotNull(activity.getTask().getDimmer());
+ }
+
private Task getTestTask() {
final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
return task.getBottomMostTask();
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index d8bd6a5..c5fc436 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -3448,4 +3448,13 @@
public Handler getHandler() {
return mHandler;
}
+
+ /**
+ * Sets this {@link ConnectionService} ready for testing purposes.
+ * @hide
+ */
+ @VisibleForTesting
+ public void setReadyForTest() {
+ mAreAccountsInitialized = true;
+ }
}
diff --git a/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java b/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java
index 179248d..33b0bbd2 100644
--- a/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java
+++ b/telephony/common/com/android/internal/telephony/SipMessageParsingUtils.java
@@ -81,6 +81,15 @@
}
/**
+ * @return true if the SIP message start line is considered a response.
+ */
+ public static boolean isSipResponse(String startLine) {
+ String[] splitLine = splitStartLineAndVerify(startLine);
+ if (splitLine == null) return false;
+ return verifySipResponse(splitLine);
+ }
+
+ /**
* Return the via branch parameter, which is used to identify the transaction ID (request and
* response pair) in a SIP transaction.
* @param headerString The string containing the headers of the SIP message.
@@ -140,7 +149,12 @@
return !headers.isEmpty() ? headers.get(0).second : null;
}
- private static String[] splitStartLineAndVerify(String startLine) {
+ /**
+ * Validate that the start line is correct and split into its three segments.
+ * @param startLine The start line to verify and split.
+ * @return The split start line, which will always have three segments.
+ */
+ public static String[] splitStartLineAndVerify(String startLine) {
String[] splitLine = startLine.split(" ");
if (isStartLineMalformed(splitLine)) return null;
return splitLine;
@@ -184,7 +198,7 @@
* (This is internally an equalsIgnoreMatch comparison).
* @return the matched header keys and values.
*/
- private static List<Pair<String, String>> parseHeaders(String headerString,
+ public static List<Pair<String, String>> parseHeaders(String headerString,
boolean stopAtFirstMatch, String... matchingHeaderKeys) {
// Ensure there is no leading whitespace
headerString = removeLeadingWhitespace(headerString);
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index 3b28616..8df41fb 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -183,16 +183,6 @@
}
/**
- * @return the absolute radio frequency channel number for this physical channel,
- * {@link #CHANNEL_NUMBER_UNKNOWN} if unknown.
- * @deprecated Use {@link #getDownlinkChannelNumber()} to get the channel number.
- */
- @Deprecated
- public int getChannelNumber() {
- return getDownlinkChannelNumber();
- }
-
- /**
* @return the rough frequency range for this physical channel,
* {@link ServiceState#FREQUENCY_RANGE_UNKNOWN} if unknown.
* @see {@link ServiceState#FREQUENCY_RANGE_LOW}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 05f5d29..179aead 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -15036,6 +15036,15 @@
"CAPABILITY_SLICING_CONFIG_SUPPORTED";
/**
+ * Indicates whether PHYSICAL_CHANNEL_CONFIG HAL1.6 is supported. See comments on
+ * respective methods for more information.
+ *
+ * @hide
+ */
+ public static final String CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED =
+ "CAPABILITY_PHYSICAL_CHANNEL_CONFIG_1_6_SUPPORTED";
+
+ /**
* A list of the radio interface capability values with public valid constants.
*
* Here is a related list for the systemapi-only valid constants:
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 08f5613..8b6f2b5 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1568,16 +1568,6 @@
}
/**
- * Same as {@link #getApnTypeString(int)}, but returns "Unknown" instead of an empty string
- * when provided with an invalid int for compatibility purposes.
- * @hide
- */
- public static @NonNull String getApnTypeStringInternal(@ApnType int apnType) {
- String result = getApnTypeString(apnType);
- return TextUtils.isEmpty(result) ? "Unknown" : result;
- }
-
- /**
* Converts the string representation of an APN type to its integer representation.
*
* @param apnType APN type as a string
diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
index ffbfde6..08513c2 100644
--- a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
+++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
@@ -26,6 +26,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
+import android.text.TextUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -561,7 +562,12 @@
builder.setSipCniHeader(getString(KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING));
builder.setSipAssociatedUriHeader(getString(KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING));
if (getBoolean(KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL, false)) {
- builder.setPublicGruuUri(Uri.parse(getString(KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING)));
+ String uri = getString(KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING);
+ Uri gruuUri = null;
+ if (!TextUtils.isEmpty(uri)) {
+ gruuUri = Uri.parse(uri);
+ }
+ builder.setPublicGruuUri(gruuUri);
}
if (getBoolean(KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL, false)) {
builder.setIpSecConfiguration(new SipDelegateConfiguration.IpSecConfiguration(
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index aa2e6b9..a504c9ab 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -81,13 +81,18 @@
public static final int MESSAGE_FAILURE_REASON_DELEGATE_CLOSED = 2;
/**
- * The SIP message has an invalid start line and the message can not be sent.
+ * The SIP message has an invalid start line and the message can not be sent or the start line
+ * failed validation due to the request containing a restricted SIP request method.
+ * {@link SipDelegateConnection}s can not send SIP requests for the methods: REGISTER, PUBLISH,
+ * or OPTIONS.
*/
public static final int MESSAGE_FAILURE_REASON_INVALID_START_LINE = 3;
/**
* One or more of the header fields in the header section of the outgoing SIP message is invalid
- * and the SIP message can not be sent.
+ * or contains a restricted header value and the SIP message can not be sent.
+ * {@link SipDelegateConnection}s can not send SIP SUBSCRIBE requests for the "Event" header
+ * value of "presence".
*/
public static final int MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS = 4;
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 9ac504b..96c7c0a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -116,7 +116,7 @@
}
}
- @Presubmit
+ @FlakyTest(bugId = 185400889)
@Test
open fun noUncoveredRegions() {
testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
@@ -134,7 +134,7 @@
testSpec.launcherWindowBecomesVisible()
}
- @Presubmit
+ @FlakyTest(bugId = 185400889)
@Test
open fun launcherLayerReplacesApp() {
testSpec.launcherLayerReplacesApp(testApp)
diff --git a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt b/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
index 87cfb34..f23ba26 100644
--- a/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
+++ b/tests/net/common/java/android/net/UnderlyingNetworkInfoTest.kt
@@ -36,15 +36,15 @@
@Test
fun testParcelUnparcel() {
val testInfo = UnderlyingNetworkInfo(TEST_OWNER_UID, TEST_IFACE, TEST_IFACE_LIST)
- assertEquals(TEST_OWNER_UID, testInfo.ownerUid)
- assertEquals(TEST_IFACE, testInfo.iface)
- assertEquals(TEST_IFACE_LIST, testInfo.underlyingIfaces)
+ assertEquals(TEST_OWNER_UID, testInfo.getOwnerUid())
+ assertEquals(TEST_IFACE, testInfo.getInterface())
+ assertEquals(TEST_IFACE_LIST, testInfo.getUnderlyingInterfaces())
assertParcelSane(testInfo, 3)
val emptyInfo = UnderlyingNetworkInfo(0, String(), listOf())
- assertEquals(0, emptyInfo.ownerUid)
- assertEquals(String(), emptyInfo.iface)
- assertEquals(listOf(), emptyInfo.underlyingIfaces)
+ assertEquals(0, emptyInfo.getOwnerUid())
+ assertEquals(String(), emptyInfo.getInterface())
+ assertEquals(listOf(), emptyInfo.getUnderlyingInterfaces())
assertParcelSane(emptyInfo, 3)
}
}
\ No newline at end of file
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index d966f70..f277e94 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -5898,9 +5898,9 @@
assertEquals("Should have exactly one VPN:", 1, infos.length);
UnderlyingNetworkInfo info = infos[0];
assertEquals("Unexpected VPN owner:", (int) vpnUid, info.getOwnerUid());
- assertEquals("Unexpected VPN interface:", vpnIfname, info.getIface());
+ assertEquals("Unexpected VPN interface:", vpnIfname, info.getInterface());
assertSameElementsNoDuplicates(underlyingIfaces,
- info.getUnderlyingIfaces().toArray(new String[0]));
+ info.getUnderlyingInterfaces().toArray(new String[0]));
} else {
assertEquals(0, infos.length);
return;
@@ -6044,8 +6044,8 @@
// network for the VPN...
verify(mStatsManager, never()).notifyNetworkStatus(any(List.class),
any(List.class), any() /* anyString() doesn't match null */,
- argThat(infos -> infos.get(0).getUnderlyingIfaces().size() == 1
- && WIFI_IFNAME.equals(infos.get(0).getUnderlyingIfaces().get(0))));
+ argThat(infos -> infos.get(0).getUnderlyingInterfaces().size() == 1
+ && WIFI_IFNAME.equals(infos.get(0).getUnderlyingInterfaces().get(0))));
verifyNoMoreInteractions(mStatsManager);
reset(mStatsManager);
@@ -6059,8 +6059,8 @@
waitForIdle();
verify(mStatsManager).notifyNetworkStatus(any(List.class),
any(List.class), any() /* anyString() doesn't match null */,
- argThat(vpnInfos -> vpnInfos.get(0).getUnderlyingIfaces().size() == 1
- && WIFI_IFNAME.equals(vpnInfos.get(0).getUnderlyingIfaces().get(0))));
+ argThat(vpnInfos -> vpnInfos.get(0).getUnderlyingInterfaces().size() == 1
+ && WIFI_IFNAME.equals(vpnInfos.get(0).getUnderlyingInterfaces().get(0))));
mEthernetNetworkAgent.disconnect();
waitForIdle();
reset(mStatsManager);
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 7cfd275..9410886 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -82,7 +82,7 @@
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
final VcnGatewayConnectionConfig.Builder builder =
- newBuilder().setRetryIntervalsMs(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU);
+ newBuilder().setRetryIntervalsMillis(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU);
for (int caps : exposedCaps) {
builder.addExposedCapability(caps);
@@ -134,7 +134,7 @@
@Test
public void testBuilderRequiresNonNullRetryInterval() {
try {
- newBuilder().setRetryIntervalsMs(null);
+ newBuilder().setRetryIntervalsMillis(null);
fail("Expected exception due to invalid retryIntervalMs");
} catch (IllegalArgumentException e) {
}
@@ -143,7 +143,7 @@
@Test
public void testBuilderRequiresNonEmptyRetryInterval() {
try {
- newBuilder().setRetryIntervalsMs(new long[0]);
+ newBuilder().setRetryIntervalsMillis(new long[0]);
fail("Expected exception due to invalid retryIntervalMs");
} catch (IllegalArgumentException e) {
}
@@ -174,7 +174,7 @@
assertEquals(TUNNEL_CONNECTION_PARAMS, config.getTunnelConnectionParams());
- assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMs());
+ assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis());
assertEquals(MAX_MTU, config.getMaxMtu());
}
diff --git a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
index 3156190..582275d 100644
--- a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
@@ -16,6 +16,8 @@
package android.net.vcn;
+import static android.net.NetworkCapabilities.REDACT_ALL;
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static org.junit.Assert.assertEquals;
@@ -37,6 +39,12 @@
private static final VcnTransportInfo WIFI_UNDERLYING_INFO = new VcnTransportInfo(WIFI_INFO);
@Test
+ public void testRedactionDefaults() {
+ assertEquals(REDACT_ALL, CELL_UNDERLYING_INFO.getRedaction());
+ assertEquals(REDACT_ALL, WIFI_UNDERLYING_INFO.getRedaction());
+ }
+
+ @Test
public void testGetWifiInfo() {
assertEquals(WIFI_INFO, WIFI_UNDERLYING_INFO.getWifiInfo());
@@ -51,6 +59,18 @@
}
@Test
+ public void testMakeCopySetsRedactions() {
+ assertEquals(
+ REDACT_FOR_NETWORK_SETTINGS,
+ ((VcnTransportInfo) CELL_UNDERLYING_INFO.makeCopy(REDACT_FOR_NETWORK_SETTINGS))
+ .getRedaction());
+ assertEquals(
+ REDACT_FOR_NETWORK_SETTINGS,
+ ((VcnTransportInfo) WIFI_UNDERLYING_INFO.makeCopy(REDACT_FOR_NETWORK_SETTINGS))
+ .getRedaction());
+ }
+
+ @Test
public void testEquals() {
assertEquals(CELL_UNDERLYING_INFO, CELL_UNDERLYING_INFO);
assertEquals(WIFI_UNDERLYING_INFO, WIFI_UNDERLYING_INFO);
@@ -64,8 +84,29 @@
}
private void verifyParcelingIsNull(VcnTransportInfo vcnTransportInfo) {
+ // Verify redacted by default
Parcel parcel = Parcel.obtain();
vcnTransportInfo.writeToParcel(parcel, 0 /* flags */);
+ parcel.setDataPosition(0);
+
assertNull(VcnTransportInfo.CREATOR.createFromParcel(parcel));
}
+
+ @Test
+ public void testParcelUnparcelNotRedactedForSysUi() {
+ verifyParcelingForSysUi(CELL_UNDERLYING_INFO);
+ verifyParcelingForSysUi(WIFI_UNDERLYING_INFO);
+ }
+
+ private void verifyParcelingForSysUi(VcnTransportInfo vcnTransportInfo) {
+ // Allow fully unredacted; SysUI will have all the relevant permissions.
+ final VcnTransportInfo unRedacted = (VcnTransportInfo) vcnTransportInfo.makeCopy(0);
+ final Parcel parcel = Parcel.obtain();
+ unRedacted.writeToParcel(parcel, 0 /* flags */);
+ parcel.setDataPosition(0);
+
+ final VcnTransportInfo unparceled = VcnTransportInfo.CREATOR.createFromParcel(parcel);
+ assertEquals(vcnTransportInfo, unparceled);
+ assertEquals(REDACT_ALL, unparceled.getRedaction());
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 90ee738..eedaac4 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -343,6 +343,31 @@
assertFalse(mGatewayConnection.isInSafeMode());
}
+ @Test
+ public void testSubsequentFailedValidationTriggersSafeMode() throws Exception {
+ triggerChildOpened();
+ mTestLooper.dispatchAll();
+
+ triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
+ assertFalse(mGatewayConnection.isInSafeMode());
+
+ // Trigger a failed validation, and the subsequent safemode timeout.
+ triggerValidation(NetworkAgent.VALIDATION_STATUS_NOT_VALID);
+ mTestLooper.dispatchAll();
+
+ final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mDeps, times(2))
+ .newWakeupMessage(
+ eq(mVcnContext),
+ any(),
+ eq(VcnGatewayConnection.SAFEMODE_TIMEOUT_ALARM),
+ runnableCaptor.capture());
+ runnableCaptor.getValue().run();
+ mTestLooper.dispatchAll();
+
+ assertTrue(mGatewayConnection.isInSafeMode());
+ }
+
private Consumer<VcnNetworkAgent> setupNetworkAndGetUnwantedCallback() {
triggerChildOpened();
mTestLooper.dispatchAll();
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
index 044bef5..a88f112 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
@@ -38,7 +38,7 @@
public void setUp() throws Exception {
super.setUp();
- mFirstRetryInterval = mConfig.getRetryIntervalsMs()[0];
+ mFirstRetryInterval = mConfig.getRetryIntervalsMillis()[0];
mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
mGatewayConnection.transitionTo(mGatewayConnection.mRetryTimeoutState);
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index b78f48c..6364ccd 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -78,6 +78,8 @@
return "interpolator";
case ResourceType::kLayout:
return "layout";
+ case ResourceType::kMacro:
+ return "macro";
case ResourceType::kMenu:
return "menu";
case ResourceType::kMipmap:
@@ -119,6 +121,7 @@
{"integer", ResourceType::kInteger},
{"interpolator", ResourceType::kInterpolator},
{"layout", ResourceType::kLayout},
+ {"macro", ResourceType::kMacro},
{"menu", ResourceType::kMenu},
{"mipmap", ResourceType::kMipmap},
{"navigation", ResourceType::kNavigation},
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index cf93870..307c21d 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -57,6 +57,7 @@
kInteger,
kInterpolator,
kLayout,
+ kMacro,
kMenu,
kMipmap,
kNavigation,
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 24c60b7..1efabbb 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -627,6 +627,16 @@
}
return true;
+ } else if (resource_type == "macro") {
+ if (!maybe_name) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<" << parser->element_name() << "> missing 'name' attribute");
+ return false;
+ }
+
+ out_resource->name.type = ResourceType::kMacro;
+ out_resource->name.entry = maybe_name.value().to_string();
+ return ParseMacro(parser, out_resource);
}
if (can_be_item) {
@@ -726,16 +736,8 @@
return true;
}
-/**
- * Reads the entire XML subtree and attempts to parse it as some Item,
- * with typeMask denoting which items it can be. If allowRawValue is
- * true, a RawString is returned if the XML couldn't be parsed as
- * an Item. If allowRawValue is false, nullptr is returned in this
- * case.
- */
-std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser,
- const uint32_t type_mask,
- const bool allow_raw_value) {
+std::optional<FlattenedXmlSubTree> ResourceParser::CreateFlattenSubTree(
+ xml::XmlPullParser* parser) {
const size_t begin_xml_line = parser->line_number();
std::string raw_value;
@@ -745,30 +747,60 @@
return {};
}
- if (!style_string.spans.empty()) {
+ return FlattenedXmlSubTree{.raw_value = raw_value,
+ .style_string = style_string,
+ .untranslatable_sections = untranslatable_sections,
+ .namespace_resolver = parser,
+ .source = source_.WithLine(begin_xml_line)};
+}
+
+/**
+ * Reads the entire XML subtree and attempts to parse it as some Item,
+ * with typeMask denoting which items it can be. If allowRawValue is
+ * true, a RawString is returned if the XML couldn't be parsed as
+ * an Item. If allowRawValue is false, nullptr is returned in this
+ * case.
+ */
+std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser, const uint32_t type_mask,
+ const bool allow_raw_value) {
+ auto sub_tree = CreateFlattenSubTree(parser);
+ if (!sub_tree.has_value()) {
+ return {};
+ }
+ return ParseXml(sub_tree.value(), type_mask, allow_raw_value, *table_, config_, *diag_);
+}
+
+std::unique_ptr<Item> ResourceParser::ParseXml(const FlattenedXmlSubTree& xmlsub_tree,
+ const uint32_t type_mask, const bool allow_raw_value,
+ ResourceTable& table,
+ const android::ConfigDescription& config,
+ IDiagnostics& diag) {
+ if (!xmlsub_tree.style_string.spans.empty()) {
// This can only be a StyledString.
std::unique_ptr<StyledString> styled_string =
- util::make_unique<StyledString>(table_->string_pool.MakeRef(
- style_string, StringPool::Context(StringPool::Context::kNormalPriority, config_)));
- styled_string->untranslatable_sections = std::move(untranslatable_sections);
+ util::make_unique<StyledString>(table.string_pool.MakeRef(
+ xmlsub_tree.style_string,
+ StringPool::Context(StringPool::Context::kNormalPriority, config)));
+ styled_string->untranslatable_sections = xmlsub_tree.untranslatable_sections;
return std::move(styled_string);
}
auto on_create_reference = [&](const ResourceName& name) {
// name.package can be empty here, as it will assume the package name of the
// table.
- std::unique_ptr<Id> id = util::make_unique<Id>();
- id->SetSource(source_.WithLine(begin_xml_line));
- table_->AddResource(NewResourceBuilder(name).SetValue(std::move(id)).Build(), diag_);
+ auto id = util::make_unique<Id>();
+ id->SetSource(xmlsub_tree.source);
+ return table.AddResource(NewResourceBuilder(name).SetValue(std::move(id)).Build(), &diag);
};
// Process the raw value.
- std::unique_ptr<Item> processed_item =
- ResourceUtils::TryParseItemForAttribute(raw_value, type_mask, on_create_reference);
+ std::unique_ptr<Item> processed_item = ResourceUtils::TryParseItemForAttribute(
+ xmlsub_tree.raw_value, type_mask, on_create_reference);
if (processed_item) {
// Fix up the reference.
- if (Reference* ref = ValueCast<Reference>(processed_item.get())) {
- ResolvePackage(parser, ref);
+ if (auto ref = ValueCast<Reference>(processed_item.get())) {
+ ref->allow_raw = allow_raw_value;
+ ResolvePackage(xmlsub_tree.namespace_resolver, ref);
}
return processed_item;
}
@@ -777,17 +809,16 @@
if (type_mask & android::ResTable_map::TYPE_STRING) {
// Use the trimmed, escaped string.
std::unique_ptr<String> string = util::make_unique<String>(
- table_->string_pool.MakeRef(style_string.str, StringPool::Context(config_)));
- string->untranslatable_sections = std::move(untranslatable_sections);
+ table.string_pool.MakeRef(xmlsub_tree.style_string.str, StringPool::Context(config)));
+ string->untranslatable_sections = xmlsub_tree.untranslatable_sections;
return std::move(string);
}
if (allow_raw_value) {
// We can't parse this so return a RawString if we are allowed.
- return util::make_unique<RawString>(
- table_->string_pool.MakeRef(util::TrimWhitespace(raw_value),
- StringPool::Context(config_)));
- } else if (util::TrimWhitespace(raw_value).empty()) {
+ return util::make_unique<RawString>(table.string_pool.MakeRef(
+ util::TrimWhitespace(xmlsub_tree.raw_value), StringPool::Context(config)));
+ } else if (util::TrimWhitespace(xmlsub_tree.raw_value).empty()) {
// If the text is empty, and the value is not allowed to be a string, encode it as a @null.
return ResourceUtils::MakeNull();
}
@@ -850,6 +881,35 @@
return true;
}
+bool ResourceParser::ParseMacro(xml::XmlPullParser* parser, ParsedResource* out_resource) {
+ auto sub_tree = CreateFlattenSubTree(parser);
+ if (!sub_tree) {
+ return false;
+ }
+
+ if (out_resource->config != ConfigDescription::DefaultConfig()) {
+ diag_->Error(DiagMessage(out_resource->source)
+ << "<macro> tags cannot be declared in configurations other than the default "
+ "configuration'");
+ return false;
+ }
+
+ auto macro = std::make_unique<Macro>();
+ macro->raw_value = std::move(sub_tree->raw_value);
+ macro->style_string = std::move(sub_tree->style_string);
+ macro->untranslatable_sections = std::move(sub_tree->untranslatable_sections);
+
+ for (const auto& decl : parser->package_decls()) {
+ macro->alias_namespaces.emplace_back(
+ Macro::Namespace{.alias = decl.prefix,
+ .package_name = decl.package.package,
+ .is_private = decl.package.private_namespace});
+ }
+
+ out_resource->value = std::move(macro);
+ return true;
+}
+
bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource) {
if (options_.visibility) {
diag_->Error(DiagMessage(out_resource->source)
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index af0db8c..5c92def 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -57,6 +57,14 @@
Maybe<Visibility::Level> visibility;
};
+struct FlattenedXmlSubTree {
+ std::string raw_value;
+ StyleString style_string;
+ std::vector<UntranslatableSection> untranslatable_sections;
+ xml::IPackageDeclStack* namespace_resolver;
+ Source source;
+};
+
/*
* Parses an XML file for resources and adds them to a ResourceTable.
*/
@@ -67,9 +75,16 @@
const ResourceParserOptions& options = {});
bool Parse(xml::XmlPullParser* parser);
+ static std::unique_ptr<Item> ParseXml(const FlattenedXmlSubTree& xmlsub_tree, uint32_t type_mask,
+ bool allow_raw_value, ResourceTable& table,
+ const android::ConfigDescription& config,
+ IDiagnostics& diag);
+
private:
DISALLOW_COPY_AND_ASSIGN(ResourceParser);
+ std::optional<FlattenedXmlSubTree> CreateFlattenSubTree(xml::XmlPullParser* parser);
+
// Parses the XML subtree as a StyleString (flattened XML representation for strings with
// formatting). If parsing fails, false is returned and the out parameters are left in an
// unspecified state. Otherwise,
@@ -96,7 +111,7 @@
bool ParseItem(xml::XmlPullParser* parser, ParsedResource* out_resource, uint32_t format);
bool ParseString(xml::XmlPullParser* parser, ParsedResource* out_resource);
-
+ bool ParseMacro(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseStagingPublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
@@ -108,8 +123,7 @@
bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, bool weak);
Maybe<Attribute::Symbol> ParseEnumOrFlagItem(xml::XmlPullParser* parser,
const android::StringPiece& tag);
- bool ParseStyle(const ResourceType type, xml::XmlPullParser* parser,
- ParsedResource* out_resource);
+ bool ParseStyle(ResourceType type, xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseStyleItem(xml::XmlPullParser* parser, Style* style);
bool ParseDeclareStyleable(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseArray(xml::XmlPullParser* parser, ParsedResource* out_resource);
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 4a509be..279ebcba 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -336,6 +336,90 @@
EXPECT_THAT(attr->type_mask, Eq(ResTable_map::TYPE_ANY));
}
+TEST_F(ResourceParserTest, ParseMacro) {
+ std::string input = R"(<macro name="foo">12345</macro>)";
+ ASSERT_TRUE(TestParse(input));
+
+ Macro* macro = test::GetValue<Macro>(&table_, "macro/foo");
+ ASSERT_THAT(macro, NotNull());
+ EXPECT_THAT(macro->raw_value, Eq("12345"));
+ EXPECT_THAT(macro->style_string.str, Eq("12345"));
+ EXPECT_THAT(macro->style_string.spans, IsEmpty());
+ EXPECT_THAT(macro->untranslatable_sections, IsEmpty());
+ EXPECT_THAT(macro->alias_namespaces, IsEmpty());
+}
+
+TEST_F(ResourceParserTest, ParseMacroUntranslatableSection) {
+ std::string input = R"(<macro name="foo" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+This being <b><xliff:g>human</xliff:g></b> is a guest house.</macro>)";
+ ASSERT_TRUE(TestParse(input));
+
+ Macro* macro = test::GetValue<Macro>(&table_, "macro/foo");
+ ASSERT_THAT(macro, NotNull());
+ EXPECT_THAT(macro->raw_value, Eq("\nThis being human is a guest house."));
+ EXPECT_THAT(macro->style_string.str, Eq(" This being human is a guest house."));
+ EXPECT_THAT(macro->style_string.spans.size(), Eq(1));
+ EXPECT_THAT(macro->style_string.spans[0].name, Eq("b"));
+ EXPECT_THAT(macro->style_string.spans[0].first_char, Eq(12));
+ EXPECT_THAT(macro->style_string.spans[0].last_char, Eq(16));
+ ASSERT_THAT(macro->untranslatable_sections.size(), Eq(1));
+ EXPECT_THAT(macro->untranslatable_sections[0].start, Eq(12));
+ EXPECT_THAT(macro->untranslatable_sections[0].end, Eq(17));
+ EXPECT_THAT(macro->alias_namespaces, IsEmpty());
+}
+
+TEST_F(ResourceParserTest, ParseMacroNamespaces) {
+ std::string input = R"(<macro name="foo" xmlns:app="http://schemas.android.com/apk/res/android">
+@app:string/foo</macro>)";
+ ASSERT_TRUE(TestParse(input));
+
+ Macro* macro = test::GetValue<Macro>(&table_, "macro/foo");
+ ASSERT_THAT(macro, NotNull());
+ EXPECT_THAT(macro->raw_value, Eq("\n@app:string/foo"));
+ EXPECT_THAT(macro->style_string.str, Eq("@app:string/foo"));
+ EXPECT_THAT(macro->style_string.spans, IsEmpty());
+ EXPECT_THAT(macro->untranslatable_sections, IsEmpty());
+ EXPECT_THAT(macro->alias_namespaces.size(), Eq(1));
+ EXPECT_THAT(macro->alias_namespaces[0].alias, Eq("app"));
+ EXPECT_THAT(macro->alias_namespaces[0].package_name, Eq("android"));
+ EXPECT_THAT(macro->alias_namespaces[0].is_private, Eq(false));
+}
+
+TEST_F(ResourceParserTest, ParseMacroReference) {
+ std::string input = R"(<string name="res_string">@macro/foo</string>)";
+ ASSERT_TRUE(TestParse(input));
+
+ Reference* macro = test::GetValue<Reference>(&table_, "string/res_string");
+ ASSERT_THAT(macro, NotNull());
+ EXPECT_THAT(macro->type_flags, Eq(ResTable_map::TYPE_STRING));
+ EXPECT_THAT(macro->allow_raw, Eq(false));
+
+ input = R"(<style name="foo">
+ <item name="bar">@macro/foo</item>
+ </style>)";
+
+ ASSERT_TRUE(TestParse(input));
+ Style* style = test::GetValue<Style>(&table_, "style/foo");
+ ASSERT_THAT(style, NotNull());
+ EXPECT_THAT(style->entries.size(), Eq(1));
+
+ macro = ValueCast<Reference>(style->entries[0].value.get());
+ ASSERT_THAT(macro, NotNull());
+ EXPECT_THAT(macro->type_flags, Eq(0U));
+ EXPECT_THAT(macro->allow_raw, Eq(true));
+}
+
+TEST_F(ResourceParserTest, ParseMacroNoNameFail) {
+ std::string input = R"(<macro>12345</macro>)";
+ ASSERT_FALSE(TestParse(input));
+}
+
+TEST_F(ResourceParserTest, ParseMacroNonDefaultConfigurationFail) {
+ const ConfigDescription watch_config = test::ParseConfigOrDie("watch");
+ std::string input = R"(<macro name="foo">12345</macro>)";
+ ASSERT_FALSE(TestParse(input, watch_config));
+}
+
// Old AAPT allowed attributes to be defined under different configurations, but ultimately
// stored them with the default configuration. Check that we have the same behavior.
TEST_F(ResourceParserTest, ParseAttrAndDeclareStyleableUnderConfigButRecordAsNoConfig) {
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 5b43df6..e0e80ac 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -628,7 +628,7 @@
std::unique_ptr<Item> TryParseItemForAttribute(
const StringPiece& value, uint32_t type_mask,
- const std::function<void(const ResourceName&)>& on_create_reference) {
+ const std::function<bool(const ResourceName&)>& on_create_reference) {
using android::ResTable_map;
auto null_or_empty = TryParseNullOrEmpty(value);
@@ -639,8 +639,11 @@
bool create = false;
auto reference = TryParseReference(value, &create);
if (reference) {
+ reference->type_flags = type_mask;
if (create && on_create_reference) {
- on_create_reference(reference->name.value());
+ if (!on_create_reference(reference->name.value())) {
+ return {};
+ }
}
return std::move(reference);
}
@@ -689,7 +692,7 @@
*/
std::unique_ptr<Item> TryParseItemForAttribute(
const StringPiece& str, const Attribute* attr,
- const std::function<void(const ResourceName&)>& on_create_reference) {
+ const std::function<bool(const ResourceName&)>& on_create_reference) {
using android::ResTable_map;
const uint32_t type_mask = attr->type_mask;
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index f77766e..be493db 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -204,11 +204,11 @@
*/
std::unique_ptr<Item> TryParseItemForAttribute(
const android::StringPiece& value, const Attribute* attr,
- const std::function<void(const ResourceName&)>& on_create_reference = {});
+ const std::function<bool(const ResourceName&)>& on_create_reference = {});
std::unique_ptr<Item> TryParseItemForAttribute(
const android::StringPiece& value, uint32_t type_mask,
- const std::function<void(const ResourceName&)>& on_create_reference = {});
+ const std::function<bool(const ResourceName&)>& on_create_reference = {});
uint32_t AndroidTypeToAttributeTypeMask(uint16_t type);
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 574bd2e..2a90f26 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -111,12 +111,15 @@
if (!other) {
return false;
}
- return reference_type == other->reference_type &&
- private_reference == other->private_reference && id == other->id &&
- name == other->name;
+ return reference_type == other->reference_type && private_reference == other->private_reference &&
+ id == other->id && name == other->name && type_flags == other->type_flags;
}
bool Reference::Flatten(android::Res_value* out_value) const {
+ if (name && name.value().type == ResourceType::kMacro) {
+ return false;
+ }
+
const ResourceId resid = id.value_or_default(ResourceId(0));
const bool dynamic = resid.is_valid() && is_dynamic;
@@ -551,7 +554,7 @@
return this_type_mask == that_type_mask;
}
-std::string Attribute::MaskString() const {
+std::string Attribute::MaskString(uint32_t type_mask) {
if (type_mask == android::ResTable_map::TYPE_ANY) {
return "any";
}
@@ -650,6 +653,10 @@
return out.str();
}
+std::string Attribute::MaskString() const {
+ return MaskString(type_mask);
+}
+
void Attribute::Print(std::ostream* out) const {
*out << "(attr) " << MaskString();
@@ -1017,6 +1024,21 @@
<< " [" << util::Joiner(entries, ", ") << "]";
}
+bool Macro::Equals(const Value* value) const {
+ const Macro* other = ValueCast<Macro>(value);
+ if (!other) {
+ return false;
+ }
+ return other->raw_value == raw_value && other->style_string.spans == style_string.spans &&
+ other->style_string.str == style_string.str &&
+ other->untranslatable_sections == untranslatable_sections &&
+ other->alias_namespaces == alias_namespaces;
+}
+
+void Macro::Print(std::ostream* out) const {
+ *out << "(macro) ";
+}
+
bool operator<(const Reference& a, const Reference& b) {
int cmp = a.name.value_or_default({}).compare(b.name.value_or_default({}));
if (cmp != 0) return cmp < 0;
@@ -1149,4 +1171,9 @@
return CopyValueFields(std::move(new_value), value);
}
+std::unique_ptr<Macro> CloningValueTransformer::TransformDerived(const Macro* value) {
+ auto new_value = std::make_unique<Macro>(*value);
+ return CopyValueFields(std::move(new_value), value);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 025864d..d11b013 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -164,6 +164,8 @@
Reference::Type reference_type;
bool private_reference = false;
bool is_dynamic = false;
+ std::optional<uint32_t> type_flags;
+ bool allow_raw;
Reference();
explicit Reference(const ResourceNameRef& n, Type type = Type::kResource);
@@ -311,6 +313,8 @@
bool IsCompatibleWith(const Attribute& attr) const;
std::string MaskString() const;
+ static std::string MaskString(uint32_t type_mask);
+
void Print(std::ostream* out) const override;
bool Matches(const Item& item, DiagMessage* out_msg = nullptr) const;
};
@@ -362,6 +366,28 @@
void MergeWith(Styleable* styleable);
};
+struct Macro : public TransformableValue<Macro, BaseValue<Macro>> {
+ std::string raw_value;
+ StyleString style_string;
+ std::vector<UntranslatableSection> untranslatable_sections;
+
+ struct Namespace {
+ std::string alias;
+ std::string package_name;
+ bool is_private;
+
+ bool operator==(const Namespace& right) const {
+ return alias == right.alias && package_name == right.package_name &&
+ is_private == right.is_private;
+ }
+ };
+
+ std::vector<Namespace> alias_namespaces;
+
+ bool Equals(const Value* value) const override;
+ void Print(std::ostream* out) const override;
+};
+
template <typename T>
typename std::enable_if<std::is_base_of<Value, T>::value, std::ostream&>::type operator<<(
std::ostream& out, const std::unique_ptr<T>& value) {
@@ -388,6 +414,7 @@
std::unique_ptr<Array> TransformDerived(const Array* value) override;
std::unique_ptr<Plural> TransformDerived(const Plural* value) override;
std::unique_ptr<Styleable> TransformDerived(const Styleable* value) override;
+ std::unique_ptr<Macro> TransformDerived(const Macro* value) override;
};
} // namespace aapt
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 4247ec5..b45c040 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -273,6 +273,7 @@
Styleable styleable = 3;
Array array = 4;
Plural plural = 5;
+ MacroBody macro = 6;
}
}
@@ -304,6 +305,13 @@
// Whether this reference is dynamic.
Boolean is_dynamic = 5;
+
+ // The type flags used when compiling the reference. Used for substituting the contents of macros.
+ uint32 type_flags = 6;
+
+ // Whether raw string values would have been accepted in place of this reference definition. Used
+ // for substituting the contents of macros.
+ bool allow_raw = 7;
}
// A value that represents an ID. This is just a placeholder, as ID values are used to occupy a
@@ -591,3 +599,32 @@
// The optional interpreted/compiled version of the `value` string.
Item compiled_item = 6;
}
+
+message MacroBody {
+ string raw_string = 1;
+ StyleString style_string = 2;
+ repeated UntranslatableSection untranslatable_sections = 3;
+ repeated NamespaceAlias namespace_stack = 4;
+ SourcePosition source = 5;
+}
+
+message NamespaceAlias {
+ string prefix = 1;
+ string package_name = 2;
+ bool is_private = 3;
+}
+
+message StyleString {
+ message Span {
+ string name = 1;
+ uint32 start_index = 2;
+ uint32 end_index = 3;
+ }
+ string str = 1;
+ repeated Span spans = 2;
+}
+
+message UntranslatableSection {
+ uint64 start_index = 1;
+ uint64 end_index = 2;
+}
\ No newline at end of file
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index 1006ca9..3457e0b 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -36,6 +36,10 @@
std::string name;
uint32_t first_char;
uint32_t last_char;
+
+ bool operator==(const Span& right) const {
+ return name == right.name && first_char == right.first_char && last_char == right.last_char;
+ }
};
struct StyleString {
diff --git a/tools/aapt2/ValueTransformer.cpp b/tools/aapt2/ValueTransformer.cpp
index 6eb2e30..2d7996b 100644
--- a/tools/aapt2/ValueTransformer.cpp
+++ b/tools/aapt2/ValueTransformer.cpp
@@ -46,5 +46,6 @@
VALUE_CREATE_VALUE_DECL(Array);
VALUE_CREATE_VALUE_DECL(Plural);
VALUE_CREATE_VALUE_DECL(Styleable);
+VALUE_CREATE_VALUE_DECL(Macro);
} // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/ValueTransformer.h b/tools/aapt2/ValueTransformer.h
index 6925111..6fc4a19 100644
--- a/tools/aapt2/ValueTransformer.h
+++ b/tools/aapt2/ValueTransformer.h
@@ -37,6 +37,7 @@
struct Array;
struct Plural;
struct Styleable;
+struct Macro;
#define AAPT_TRANSFORM_VALUE(T) \
virtual std::unique_ptr<T> TransformDerived(const T* value) = 0; \
@@ -97,6 +98,7 @@
AAPT_TRANSFORM_VALUE(Array);
AAPT_TRANSFORM_VALUE(Plural);
AAPT_TRANSFORM_VALUE(Styleable);
+ AAPT_TRANSFORM_VALUE(Macro);
protected:
StringPool* const pool_;
diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h
index 4e74ec3..d0c9d89 100644
--- a/tools/aapt2/ValueVisitor.h
+++ b/tools/aapt2/ValueVisitor.h
@@ -43,6 +43,9 @@
virtual void Visit(Array* value) { VisitAny(value); }
virtual void Visit(Plural* value) { VisitAny(value); }
virtual void Visit(Styleable* value) { VisitAny(value); }
+ virtual void Visit(Macro* value) {
+ VisitAny(value);
+ }
};
// Const version of ValueVisitor.
@@ -92,6 +95,9 @@
virtual void Visit(const Styleable* value) {
VisitAny(value);
}
+ virtual void Visit(const Macro* value) {
+ VisitAny(value);
+ }
};
// NOLINT, do not add parentheses around T.
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 2c57fb2..e4d0f3b 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -462,7 +462,7 @@
// that existing projects have out-of-date references which pass compilation.
xml::StripAndroidStudioAttributes(doc->root.get());
- XmlReferenceLinker xml_linker;
+ XmlReferenceLinker xml_linker(table);
if (!options_.do_not_fail_on_missing_resources && !xml_linker.Consume(context_, doc)) {
return {};
}
@@ -2112,7 +2112,7 @@
std::unique_ptr<xml::XmlResource> split_manifest =
GenerateSplitManifest(app_info_, *split_constraints_iter);
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(&final_table_);
if (!linker.Consume(context_, split_manifest.get())) {
context_->GetDiagnostics()->Error(DiagMessage()
<< "failed to create Split AndroidManifest.xml");
@@ -2143,7 +2143,7 @@
// So we give it a package name so it can see local resources.
manifest_xml->file.name.package = context_->GetCompilationPackage();
- XmlReferenceLinker manifest_linker;
+ XmlReferenceLinker manifest_linker(&final_table_);
if (options_.merge_only || manifest_linker.Consume(context_, manifest_xml.get())) {
if (options_.generate_proguard_rules_path &&
!proguard::CollectProguardRulesForManifest(manifest_xml.get(), &proguard_keep_set)) {
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index d1e6d39..3118eb8 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -24,6 +24,7 @@
using testing::Eq;
using testing::HasSubstr;
+using testing::IsNull;
using testing::Ne;
using testing::NotNull;
@@ -532,4 +533,109 @@
EXPECT_THAT(*result, Eq(0x01fd0072));
}
+TEST_F(LinkTest, MacroSubstitution) {
+ StdErrDiagnostics diag;
+ const std::string values =
+ R"(<resources xmlns:an="http://schemas.android.com/apk/res/android">
+ <macro name="is_enabled">true</macro>
+ <macro name="deep_is_enabled">@macro/is_enabled</macro>
+ <macro name="attr_ref">?is_enabled_attr</macro>
+ <macro name="raw_string">Hello World!</macro>
+ <macro name="android_ref">@an:color/primary_text_dark</macro>
+
+ <attr name="is_enabled_attr" />
+ <public type="attr" name="is_enabled_attr" id="0x7f010000"/>
+
+ <string name="is_enabled_str">@macro/is_enabled</string>
+ <bool name="is_enabled_bool">@macro/deep_is_enabled</bool>
+
+ <array name="my_array">
+ <item>@macro/is_enabled</item>
+ </array>
+
+ <style name="MyStyle">
+ <item name="android:background">@macro/attr_ref</item>
+ <item name="android:fontFamily">@macro/raw_string</item>
+ </style>
+ </resources>)";
+
+ const std::string xml_values =
+ R"(<SomeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:background="@macro/android_ref"
+ android:fontFamily="@macro/raw_string">
+ </SomeLayout>)";
+
+ // Build a library with a public attribute
+ const std::string lib_res = GetTestPath("test-res");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), values, lib_res, &diag));
+ ASSERT_TRUE(CompileFile(GetTestPath("res/layout/layout.xml"), xml_values, lib_res, &diag));
+
+ const std::string lib_apk = GetTestPath("test.apk");
+ // clang-format off
+ auto lib_link_args = LinkCommandBuilder(this)
+ .SetManifestFile(ManifestBuilder(this).SetPackageName("com.test").Build())
+ .AddCompiledResDir(lib_res, &diag)
+ .AddFlag("--no-auto-version")
+ .Build(lib_apk);
+ // clang-format on
+ ASSERT_TRUE(Link(lib_link_args, &diag));
+
+ auto apk = LoadedApk::LoadApkFromPath(lib_apk, &diag);
+ ASSERT_THAT(apk, NotNull());
+
+ // Test that the type flags determines the value type
+ auto actual_bool =
+ test::GetValue<BinaryPrimitive>(apk->GetResourceTable(), "com.test:bool/is_enabled_bool");
+ ASSERT_THAT(actual_bool, NotNull());
+ EXPECT_EQ(android::Res_value::TYPE_INT_BOOLEAN, actual_bool->value.dataType);
+ EXPECT_EQ(0xffffffffu, actual_bool->value.data);
+
+ auto actual_str =
+ test::GetValue<String>(apk->GetResourceTable(), "com.test:string/is_enabled_str");
+ ASSERT_THAT(actual_str, NotNull());
+ EXPECT_EQ(*actual_str->value, "true");
+
+ // Test nested data structures
+ auto actual_array = test::GetValue<Array>(apk->GetResourceTable(), "com.test:array/my_array");
+ ASSERT_THAT(actual_array, NotNull());
+ EXPECT_THAT(actual_array->elements.size(), Eq(1));
+
+ auto array_el_ref = ValueCast<BinaryPrimitive>(actual_array->elements[0].get());
+ ASSERT_THAT(array_el_ref, NotNull());
+ EXPECT_THAT(array_el_ref->value.dataType, Eq(android::Res_value::TYPE_INT_BOOLEAN));
+ EXPECT_THAT(array_el_ref->value.data, Eq(0xffffffffu));
+
+ auto actual_style = test::GetValue<Style>(apk->GetResourceTable(), "com.test:style/MyStyle");
+ ASSERT_THAT(actual_style, NotNull());
+ EXPECT_THAT(actual_style->entries.size(), Eq(2));
+
+ {
+ auto style_el = ValueCast<Reference>(actual_style->entries[0].value.get());
+ ASSERT_THAT(style_el, NotNull());
+ EXPECT_THAT(style_el->reference_type, Eq(Reference::Type::kAttribute));
+ EXPECT_THAT(style_el->id, Eq(0x7f010000));
+ }
+
+ {
+ auto style_el = ValueCast<String>(actual_style->entries[1].value.get());
+ ASSERT_THAT(style_el, NotNull());
+ EXPECT_THAT(*style_el->value, Eq("Hello World!"));
+ }
+
+ // Test substitution in compiled xml files
+ auto xml = apk->LoadXml("res/layout/layout.xml", &diag);
+ ASSERT_THAT(xml, NotNull());
+
+ auto& xml_attrs = xml->root->attributes;
+ ASSERT_THAT(xml_attrs.size(), Eq(2));
+
+ auto attr_value = ValueCast<Reference>(xml_attrs[0].compiled_value.get());
+ ASSERT_THAT(attr_value, NotNull());
+ EXPECT_THAT(attr_value->reference_type, Eq(Reference::Type::kResource));
+ EXPECT_THAT(attr_value->id, Eq(0x01060001));
+
+ EXPECT_THAT(xml_attrs[1].compiled_value.get(), IsNull());
+ EXPECT_THAT(xml_attrs[1].value, Eq("Hello World!"));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 17d11a6..74ecf47 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -567,13 +567,10 @@
}
bool FlattenTypes(BigBuffer* buffer) {
- // Sort the types by their IDs. They will be inserted into the StringPool in
- // this order.
-
size_t expected_type_id = 1;
for (const ResourceTableTypeView& type : package_.types) {
- if (type.type == ResourceType::kStyleable) {
- // Styleables aren't real Resource Types, they are represented in the R.java file.
+ if (type.type == ResourceType::kStyleable || type.type == ResourceType::kMacro) {
+ // Styleables and macros are not real resource types.
continue;
}
diff --git a/tools/aapt2/format/binary/XmlFlattener_test.cpp b/tools/aapt2/format/binary/XmlFlattener_test.cpp
index c24488b..d97e888 100644
--- a/tools/aapt2/format/binary/XmlFlattener_test.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener_test.cpp
@@ -222,7 +222,7 @@
android:id="@id/foo"
app:foo="@id/foo" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
// The tree needs a custom DynamicRefTable since it is not using a standard app ID (0x7f).
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index 498d5a2..ec331df 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -656,6 +656,38 @@
}
out_ref->name = name_ref.ToResourceName();
}
+ if (pb_ref.type_flags() != 0) {
+ out_ref->type_flags = pb_ref.type_flags();
+ }
+ out_ref->allow_raw = pb_ref.allow_raw();
+ return true;
+}
+
+static bool DeserializeMacroFromPb(const pb::MacroBody& pb_ref, Macro* out_ref,
+ std::string* out_error) {
+ out_ref->raw_value = pb_ref.raw_string();
+
+ if (pb_ref.has_style_string()) {
+ out_ref->style_string.str = pb_ref.style_string().str();
+ for (const auto& span : pb_ref.style_string().spans()) {
+ out_ref->style_string.spans.emplace_back(Span{
+ .name = span.name(), .first_char = span.start_index(), .last_char = span.end_index()});
+ }
+ }
+
+ for (const auto& untranslatable_section : pb_ref.untranslatable_sections()) {
+ out_ref->untranslatable_sections.emplace_back(
+ UntranslatableSection{.start = static_cast<size_t>(untranslatable_section.start_index()),
+ .end = static_cast<size_t>(untranslatable_section.end_index())});
+ }
+
+ for (const auto& namespace_decls : pb_ref.namespace_stack()) {
+ out_ref->alias_namespaces.emplace_back(
+ Macro::Namespace{.alias = namespace_decls.prefix(),
+ .package_name = namespace_decls.package_name(),
+ .is_private = namespace_decls.is_private()});
+ }
+
return true;
}
@@ -801,6 +833,15 @@
value = std::move(plural);
} break;
+ case pb::CompoundValue::kMacro: {
+ const pb::MacroBody& pb_macro = pb_compound_value.macro();
+ auto macro = std::make_unique<Macro>();
+ if (!DeserializeMacroFromPb(pb_macro, macro.get(), out_error)) {
+ return {};
+ }
+ value = std::move(macro);
+ } break;
+
default:
LOG(FATAL) << "unknown compound value: " << (int)pb_compound_value.value_case();
break;
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index f13f82d..d2f0336 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -440,6 +440,36 @@
if (ref.is_dynamic) {
pb_ref->mutable_is_dynamic()->set_value(ref.is_dynamic);
}
+ if (ref.type_flags) {
+ pb_ref->set_type_flags(*ref.type_flags);
+ }
+ pb_ref->set_allow_raw(ref.allow_raw);
+}
+
+static void SerializeMacroToPb(const Macro& ref, pb::MacroBody* pb_macro) {
+ pb_macro->set_raw_string(ref.raw_value);
+
+ auto pb_style_str = pb_macro->mutable_style_string();
+ pb_style_str->set_str(ref.style_string.str);
+ for (const auto& span : ref.style_string.spans) {
+ auto pb_span = pb_style_str->add_spans();
+ pb_span->set_name(span.name);
+ pb_span->set_start_index(span.first_char);
+ pb_span->set_end_index(span.last_char);
+ }
+
+ for (const auto& untranslatable_section : ref.untranslatable_sections) {
+ auto pb_section = pb_macro->add_untranslatable_sections();
+ pb_section->set_start_index(untranslatable_section.start);
+ pb_section->set_end_index(untranslatable_section.end);
+ }
+
+ for (const auto& namespace_decls : ref.alias_namespaces) {
+ auto pb_namespace = pb_macro->add_namespace_stack();
+ pb_namespace->set_prefix(namespace_decls.alias);
+ pb_namespace->set_package_name(namespace_decls.package_name);
+ pb_namespace->set_is_private(namespace_decls.is_private);
+ }
}
template <typename T>
@@ -643,6 +673,11 @@
}
}
+ void Visit(const Macro* macro) override {
+ pb::MacroBody* pb_macro = out_value_->mutable_compound_value()->mutable_macro();
+ SerializeMacroToPb(*macro, pb_macro);
+ }
+
void VisitAny(const Value* unknown) override {
LOG(FATAL) << "unimplemented value: " << *unknown;
}
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 591ba149..e563eda 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -894,4 +894,38 @@
EXPECT_THAT(*(s->value), Eq("foo"));
}
+TEST(ProtoSerializeTest, SerializeMacro) {
+ auto original = std::make_unique<Macro>();
+ original->raw_value = "\nThis being human is a guest house.";
+ original->style_string.str = " This being human is a guest house.";
+ original->style_string.spans.emplace_back(Span{.name = "b", .first_char = 12, .last_char = 16});
+ original->untranslatable_sections.emplace_back(UntranslatableSection{.start = 12, .end = 17});
+ original->alias_namespaces.emplace_back(
+ Macro::Namespace{.alias = "prefix", .package_name = "package.name", .is_private = true});
+
+ CloningValueTransformer cloner(nullptr);
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .Add(NewResourceBuilder("com.app.a:macro/foo")
+ .SetValue(original->Transform(cloner))
+ .Build())
+ .Build();
+
+ ResourceTable new_table;
+ pb::ResourceTable pb_table;
+ MockFileCollection files;
+ std::string error;
+ SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
+ ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
+ EXPECT_THAT(error, IsEmpty());
+
+ Macro* deserialized = test::GetValue<Macro>(&new_table, "com.app.a:macro/foo");
+ ASSERT_THAT(deserialized, NotNull());
+ EXPECT_THAT(deserialized->raw_value, Eq(original->raw_value));
+ EXPECT_THAT(deserialized->style_string.str, Eq(original->style_string.str));
+ EXPECT_THAT(deserialized->style_string.spans, Eq(original->style_string.spans));
+ EXPECT_THAT(deserialized->untranslatable_sections, Eq(original->untranslatable_sections));
+ EXPECT_THAT(deserialized->alias_namespaces, Eq(original->alias_namespaces));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index e1e2e01..de6524d 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -616,8 +616,9 @@
for (const auto& package : table_->packages) {
for (const auto& type : package->types) {
- if (type->type == ResourceType::kAttrPrivate) {
- // We generate these as part of the kAttr type, so skip them here.
+ if (type->type == ResourceType::kAttrPrivate || type->type == ResourceType::kMacro) {
+ // We generate kAttrPrivate as part of the kAttr type, so skip them here.
+ // Macros are not actual resources, so skip them as well.
continue;
}
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index d08b61e..40395ed 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -570,4 +570,25 @@
EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_dynamic_attr=1;"));
}
+TEST(JavaClassGeneratorTest, SkipMacros) {
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddValue("android:macro/bar", ResourceId(0x01010000), test::AttributeBuilder().Build())
+ .Build();
+
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .SetNameManglerPolicy(NameManglerPolicy{"android"})
+ .Build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
+
+ std::string output;
+ StringOutputStream out(&output);
+ EXPECT_TRUE(generator.Generate("android", &out));
+ out.Flush();
+
+ EXPECT_THAT(output, Not(HasSubstr("bar")));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index b7dfec3..e104066 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -264,7 +264,7 @@
</View>)");
foo_layout->file.name = test::ParseNameOrDie("com.foo:layout/foo");
- XmlReferenceLinker xml_linker;
+ XmlReferenceLinker xml_linker(nullptr);
ASSERT_TRUE(xml_linker.Consume(context.get(), bar_layout.get()));
ASSERT_TRUE(xml_linker.Consume(context.get(), foo_layout.get()));
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index c9b8d39..be6c930 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -133,12 +133,14 @@
// Once an XmlResource is processed by this linker, it is ready to be flattened.
class XmlReferenceLinker : public IXmlResourceConsumer {
public:
- XmlReferenceLinker() = default;
+ explicit XmlReferenceLinker(ResourceTable* table) : table_(table) {
+ }
bool Consume(IAaptContext* context, xml::XmlResource* resource) override;
private:
DISALLOW_COPY_AND_ASSIGN(XmlReferenceLinker);
+ ResourceTable* table_;
};
} // namespace aapt
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 8e49fab..4ac25bd 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -21,6 +21,7 @@
#include "androidfw/ResourceTypes.h"
#include "Diagnostics.h"
+#include "ResourceParser.h"
#include "ResourceTable.h"
#include "ResourceUtils.h"
#include "ResourceValues.h"
@@ -37,128 +38,153 @@
using ::android::base::StringPrintf;
namespace aapt {
-
namespace {
-
-// The ReferenceLinkerVisitor will follow all references and make sure they point
-// to resources that actually exist, either in the local resource table, or as external
-// symbols. Once the target resource has been found, the ID of the resource will be assigned
-// to the reference object.
-//
-// NOTE: All of the entries in the ResourceTable must be assigned IDs.
-class ReferenceLinkerVisitor : public DescendingValueVisitor {
- public:
- using DescendingValueVisitor::Visit;
-
- ReferenceLinkerVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
- StringPool* string_pool, xml::IPackageDeclStack* decl)
- : callsite_(callsite),
- context_(context),
- symbols_(symbols),
- package_decls_(decl),
- string_pool_(string_pool) {}
-
- void Visit(Reference* ref) override {
- if (!ReferenceLinker::LinkReference(callsite_, ref, context_, symbols_, package_decls_)) {
- error_ = true;
- }
+struct LoggingResourceName {
+ LoggingResourceName(const Reference& ref, const CallSite& callsite,
+ const xml::IPackageDeclStack* decls)
+ : ref_(ref), callsite_(callsite), decls_(decls) {
}
- // We visit the Style specially because during this phase, values of attributes are
- // all RawString values. Now that we are expected to resolve all symbols, we can
- // lookup the attributes to find out which types are allowed for the attributes' values.
- void Visit(Style* style) override {
- if (style->parent) {
- Visit(&style->parent.value());
+ const Reference& ref_;
+ const CallSite& callsite_;
+ const xml::IPackageDeclStack* decls_;
+};
+
+inline ::std::ostream& operator<<(::std::ostream& out, const LoggingResourceName& name) {
+ if (!name.ref_.name) {
+ out << name.ref_.id.value();
+ return out;
+ }
+
+ out << name.ref_.name.value();
+
+ Reference fully_qualified = name.ref_;
+ xml::ResolvePackage(name.decls_, &fully_qualified);
+
+ ResourceName& full_name = fully_qualified.name.value();
+ if (full_name.package.empty()) {
+ full_name.package = name.callsite_.package;
+ }
+
+ if (full_name != name.ref_.name.value()) {
+ out << " (aka " << full_name << ")";
+ }
+ return out;
+}
+
+} // namespace
+
+std::unique_ptr<Reference> ReferenceLinkerTransformer::TransformDerived(const Reference* value) {
+ auto linked_item =
+ ReferenceLinker::LinkReference(callsite_, *value, context_, symbols_, table_, package_decls_);
+ if (linked_item) {
+ auto linked_item_ptr = linked_item.release();
+ if (auto ref = ValueCast<Reference>(linked_item_ptr)) {
+ return std::unique_ptr<Reference>(ref);
}
+ context_->GetDiagnostics()->Error(DiagMessage(value->GetSource())
+ << "value of '"
+ << LoggingResourceName(*value, callsite_, package_decls_)
+ << "' must be a resource reference");
+ delete linked_item_ptr;
+ }
- for (Style::Entry& entry : style->entries) {
- std::string err_str;
+ error_ = true;
+ return CloningValueTransformer::TransformDerived(value);
+}
- // Transform the attribute reference so that it is using the fully qualified package
- // name. This will also mark the reference as being able to see private resources if
- // there was a '*' in the reference or if the package came from the private namespace.
- Reference transformed_reference = entry.key;
- ResolvePackage(package_decls_, &transformed_reference);
+std::unique_ptr<Style> ReferenceLinkerTransformer::TransformDerived(const Style* style) {
+ // We visit the Style specially because during this phase, values of attributes are either
+ // RawString or Reference values. Now that we are expected to resolve all symbols, we can lookup
+ // the attributes to find out which types are allowed for the attributes' values.
+ auto new_style = CloningValueTransformer::TransformDerived(style);
+ if (new_style->parent) {
+ new_style->parent = *TransformDerived(&style->parent.value());
+ }
- // Find the attribute in the symbol table and check if it is visible from this callsite.
- const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
- transformed_reference, callsite_, context_, symbols_, &err_str);
- if (symbol) {
- // Assign our style key the correct ID. The ID may not exist.
- entry.key.id = symbol->id;
+ for (Style::Entry& entry : new_style->entries) {
+ std::string err_str;
- // Try to convert the value to a more specific, typed value based on the attribute it is
- // set to.
- entry.value = ParseValueWithAttribute(std::move(entry.value), symbol->attribute.get());
+ // Transform the attribute reference so that it is using the fully qualified package
+ // name. This will also mark the reference as being able to see private resources if
+ // there was a '*' in the reference or if the package came from the private namespace.
+ Reference transformed_reference = entry.key;
+ ResolvePackage(package_decls_, &transformed_reference);
- // Link/resolve the final value (mostly if it's a reference).
- entry.value->Accept(this);
+ // Find the attribute in the symbol table and check if it is visible from this callsite.
+ const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
+ transformed_reference, callsite_, context_, symbols_, &err_str);
+ if (symbol) {
+ // Assign our style key the correct ID. The ID may not exist.
+ entry.key.id = symbol->id;
- // Now verify that the type of this item is compatible with the
- // attribute it is defined for. We pass `nullptr` as the DiagMessage so that this
- // check is fast and we avoid creating a DiagMessage when the match is successful.
- if (!symbol->attribute->Matches(*entry.value, nullptr)) {
- // The actual type of this item is incompatible with the attribute.
- DiagMessage msg(entry.key.GetSource());
+ // Link/resolve the final value if it's a reference.
+ entry.value = entry.value->Transform(*this);
- // Call the matches method again, this time with a DiagMessage so we fill in the actual
- // error message.
- symbol->attribute->Matches(*entry.value, &msg);
- context_->GetDiagnostics()->Error(msg);
- error_ = true;
- }
+ // Try to convert the value to a more specific, typed value based on the attribute it is
+ // set to.
+ entry.value = ParseValueWithAttribute(std::move(entry.value), symbol->attribute.get());
- } else {
+ // Now verify that the type of this item is compatible with the
+ // attribute it is defined for. We pass `nullptr` as the DiagMessage so that this
+ // check is fast and we avoid creating a DiagMessage when the match is successful.
+ if (!symbol->attribute->Matches(*entry.value, nullptr)) {
+ // The actual type of this item is incompatible with the attribute.
DiagMessage msg(entry.key.GetSource());
- msg << "style attribute '";
- ReferenceLinker::WriteResourceName(entry.key, callsite_, package_decls_, &msg);
- msg << "' " << err_str;
+
+ // Call the matches method again, this time with a DiagMessage so we fill in the actual
+ // error message.
+ symbol->attribute->Matches(*entry.value, &msg);
context_->GetDiagnostics()->Error(msg);
error_ = true;
}
+ } else {
+ context_->GetDiagnostics()->Error(DiagMessage(entry.key.GetSource())
+ << "style attribute '"
+ << LoggingResourceName(entry.key, callsite_, package_decls_)
+ << "' " << err_str);
+
+ error_ = true;
}
}
+ return new_style;
+}
- bool HasError() {
- return error_;
+std::unique_ptr<Item> ReferenceLinkerTransformer::TransformItem(const Reference* value) {
+ auto linked_value =
+ ReferenceLinker::LinkReference(callsite_, *value, context_, symbols_, table_, package_decls_);
+ if (linked_value) {
+ return linked_value;
}
+ error_ = true;
+ return CloningValueTransformer::TransformDerived(value);
+}
- private:
- DISALLOW_COPY_AND_ASSIGN(ReferenceLinkerVisitor);
+// Transform a RawString value into a more specific, appropriate value, based on the
+// Attribute. If a non RawString value is passed in, this is an identity transform.
+std::unique_ptr<Item> ReferenceLinkerTransformer::ParseValueWithAttribute(
+ std::unique_ptr<Item> value, const Attribute* attr) {
+ if (RawString* raw_string = ValueCast<RawString>(value.get())) {
+ std::unique_ptr<Item> transformed =
+ ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr);
- // Transform a RawString value into a more specific, appropriate value, based on the
- // Attribute. If a non RawString value is passed in, this is an identity transform.
- std::unique_ptr<Item> ParseValueWithAttribute(std::unique_ptr<Item> value,
- const Attribute* attr) {
- if (RawString* raw_string = ValueCast<RawString>(value.get())) {
- std::unique_ptr<Item> transformed =
- ResourceUtils::TryParseItemForAttribute(*raw_string->value, attr);
-
- // If we could not parse as any specific type, try a basic STRING.
- if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
- StringBuilder string_builder;
- string_builder.AppendText(*raw_string->value);
- if (string_builder) {
- transformed =
- util::make_unique<String>(string_pool_->MakeRef(string_builder.to_string()));
- }
- }
-
- if (transformed) {
- return transformed;
+ // If we could not parse as any specific type, try a basic STRING.
+ if (!transformed && (attr->type_mask & android::ResTable_map::TYPE_STRING)) {
+ StringBuilder string_builder;
+ string_builder.AppendText(*raw_string->value);
+ if (string_builder) {
+ transformed = util::make_unique<String>(pool_->MakeRef(string_builder.to_string()));
}
}
- return value;
- }
- const CallSite& callsite_;
- IAaptContext* context_;
- SymbolTable* symbols_;
- xml::IPackageDeclStack* package_decls_;
- StringPool* string_pool_;
- bool error_ = false;
-};
+ if (transformed) {
+ return transformed;
+ }
+ }
+ return value;
+}
+
+namespace {
class EmptyDeclStack : public xml::IPackageDeclStack {
public:
@@ -175,6 +201,27 @@
DISALLOW_COPY_AND_ASSIGN(EmptyDeclStack);
};
+struct MacroDeclStack : public xml::IPackageDeclStack {
+ explicit MacroDeclStack(std::vector<Macro::Namespace> namespaces)
+ : alias_namespaces_(std::move(namespaces)) {
+ }
+
+ Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
+ if (alias.empty()) {
+ return xml::ExtractedPackage{{}, true /*private*/};
+ }
+ for (auto it = alias_namespaces_.rbegin(); it != alias_namespaces_.rend(); ++it) {
+ if (alias == StringPiece(it->alias)) {
+ return xml::ExtractedPackage{it->package_name, it->is_private};
+ }
+ }
+ return {};
+ }
+
+ private:
+ std::vector<Macro::Namespace> alias_namespaces_;
+};
+
// The symbol is visible if it is public, or if the reference to it is requesting private access
// or if the callsite comes from the same package.
bool IsSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
@@ -220,8 +267,6 @@
// If the callsite package is the same as the current compilation package,
// check the feature split dependencies as well. Feature split resources
// can be referenced without a namespace, just like the base package.
- // TODO: modify the package name of included splits instead of having the
- // symbol table look up the resource in in every package. b/136105066
if (callsite.package == context->GetCompilationPackage()) {
const auto& split_name_dependencies = context->GetSplitNameDependencies();
for (const std::string& split_name : split_name_dependencies) {
@@ -295,29 +340,6 @@
return xml::AaptAttribute(*symbol->attribute, symbol->id);
}
-void ReferenceLinker::WriteResourceName(const Reference& ref, const CallSite& callsite,
- const xml::IPackageDeclStack* decls, DiagMessage* out_msg) {
- CHECK(out_msg != nullptr);
- if (!ref.name) {
- *out_msg << ref.id.value();
- return;
- }
-
- *out_msg << ref.name.value();
-
- Reference fully_qualified = ref;
- xml::ResolvePackage(decls, &fully_qualified);
-
- ResourceName& full_name = fully_qualified.name.value();
- if (full_name.package.empty()) {
- full_name.package = callsite.package;
- }
-
- if (full_name != ref.name.value()) {
- *out_msg << " (aka " << full_name << ")";
- }
-}
-
void ReferenceLinker::WriteAttributeName(const Reference& ref, const CallSite& callsite,
const xml::IPackageDeclStack* decls,
DiagMessage* out_msg) {
@@ -348,18 +370,71 @@
}
}
-bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* reference,
- IAaptContext* context, SymbolTable* symbols,
- const xml::IPackageDeclStack* decls) {
- CHECK(reference != nullptr);
- if (!reference->name && !reference->id) {
+std::unique_ptr<Item> ReferenceLinker::LinkReference(const CallSite& callsite,
+ const Reference& reference,
+ IAaptContext* context, SymbolTable* symbols,
+ ResourceTable* table,
+ const xml::IPackageDeclStack* decls) {
+ if (!reference.name && !reference.id) {
// This is @null.
- return true;
+ return std::make_unique<Reference>(reference);
}
- Reference transformed_reference = *reference;
+ Reference transformed_reference = reference;
xml::ResolvePackage(decls, &transformed_reference);
+ if (transformed_reference.name.value().type == ResourceType::kMacro) {
+ if (transformed_reference.name.value().package.empty()) {
+ transformed_reference.name.value().package = callsite.package;
+ }
+
+ auto result = table->FindResource(transformed_reference.name.value());
+ if (!result || result.value().entry->values.empty()) {
+ context->GetDiagnostics()->Error(
+ DiagMessage(reference.GetSource())
+ << "failed to find definition for "
+ << LoggingResourceName(transformed_reference, callsite, decls));
+ return {};
+ }
+
+ auto& macro_values = result.value().entry->values;
+ CHECK(macro_values.size() == 1) << "Macros can only be defined in the default configuration.";
+
+ auto macro = ValueCast<Macro>(macro_values[0]->value.get());
+ CHECK(macro != nullptr) << "Value of macro resource is not a Macro (actual "
+ << *macro_values[0]->value << ")";
+
+ // Re-create the state used to parse the macro tag to compile the macro contents as if it was
+ // defined inline
+ uint32_t type_flags = 0;
+ if (reference.type_flags.has_value()) {
+ type_flags = reference.type_flags.value();
+ }
+
+ MacroDeclStack namespace_stack(macro->alias_namespaces);
+ FlattenedXmlSubTree sub_tree{.raw_value = macro->raw_value,
+ .style_string = macro->style_string,
+ .untranslatable_sections = macro->untranslatable_sections,
+ .namespace_resolver = &namespace_stack,
+ .source = macro->GetSource()};
+
+ auto new_value = ResourceParser::ParseXml(sub_tree, type_flags, reference.allow_raw, *table,
+ macro_values[0]->config, *context->GetDiagnostics());
+ if (new_value == nullptr) {
+ context->GetDiagnostics()->Error(
+ DiagMessage(reference.GetSource())
+ << "failed to substitute macro "
+ << LoggingResourceName(transformed_reference, callsite, decls)
+ << ": failed to parse contents as one of type(s) " << Attribute::MaskString(type_flags));
+ return {};
+ }
+
+ if (auto ref = ValueCast<Reference>(new_value.get())) {
+ return LinkReference(callsite, *ref, context, symbols, table, decls);
+ }
+ return new_value;
+ }
+
std::string err_str;
const SymbolTable::Symbol* s =
ResolveSymbolCheckVisibility(transformed_reference, callsite, context, symbols, &err_str);
@@ -367,17 +442,17 @@
// The ID may not exist. This is fine because of the possibility of building
// against libraries without assigned IDs.
// Ex: Linking against own resources when building a static library.
- reference->id = s->id;
- reference->is_dynamic = s->is_dynamic;
- return true;
+ auto new_ref = std::make_unique<Reference>(reference);
+ new_ref->id = s->id;
+ new_ref->is_dynamic = s->is_dynamic;
+ return std::move(new_ref);
}
- DiagMessage error_msg(reference->GetSource());
- error_msg << "resource ";
- WriteResourceName(*reference, callsite, decls, &error_msg);
- error_msg << " " << err_str;
- context->GetDiagnostics()->Error(error_msg);
- return false;
+ context->GetDiagnostics()->Error(DiagMessage(reference.GetSource())
+ << "resource "
+ << LoggingResourceName(transformed_reference, callsite, decls)
+ << " " << err_str);
+ return {};
}
bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
@@ -412,14 +487,15 @@
// The context of this resource is the package in which it is defined.
const CallSite callsite{name.package};
- ReferenceLinkerVisitor visitor(callsite, context, context->GetExternalSymbols(),
- &table->string_pool, &decl_stack);
+ ReferenceLinkerTransformer reference_transformer(callsite, context,
+ context->GetExternalSymbols(),
+ &table->string_pool, table, &decl_stack);
for (auto& config_value : entry->values) {
- config_value->value->Accept(&visitor);
+ config_value->value = config_value->value->Transform(reference_transformer);
}
- if (visitor.HasError()) {
+ if (reference_transformer.HasError()) {
error = true;
}
}
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index 1256709..770f1e5 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -28,6 +28,41 @@
namespace aapt {
+// A ValueTransformer that returns fully linked versions of resource and macro references.
+class ReferenceLinkerTransformer : public CloningValueTransformer {
+ public:
+ ReferenceLinkerTransformer(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
+ StringPool* string_pool, ResourceTable* table,
+ xml::IPackageDeclStack* decl)
+ : CloningValueTransformer(string_pool),
+ callsite_(callsite),
+ context_(context),
+ symbols_(symbols),
+ table_(table),
+ package_decls_(decl) {
+ }
+
+ std::unique_ptr<Reference> TransformDerived(const Reference* value) override;
+ std::unique_ptr<Item> TransformItem(const Reference* value) override;
+ std::unique_ptr<Style> TransformDerived(const Style* value) override;
+
+ bool HasError() {
+ return error_;
+ }
+
+ private:
+ // Transform a RawString value into a more specific, appropriate value, based on the
+ // Attribute. If a non RawString value is passed in, this is an identity transform.
+ std::unique_ptr<Item> ParseValueWithAttribute(std::unique_ptr<Item> value, const Attribute* attr);
+
+ const CallSite& callsite_;
+ IAaptContext* context_;
+ SymbolTable* symbols_;
+ ResourceTable* table_;
+ xml::IPackageDeclStack* package_decls_;
+ bool error_ = false;
+};
+
// Resolves all references to resources in the ResourceTable and assigns them IDs.
// The ResourceTable must already have IDs assigned to each resource.
// Once the ResourceTable is processed by this linker, it is ready to be flattened.
@@ -70,19 +105,28 @@
// Writes the resource name to the DiagMessage, using the
// "orig_name (aka <transformed_name>)" syntax.
- static void WriteResourceName(const Reference& orig, const CallSite& callsite,
- const xml::IPackageDeclStack* decls, DiagMessage* out_msg);
+ /*static void WriteResourceName(const Reference& orig, const CallSite& callsite,
+ const xml::IPackageDeclStack* decls, DiagMessage* out_msg);*/
// Same as WriteResourceName but omits the 'attr' part.
static void WriteAttributeName(const Reference& ref, const CallSite& callsite,
const xml::IPackageDeclStack* decls, DiagMessage* out_msg);
- // Transforms the package name of the reference to the fully qualified package name using
- // the xml::IPackageDeclStack, then mangles and looks up the symbol. If the symbol is visible
- // to the reference at the callsite, the reference is updated with an ID.
- // Returns false on failure, and an error message is logged to the IDiagnostics in the context.
- static bool LinkReference(const CallSite& callsite, Reference* reference, IAaptContext* context,
- SymbolTable* symbols, const xml::IPackageDeclStack* decls);
+ // Returns a fully linked version a resource reference.
+ //
+ // If the reference points to a non-macro resource, the xml::IPackageDeclStack is used to
+ // determine the fully qualified name of the referenced resource. If the symbol is visible
+ // to the reference at the callsite, a copy of the reference with an updated updated ID is
+ // returned.
+ //
+ // If the reference points to a macro, the ResourceTable is used to find the macro definition and
+ // substitute its contents in place of the reference.
+ //
+ // Returns nullptr on failure, and an error message is logged to the IDiagnostics in the context.
+ static std::unique_ptr<Item> LinkReference(const CallSite& callsite, const Reference& reference,
+ IAaptContext* context, SymbolTable* symbols,
+ ResourceTable* table,
+ const xml::IPackageDeclStack* decls);
// Links all references in the ResourceTable.
bool Consume(IAaptContext* context, ResourceTable* table) override;
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 228c5bd74..2d8f0d3 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -365,4 +365,22 @@
EXPECT_THAT(s, IsNull());
}
+TEST(ReferenceLinkerTest, MacroFailToFindDefinition) {
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddReference("com.app.test:string/foo", ResourceId(0x7f020000), "com.app.test:macro/bar")
+ .Build();
+
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetPackageId(0x7f)
+ .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .Build();
+
+ ReferenceLinker linker;
+ ASSERT_FALSE(linker.Consume(context.get(), table.get()));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/link/XmlCompatVersioner_test.cpp b/tools/aapt2/link/XmlCompatVersioner_test.cpp
index a98ab0f..d638096 100644
--- a/tools/aapt2/link/XmlCompatVersioner_test.cpp
+++ b/tools/aapt2/link/XmlCompatVersioner_test.cpp
@@ -82,7 +82,7 @@
app:foo="16dp"
foo="bar"/>)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
XmlCompatVersioner::Rules rules;
@@ -121,7 +121,7 @@
app:foo="16dp"
foo="bar"/>)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
XmlCompatVersioner::Rules rules;
@@ -181,7 +181,7 @@
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:paddingHorizontal="24dp" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
XmlCompatVersioner::Rules rules;
@@ -256,7 +256,7 @@
android:paddingLeft="16dp"
android:paddingRight="16dp"/>)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
Item* padding_horizontal_value =
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index c3c16b9..aaa085e 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -33,49 +33,18 @@
namespace {
-// Visits all references (including parents of styles, references in styles, arrays, etc) and
-// links their symbolic name to their Resource ID, performing mangling and package aliasing
-// as needed.
-class ReferenceVisitor : public DescendingValueVisitor {
- public:
- using DescendingValueVisitor::Visit;
-
- ReferenceVisitor(const CallSite& callsite, IAaptContext* context, SymbolTable* symbols,
- xml::IPackageDeclStack* decls)
- : callsite_(callsite), context_(context), symbols_(symbols), decls_(decls), error_(false) {}
-
- void Visit(Reference* ref) override {
- if (!ReferenceLinker::LinkReference(callsite_, ref, context_, symbols_, decls_)) {
- error_ = true;
- }
- }
-
- bool HasError() const {
- return error_;
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ReferenceVisitor);
-
- const CallSite& callsite_;
- IAaptContext* context_;
- SymbolTable* symbols_;
- xml::IPackageDeclStack* decls_;
- bool error_;
-};
-
// Visits each xml Element and compiles the attributes within.
class XmlVisitor : public xml::PackageAwareVisitor {
public:
using xml::PackageAwareVisitor::Visit;
- XmlVisitor(const Source& source, const CallSite& callsite, IAaptContext* context,
- SymbolTable* symbols)
+ XmlVisitor(const Source& source, StringPool* pool, const CallSite& callsite,
+ IAaptContext* context, ResourceTable* table, SymbolTable* symbols)
: source_(source),
callsite_(callsite),
context_(context),
symbols_(symbols),
- reference_visitor_(callsite, context, symbols, this) {
+ reference_transformer_(callsite, context, symbols, pool, table, this) {
}
void Visit(xml::Element* el) override {
@@ -127,7 +96,7 @@
if (attr.compiled_value) {
// With a compiledValue, we must resolve the reference and assign it an ID.
attr.compiled_value->SetSource(source);
- attr.compiled_value->Accept(&reference_visitor_);
+ attr.compiled_value = attr.compiled_value->Transform(reference_transformer_);
} else if ((attribute->type_mask & android::ResTable_map::TYPE_STRING) == 0) {
// We won't be able to encode this as a string.
DiagMessage msg(source);
@@ -143,7 +112,7 @@
}
bool HasError() {
- return error_ || reference_visitor_.HasError();
+ return error_ || reference_transformer_.HasError();
}
private:
@@ -154,7 +123,7 @@
IAaptContext* context_;
SymbolTable* symbols_;
- ReferenceVisitor reference_visitor_;
+ ReferenceLinkerTransformer reference_transformer_;
bool error_ = false;
};
@@ -173,7 +142,8 @@
callsite.package = context->GetCompilationPackage();
}
- XmlVisitor visitor(resource->file.source, callsite, context, context->GetExternalSymbols());
+ XmlVisitor visitor(resource->file.source, &resource->string_pool, callsite, context, table_,
+ context->GetExternalSymbols());
if (resource->root) {
resource->root->Accept(&visitor);
return !visitor.HasError();
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index 0ce2e50..ddf5b9a 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -91,7 +91,7 @@
nonAaptAttrRef="@id/id"
class="hello" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* view_el = doc->root.get();
@@ -144,7 +144,7 @@
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:colorAccent="@android:color/hidden" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_FALSE(linker.Consume(context_.get(), doc.get()));
}
@@ -153,7 +153,7 @@
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:colorAccent="@*android:color/hidden" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
}
@@ -162,7 +162,7 @@
<View xmlns:support="http://schemas.android.com/apk/res/com.android.support"
support:colorAccent="#ff0000" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* view_el = doc->root.get();
@@ -181,7 +181,7 @@
<View xmlns:app="http://schemas.android.com/apk/res-auto"
app:colorAccent="@app:color/red" />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* view_el = doc->root.get();
@@ -203,7 +203,7 @@
<View xmlns:app="http://schemas.android.com/apk/res/com.app.test" app:attr="@app:id/id"/>
</View>)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* view_el = doc->root.get();
@@ -239,7 +239,7 @@
<View xmlns:android="http://schemas.android.com/apk/res/com.app.test"
android:attr="@id/id"/>)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* view_el = doc->root.get();
@@ -261,7 +261,7 @@
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
<gradient />)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* gradient_el = doc->root.get();
@@ -283,7 +283,7 @@
<gradient xmlns:android="http://schemas.android.com/apk/res/android"
android:angle="90"/>)");
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* gradient_el = doc->root.get();
@@ -305,7 +305,7 @@
<gradient xmlns:android="http://schemas.android.com/apk/res/android" />)");
context_->SetMinSdkVersion(30);
- XmlReferenceLinker linker;
+ XmlReferenceLinker linker(nullptr);
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
xml::Element* gradient_el = doc->root.get();
diff --git a/tools/aapt2/xml/XmlPullParser.cpp b/tools/aapt2/xml/XmlPullParser.cpp
index a023494..182203d 100644
--- a/tools/aapt2/xml/XmlPullParser.cpp
+++ b/tools/aapt2/xml/XmlPullParser.cpp
@@ -177,6 +177,10 @@
return event_queue_.front().data2;
}
+const std::vector<XmlPullParser::PackageDecl>& XmlPullParser::package_decls() const {
+ return package_aliases_;
+}
+
XmlPullParser::const_iterator XmlPullParser::begin_attributes() const {
return event_queue_.front().attributes.begin();
}
diff --git a/tools/aapt2/xml/XmlPullParser.h b/tools/aapt2/xml/XmlPullParser.h
index 6ebaa28..5da2d4b 100644
--- a/tools/aapt2/xml/XmlPullParser.h
+++ b/tools/aapt2/xml/XmlPullParser.h
@@ -123,6 +123,13 @@
*/
Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
+ struct PackageDecl {
+ std::string prefix;
+ ExtractedPackage package;
+ };
+
+ const std::vector<PackageDecl>& package_decls() const;
+
//
// Remaining methods are for retrieving information about attributes
// associated with a StartElement.
@@ -180,11 +187,6 @@
const std::string empty_;
size_t depth_;
std::stack<std::string> namespace_uris_;
-
- struct PackageDecl {
- std::string prefix;
- ExtractedPackage package;
- };
std::vector<PackageDecl> package_aliases_;
};