Merge "Remove usage of Math.randomLongInternal" into sc-dev
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
index 80317e4..78c4820 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7396576"
+ build_id: "7471822"
target: "CtsShim"
source_file: "aosp_arm64/CtsShimPriv.apk"
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
index 3605b6d..a9632ad 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7396576"
+ build_id: "7471822"
target: "CtsShim"
source_file: "aosp_arm64/CtsShim.apk"
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
index 025ec3a..df3a0bb 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7396576"
+ build_id: "7471822"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShimPriv.apk"
}
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
index e19235a..1bc6cb8 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
drops {
android_build_drop {
- build_id: "7396576"
+ build_id: "7471822"
target: "CtsShim"
source_file: "aosp_x86_64/CtsShim.apk"
}
diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp
index b8fce4f..cd9be9b 100644
--- a/apex/appsearch/framework/Android.bp
+++ b/apex/appsearch/framework/Android.bp
@@ -65,5 +65,8 @@
},
jarjar_rules: "jarjar-rules.txt",
apex_available: ["com.android.appsearch"],
+ impl_library_visibility: [
+ "//frameworks/base/apex/appsearch/service",
+ ],
unsafe_ignore_missing_latest_api: true, // TODO(b/146218515) should be removed
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 4ef91b5..b5e3662 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -90,6 +90,7 @@
@NonNull Consumer<AppSearchResult<AppSearchSession>> callback) {
try {
mService.initialize(
+ mPackageName,
mUserHandle,
/*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
new IAppSearchResultCallback.Stub() {
@@ -136,8 +137,6 @@
* @param callback Callback to receive errors resulting from setting the schema. If the
* operation succeeds, the callback will be invoked with {@code null}.
*/
- // TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are
- // exposed.
public void setSchema(
@NonNull SetSchemaRequest request,
@NonNull Executor workExecutor,
@@ -152,7 +151,7 @@
for (AppSearchSchema schema : request.getSchemas()) {
schemaBundles.add(schema.getBundle());
}
- Map<String, List<Bundle>> schemasPackageAccessibleBundles =
+ Map<String, List<Bundle>> schemasVisibleToPackagesBundles =
new ArrayMap<>(request.getSchemasVisibleToPackagesInternal().size());
for (Map.Entry<String, Set<PackageIdentifier>> entry :
request.getSchemasVisibleToPackagesInternal().entrySet()) {
@@ -160,7 +159,7 @@
for (PackageIdentifier packageIdentifier : entry.getValue()) {
packageIdentifierBundles.add(packageIdentifier.getBundle());
}
- schemasPackageAccessibleBundles.put(entry.getKey(), packageIdentifierBundles);
+ schemasVisibleToPackagesBundles.put(entry.getKey(), packageIdentifierBundles);
}
// No need to trigger migration if user never set migrator
@@ -168,14 +167,14 @@
setSchemaNoMigrations(
request,
schemaBundles,
- schemasPackageAccessibleBundles,
+ schemasVisibleToPackagesBundles,
callbackExecutor,
callback);
} else {
setSchemaWithMigrations(
request,
schemaBundles,
- schemasPackageAccessibleBundles,
+ schemasVisibleToPackagesBundles,
workExecutor,
callbackExecutor,
callback);
@@ -687,7 +686,9 @@
if (mIsMutated && !mIsClosed) {
try {
mService.persistToDisk(
- mUserHandle, /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime());
+ mPackageName,
+ mUserHandle,
+ /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime());
mIsClosed = true;
} catch (RemoteException e) {
Log.e(TAG, "Unable to close the AppSearchSession", e);
@@ -704,7 +705,7 @@
private void setSchemaNoMigrations(
@NonNull SetSchemaRequest request,
@NonNull List<Bundle> schemaBundles,
- @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles,
+ @NonNull Map<String, List<Bundle>> schemasVisibleToPackagesBundles,
@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback) {
try {
@@ -713,7 +714,7 @@
mDatabaseName,
schemaBundles,
new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
- schemasPackageAccessibleBundles,
+ schemasVisibleToPackagesBundles,
request.isForceOverride(),
request.getVersion(),
mUserHandle,
@@ -761,7 +762,7 @@
private void setSchemaWithMigrations(
@NonNull SetSchemaRequest request,
@NonNull List<Bundle> schemaBundles,
- @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles,
+ @NonNull Map<String, List<Bundle>> schemasVisibleToPackagesBundles,
@NonNull Executor workExecutor,
@NonNull @CallbackExecutor Executor callbackExecutor,
@NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback) {
@@ -787,7 +788,7 @@
// No need to trigger migration if no migrator is active.
if (activeMigrators.isEmpty()) {
- setSchemaNoMigrations(request, schemaBundles, schemasPackageAccessibleBundles,
+ setSchemaNoMigrations(request, schemaBundles, schemasVisibleToPackagesBundles,
callbackExecutor, callback);
return;
}
@@ -801,7 +802,7 @@
mDatabaseName,
schemaBundles,
new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
- schemasPackageAccessibleBundles,
+ schemasVisibleToPackagesBundles,
/*forceOverride=*/ false,
request.getVersion(),
mUserHandle,
@@ -853,7 +854,7 @@
mDatabaseName,
schemaBundles,
new ArrayList<>(request.getSchemasNotDisplayedBySystem()),
- schemasPackageAccessibleBundles,
+ schemasVisibleToPackagesBundles,
/*forceOverride=*/ true,
request.getVersion(),
mUserHandle,
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
index 247eb08..130e442 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
@@ -73,6 +73,7 @@
@NonNull Consumer<AppSearchResult<GlobalSearchSession>> callback) {
try {
mService.initialize(
+ mPackageName,
mUserHandle,
/*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
new IAppSearchResultCallback.Stub() {
@@ -187,7 +188,9 @@
if (mIsMutated && !mIsClosed) {
try {
mService.persistToDisk(
- mUserHandle, /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime());
+ mPackageName,
+ mUserHandle,
+ /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime());
mIsClosed = true;
} catch (RemoteException e) {
Log.e(TAG, "Unable to close the GlobalSearchSession", e);
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
index eb5d22e..6dfa01f 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
@@ -124,7 +124,8 @@
wrapCallback(executor, callback));
}
} else {
- mService.getNextPage(mNextPageToken, mUserHandle, wrapCallback(executor, callback));
+ mService.getNextPage(mPackageName, mNextPageToken, mUserHandle,
+ wrapCallback(executor, callback));
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -135,7 +136,7 @@
public void close() {
if (!mIsClosed) {
try {
- mService.invalidateNextPageToken(mNextPageToken, mUserHandle);
+ mService.invalidateNextPageToken(mPackageName, mNextPageToken, mUserHandle);
mIsClosed = true;
} catch (RemoteException e) {
Log.e(TAG, "Unable to close the SearchResults", e);
diff --git a/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl
index 0b26e14..a2f545f 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl
@@ -32,7 +32,7 @@
* @param schemaBundles List of {@link AppSearchSchema} bundles.
* @param schemasNotDisplayedBySystem Schema types that should not be surfaced on platform
* surfaces.
- * @param schemasPackageAccessibleBundles Schema types that are visible to the specified
+ * @param schemasVisibleToPackagesBundles Schema types that are visible to the specified
* packages. The value List contains PackageIdentifier Bundles.
* @param forceOverride Whether to apply the new schema even if it is incompatible. All
* incompatible documents will be deleted.
@@ -48,7 +48,7 @@
in String databaseName,
in List<Bundle> schemaBundles,
in List<String> schemasNotDisplayedBySystem,
- in Map<String, List<Bundle>> schemasPackageAccessibleBundles,
+ in Map<String, List<Bundle>> schemasVisibleToPackagesBundles,
boolean forceOverride,
in int schemaVersion,
in UserHandle userHandle,
@@ -181,21 +181,30 @@
* Fetches the next page of results of a previously executed query. Results can be empty if
* next-page token is invalid or all pages have been returned.
*
+ * @param packageName The name of the package to persist to disk for.
* @param nextPageToken The token of pre-loaded results of previously executed query.
* @param userHandle Handle of the calling user
* @param callback {@link AppSearchResult}<{@link Bundle}> of performing this
* operation.
*/
- void getNextPage(in long nextPageToken, in UserHandle userHandle, in IAppSearchResultCallback callback);
+ void getNextPage(
+ in String packageName,
+ in long nextPageToken,
+ in UserHandle userHandle,
+ in IAppSearchResultCallback callback);
/**
* Invalidates the next-page token so that no more results of the related query can be returned.
*
+ * @param packageName The name of the package to persist to disk for.
* @param nextPageToken The token of pre-loaded results of previously executed query to be
* Invalidated.
* @param userHandle Handle of the calling user
*/
- void invalidateNextPageToken(in long nextPageToken, in UserHandle userHandle);
+ void invalidateNextPageToken(
+ in String packageName,
+ in long nextPageToken,
+ in UserHandle userHandle);
/**
* Searches a document based on a given specifications.
@@ -336,20 +345,26 @@
/**
* Persists all update/delete requests to the disk.
*
+ * @param packageName The name of the package to persist to disk for.
* @param userHandle Handle of the calling user
* @param binderCallStartTimeMillis start timestamp of binder call in Millis
*/
- void persistToDisk(in UserHandle userHandle, in long binderCallStartTimeMillis);
+ void persistToDisk(
+ in String packageName,
+ in UserHandle userHandle,
+ in long binderCallStartTimeMillis);
/**
* Creates and initializes AppSearchImpl for the calling app.
*
+ * @param packageName The name of the package to initialize for.
* @param userHandle Handle of the calling user
* @param binderCallStartTimeMillis start timestamp of binder call in Millis
* @param callback {@link IAppSearchResultCallback#onResult} will be called with an
* {@link AppSearchResult}<{@link Void}>.
*/
void initialize(
+ in String packageName,
in UserHandle userHandle,
in long binderCallStartTimeMillis,
in IAppSearchResultCallback callback);
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
index 272e12d..d493a1c 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
@@ -96,17 +96,6 @@
return Collections.unmodifiableMap(mAll);
}
- /**
- * Asserts that this {@link AppSearchBatchResult} has no failures.
- *
- * @hide
- */
- public void checkSuccess() {
- if (!isSuccess()) {
- throw new IllegalStateException("AppSearchBatchResult has failures: " + this);
- }
- }
-
@Override
@NonNull
public String toString() {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java
index c57cf2e..b1cb132 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java
@@ -239,6 +239,8 @@
resultCode = AppSearchResult.RESULT_INVALID_ARGUMENT;
} else if (t instanceof IOException) {
resultCode = AppSearchResult.RESULT_IO_ERROR;
+ } else if (t instanceof SecurityException) {
+ resultCode = AppSearchResult.RESULT_SECURITY_ERROR;
} else {
resultCode = AppSearchResult.RESULT_UNKNOWN_ERROR;
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
index 237e624..0ee5e65 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -21,6 +21,7 @@
import android.annotation.Nullable;
import android.app.appsearch.exceptions.IllegalSchemaException;
import android.app.appsearch.util.BundleUtil;
+import android.app.appsearch.util.IndentingStringBuilder;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.util.ArraySet;
@@ -30,6 +31,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -67,8 +69,45 @@
}
@Override
+ @NonNull
public String toString() {
- return mBundle.toString();
+ IndentingStringBuilder stringBuilder = new IndentingStringBuilder();
+ appendAppSearchSchemaString(stringBuilder);
+ return stringBuilder.toString();
+ }
+
+ /**
+ * Appends a debugging string for the {@link AppSearchSchema} instance to the given string
+ * builder.
+ *
+ * @param builder the builder to append to.
+ */
+ private void appendAppSearchSchemaString(@NonNull IndentingStringBuilder builder) {
+ Objects.requireNonNull(builder);
+
+ builder.append("{\n");
+ builder.increaseIndentLevel();
+ builder.append("schemaType: \"").append(getSchemaType()).append("\",\n");
+ builder.append("properties: [\n");
+
+ AppSearchSchema.PropertyConfig[] sortedProperties =
+ getProperties().toArray(new AppSearchSchema.PropertyConfig[0]);
+ Arrays.sort(sortedProperties, (o1, o2) -> o1.getName().compareTo(o2.getName()));
+
+ for (int i = 0; i < sortedProperties.length; i++) {
+ AppSearchSchema.PropertyConfig propertyConfig = sortedProperties[i];
+ builder.increaseIndentLevel();
+ propertyConfig.appendPropertyConfigString(builder);
+ if (i != sortedProperties.length - 1) {
+ builder.append(",\n");
+ }
+ builder.decreaseIndentLevel();
+ }
+
+ builder.append("\n");
+ builder.append("]\n");
+ builder.decreaseIndentLevel();
+ builder.append("}");
}
/** Returns the name of this schema type, e.g. Email. */
@@ -255,7 +294,68 @@
@Override
@NonNull
public String toString() {
- return mBundle.toString();
+ IndentingStringBuilder stringBuilder = new IndentingStringBuilder();
+ appendPropertyConfigString(stringBuilder);
+ return stringBuilder.toString();
+ }
+
+ /**
+ * Appends a debug string for the {@link AppSearchSchema.PropertyConfig} instance to the
+ * given string builder.
+ *
+ * @param builder the builder to append to.
+ */
+ void appendPropertyConfigString(@NonNull IndentingStringBuilder builder) {
+ Objects.requireNonNull(builder);
+
+ builder.append("{\n");
+ builder.increaseIndentLevel();
+ builder.append("name: \"").append(getName()).append("\",\n");
+
+ if (this instanceof AppSearchSchema.StringPropertyConfig) {
+ ((StringPropertyConfig) this).appendStringPropertyConfigFields(builder);
+ } else if (this instanceof AppSearchSchema.DocumentPropertyConfig) {
+ ((DocumentPropertyConfig) this).appendDocumentPropertyConfigFields(builder);
+ }
+
+ switch (getCardinality()) {
+ case AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED:
+ builder.append("cardinality: CARDINALITY_REPEATED,\n");
+ break;
+ case AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL:
+ builder.append("cardinality: CARDINALITY_OPTIONAL,\n");
+ break;
+ case AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED:
+ builder.append("cardinality: CARDINALITY_REQUIRED,\n");
+ break;
+ default:
+ builder.append("cardinality: CARDINALITY_UNKNOWN,\n");
+ }
+
+ switch (getDataType()) {
+ case AppSearchSchema.PropertyConfig.DATA_TYPE_STRING:
+ builder.append("dataType: DATA_TYPE_STRING,\n");
+ break;
+ case AppSearchSchema.PropertyConfig.DATA_TYPE_LONG:
+ builder.append("dataType: DATA_TYPE_LONG,\n");
+ break;
+ case AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE:
+ builder.append("dataType: DATA_TYPE_DOUBLE,\n");
+ break;
+ case AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN:
+ builder.append("dataType: DATA_TYPE_BOOLEAN,\n");
+ break;
+ case AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES:
+ builder.append("dataType: DATA_TYPE_BYTES,\n");
+ break;
+ case AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT:
+ builder.append("dataType: DATA_TYPE_DOCUMENT,\n");
+ break;
+ default:
+ builder.append("dataType: DATA_TYPE_UNKNOWN,\n");
+ }
+ builder.decreaseIndentLevel();
+ builder.append("}");
}
/** Returns the name of this property. */
@@ -506,6 +606,41 @@
return new StringPropertyConfig(bundle);
}
}
+
+ /**
+ * Appends a debug string for the {@link StringPropertyConfig} instance to the given string
+ * builder.
+ *
+ * <p>This appends fields specific to a {@link StringPropertyConfig} instance.
+ *
+ * @param builder the builder to append to.
+ */
+ void appendStringPropertyConfigFields(@NonNull IndentingStringBuilder builder) {
+ switch (getIndexingType()) {
+ case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE:
+ builder.append("indexingType: INDEXING_TYPE_NONE,\n");
+ break;
+ case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS:
+ builder.append("indexingType: INDEXING_TYPE_EXACT_TERMS,\n");
+ break;
+ case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES:
+ builder.append("indexingType: INDEXING_TYPE_PREFIXES,\n");
+ break;
+ default:
+ builder.append("indexingType: INDEXING_TYPE_UNKNOWN,\n");
+ }
+
+ switch (getTokenizerType()) {
+ case AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE:
+ builder.append("tokenizerType: TOKENIZER_TYPE_NONE,\n");
+ break;
+ case AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN:
+ builder.append("tokenizerType: TOKENIZER_TYPE_PLAIN,\n");
+ break;
+ default:
+ builder.append("tokenizerType: TOKENIZER_TYPE_UNKNOWN,\n");
+ }
+ }
}
/**
@@ -858,5 +993,21 @@
return new DocumentPropertyConfig(bundle);
}
}
+
+ /**
+ * Appends a debug string for the {@link DocumentPropertyConfig} instance to the given
+ * string builder.
+ *
+ * <p>This appends fields specific to a {@link DocumentPropertyConfig} instance.
+ *
+ * @param builder the builder to append to.
+ */
+ void appendDocumentPropertyConfigFields(@NonNull IndentingStringBuilder builder) {
+ builder.append("shouldIndexNestedProperties: ")
+ .append(shouldIndexNestedProperties())
+ .append(",\n");
+
+ builder.append("schemaType: \"").append(getSchemaType()).append("\",\n");
+ }
}
}
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
index bcd341e..c905f95 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.appsearch.util.BundleUtil;
+import android.app.appsearch.util.IndentingStringBuilder;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.os.Parcelable;
@@ -50,9 +51,6 @@
public class GenericDocument {
private static final String TAG = "AppSearchGenericDocumen";
- /** The maximum number of elements in a repeatable field. */
- private static final int MAX_REPEATED_PROPERTY_LENGTH = 100;
-
/** The maximum {@link String#length} of a {@link String} field. */
private static final int MAX_STRING_LENGTH = 20_000;
@@ -861,6 +859,7 @@
*
* @hide
*/
+ // TODO(b/171882200): Expose this API in Android T
@NonNull
public GenericDocument.Builder<GenericDocument.Builder<?>> toBuilder() {
Bundle clonedBundle = BundleUtil.deepCopy(mBundle);
@@ -890,121 +889,101 @@
@Override
@NonNull
public String toString() {
- StringBuilder stringBuilder = new StringBuilder();
- appendGenericDocumentString(this, /*indentLevel=*/ 0, stringBuilder);
+ IndentingStringBuilder stringBuilder = new IndentingStringBuilder();
+ appendGenericDocumentString(stringBuilder);
return stringBuilder.toString();
}
- private static void appendGenericDocumentString(
- @NonNull GenericDocument document, int indentLevel, @NonNull StringBuilder builder) {
- Objects.requireNonNull(document);
+ /**
+ * Appends a debug string for the {@link GenericDocument} instance to the given string builder.
+ *
+ * @param builder the builder to append to.
+ */
+ void appendGenericDocumentString(@NonNull IndentingStringBuilder builder) {
Objects.requireNonNull(builder);
- builder.append(getIndent(indentLevel)).append("{\n");
+ builder.append("{\n");
+ builder.increaseIndentLevel();
- String indent1 = getIndent(indentLevel + 1);
-
- builder.append(indent1)
- .append("namespace: \"")
- .append(document.getNamespace())
- .append("\",\n");
-
- builder.append(indent1).append("id: \"").append(document.getId()).append("\",\n");
-
- builder.append(indent1).append("score: ").append(document.getScore()).append(",\n");
-
- builder.append(indent1)
- .append("schemaType: \"")
- .append(document.getSchemaType())
- .append("\",\n");
-
- builder.append(indent1)
- .append("creationTimestampMillis: ")
- .append(document.getCreationTimestampMillis())
+ builder.append("namespace: \"").append(getNamespace()).append("\",\n");
+ builder.append("id: \"").append(getId()).append("\",\n");
+ builder.append("score: ").append(getScore()).append(",\n");
+ builder.append("schemaType: \"").append(getSchemaType()).append("\",\n");
+ builder.append("creationTimestampMillis: ")
+ .append(getCreationTimestampMillis())
.append(",\n");
+ builder.append("timeToLiveMillis: ").append(getTtlMillis()).append(",\n");
- builder.append(indent1)
- .append("timeToLiveMillis: ")
- .append(document.getTtlMillis())
- .append(",\n");
+ builder.append("properties: {\n");
- builder.append(indent1).append("properties: {\n");
-
- String[] sortedProperties = document.getPropertyNames().toArray(new String[0]);
+ String[] sortedProperties = getPropertyNames().toArray(new String[0]);
Arrays.sort(sortedProperties);
for (int i = 0; i < sortedProperties.length; i++) {
- Object property = document.getProperty(sortedProperties[i]);
- builder.append(getIndent(indentLevel + 2))
- .append("\"")
- .append(sortedProperties[i])
- .append("\"")
- .append(": ");
- appendPropertyString(property, indentLevel + 2, builder);
+ Object property = getProperty(sortedProperties[i]);
+ builder.increaseIndentLevel();
+ appendPropertyString(sortedProperties[i], property, builder);
if (i != sortedProperties.length - 1) {
builder.append(",\n");
}
+ builder.decreaseIndentLevel();
}
builder.append("\n");
- builder.append(indent1).append("}");
+ builder.append("}");
+ builder.decreaseIndentLevel();
builder.append("\n");
- builder.append(getIndent(indentLevel)).append("}");
+ builder.append("}");
}
/**
- * Appends a string for the given property to the given builder.
+ * Appends a debug string for the given document property to the given string builder.
*
+ * @param propertyName name of property to create string for.
* @param property property object to create string for.
- * @param indentLevel base indent level for property.
* @param builder the builder to append to.
*/
- private static void appendPropertyString(
- @NonNull Object property, int indentLevel, @NonNull StringBuilder builder) {
+ private void appendPropertyString(
+ @NonNull String propertyName,
+ @NonNull Object property,
+ @NonNull IndentingStringBuilder builder) {
+ Objects.requireNonNull(propertyName);
Objects.requireNonNull(property);
Objects.requireNonNull(builder);
- builder.append("[");
+ builder.append("\"").append(propertyName).append("\": [");
if (property instanceof GenericDocument[]) {
GenericDocument[] documentValues = (GenericDocument[]) property;
for (int i = 0; i < documentValues.length; ++i) {
builder.append("\n");
- appendGenericDocumentString(documentValues[i], indentLevel + 1, builder);
+ builder.increaseIndentLevel();
+ documentValues[i].appendGenericDocumentString(builder);
if (i != documentValues.length - 1) {
- builder.append(", ");
+ builder.append(",");
}
builder.append("\n");
+ builder.decreaseIndentLevel();
}
- builder.append(getIndent(indentLevel));
+ builder.append("]");
} else {
int propertyArrLength = Array.getLength(property);
for (int i = 0; i < propertyArrLength; i++) {
Object propertyElement = Array.get(property, i);
if (propertyElement instanceof String) {
- builder.append("\"").append(propertyElement).append("\"");
+ builder.append("\"").append((String) propertyElement).append("\"");
} else if (propertyElement instanceof byte[]) {
builder.append(Arrays.toString((byte[]) propertyElement));
} else {
- builder.append(propertyElement);
+ builder.append(propertyElement.toString());
}
if (i != propertyArrLength - 1) {
builder.append(", ");
+ } else {
+ builder.append("]");
}
}
}
-
- builder.append("]");
- }
-
- /** Appends a string for given indent level to the given builder. */
- @NonNull
- private static String getIndent(int indentLevel) {
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < indentLevel; ++i) {
- builder.append(" ");
- }
- return builder.toString();
}
/**
@@ -1187,8 +1166,8 @@
* @param name the name associated with the {@code values}. Must match the name for this
* property as given in {@link AppSearchSchema.PropertyConfig#getName}.
* @param values the {@code String} values of the property.
- * @throws IllegalArgumentException if no values are provided, if provided values exceed
- * maximum repeated property length, or if a passed in {@code String} is {@code null}.
+ * @throws IllegalArgumentException if no values are provided, or if a passed in {@code
+ * String} is {@code null}.
*/
@NonNull
public BuilderType setPropertyString(@NonNull String name, @NonNull String... values) {
@@ -1206,7 +1185,6 @@
* @param name the name associated with the {@code values}. Must match the name for this
* property as given in {@link AppSearchSchema.PropertyConfig#getName}.
* @param values the {@code boolean} values of the property.
- * @throws IllegalArgumentException if values exceed maximum repeated property length.
*/
@NonNull
public BuilderType setPropertyBoolean(@NonNull String name, @NonNull boolean... values) {
@@ -1223,7 +1201,6 @@
* @param name the name associated with the {@code values}. Must match the name for this
* property as given in {@link AppSearchSchema.PropertyConfig#getName}.
* @param values the {@code long} values of the property.
- * @throws IllegalArgumentException if values exceed maximum repeated property length.
*/
@NonNull
public BuilderType setPropertyLong(@NonNull String name, @NonNull long... values) {
@@ -1240,7 +1217,6 @@
* @param name the name associated with the {@code values}. Must match the name for this
* property as given in {@link AppSearchSchema.PropertyConfig#getName}.
* @param values the {@code double} values of the property.
- * @throws IllegalArgumentException if values exceed maximum repeated property length.
*/
@NonNull
public BuilderType setPropertyDouble(@NonNull String name, @NonNull double... values) {
@@ -1257,8 +1233,8 @@
* @param name the name associated with the {@code values}. Must match the name for this
* property as given in {@link AppSearchSchema.PropertyConfig#getName}.
* @param values the {@code byte[]} of the property.
- * @throws IllegalArgumentException if no values are provided, if provided values exceed
- * maximum repeated property length, or if a passed in {@code byte[]} is {@code null}.
+ * @throws IllegalArgumentException if no values are provided, or if a passed in {@code
+ * byte[]} is {@code null}.
*/
@NonNull
public BuilderType setPropertyBytes(@NonNull String name, @NonNull byte[]... values) {
@@ -1276,8 +1252,7 @@
* @param name the name associated with the {@code values}. Must match the name for this
* property as given in {@link AppSearchSchema.PropertyConfig#getName}.
* @param values the {@link GenericDocument} values of the property.
- * @throws IllegalArgumentException if no values are provided, if provided values exceed if
- * provided values exceed maximum repeated property length, or if a passed in {@link
+ * @throws IllegalArgumentException if no values are provided, or if a passed in {@link
* GenericDocument} is {@code null}.
*/
@NonNull
@@ -1308,7 +1283,6 @@
private void putInPropertyBundle(@NonNull String name, @NonNull String[] values)
throws IllegalArgumentException {
- validateRepeatedPropertyLength(name, values.length);
for (int i = 0; i < values.length; i++) {
if (values[i] == null) {
throw new IllegalArgumentException("The String at " + i + " is null.");
@@ -1327,17 +1301,14 @@
}
private void putInPropertyBundle(@NonNull String name, @NonNull boolean[] values) {
- validateRepeatedPropertyLength(name, values.length);
mProperties.putBooleanArray(name, values);
}
private void putInPropertyBundle(@NonNull String name, @NonNull double[] values) {
- validateRepeatedPropertyLength(name, values.length);
mProperties.putDoubleArray(name, values);
}
private void putInPropertyBundle(@NonNull String name, @NonNull long[] values) {
- validateRepeatedPropertyLength(name, values.length);
mProperties.putLongArray(name, values);
}
@@ -1348,7 +1319,6 @@
* into ArrayList<Bundle>, and each elements will contain a one dimension byte[].
*/
private void putInPropertyBundle(@NonNull String name, @NonNull byte[][] values) {
- validateRepeatedPropertyLength(name, values.length);
ArrayList<Bundle> bundles = new ArrayList<>(values.length);
for (int i = 0; i < values.length; i++) {
if (values[i] == null) {
@@ -1362,7 +1332,6 @@
}
private void putInPropertyBundle(@NonNull String name, @NonNull GenericDocument[] values) {
- validateRepeatedPropertyLength(name, values.length);
Parcelable[] documentBundles = new Parcelable[values.length];
for (int i = 0; i < values.length; i++) {
if (values[i] == null) {
@@ -1373,18 +1342,6 @@
mProperties.putParcelableArray(name, documentBundles);
}
- private static void validateRepeatedPropertyLength(@NonNull String name, int length) {
- if (length > MAX_REPEATED_PROPERTY_LENGTH) {
- throw new IllegalArgumentException(
- "Repeated property \""
- + name
- + "\" has length "
- + length
- + ", which exceeds the limit of "
- + MAX_REPEATED_PROPERTY_LENGTH);
- }
- }
-
/** Builds the {@link GenericDocument} object. */
@NonNull
public GenericDocument build() {
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
index 0af3e7a..b72ca9a 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
@@ -169,13 +169,14 @@
/** Builder for {@link SetSchemaRequest} objects. */
public static final class Builder {
+ private static final int DEFAULT_VERSION = 1;
private ArraySet<AppSearchSchema> mSchemas = new ArraySet<>();
private ArraySet<String> mSchemasNotDisplayedBySystem = new ArraySet<>();
private ArrayMap<String, Set<PackageIdentifier>> mSchemasVisibleToPackages =
new ArrayMap<>();
private ArrayMap<String, Migrator> mMigrators = new ArrayMap<>();
private boolean mForceOverride = false;
- private int mVersion = 1;
+ private int mVersion = DEFAULT_VERSION;
private boolean mBuilt = false;
/**
@@ -384,6 +385,9 @@
* <p>The version number can stay the same, increase, or decrease relative to the current
* version number that is already stored in the {@link AppSearchSession} database.
*
+ * <p>The version of an empty database will always be 0. You cannot set version to the
+ * {@link SetSchemaRequest}, if it doesn't contains any {@link AppSearchSchema}.
+ *
* @param version A positive integer representing the version of the entire set of schemas
* represents the version of the whole schema in the {@link AppSearchSession} database,
* default version is 1.
@@ -423,7 +427,10 @@
throw new IllegalArgumentException(
"Schema types " + referencedSchemas + " referenced, but were not added.");
}
-
+ if (mSchemas.isEmpty() && mVersion != DEFAULT_VERSION) {
+ throw new IllegalArgumentException(
+ "Cannot set version to the request if schema is empty.");
+ }
mBuilt = true;
return new SetSchemaRequest(
mSchemas,
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/IndentingStringBuilder.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/IndentingStringBuilder.java
new file mode 100644
index 0000000..b494c3c
--- /dev/null
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/IndentingStringBuilder.java
@@ -0,0 +1,126 @@
+/*
+ * 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 android.app.appsearch.util;
+
+import android.annotation.NonNull;
+
+/**
+ * Utility for building indented strings.
+ *
+ * <p>This is a wrapper for {@link StringBuilder} for appending strings with indentation. The
+ * indentation level can be increased by calling {@link #increaseIndentLevel()} and decreased by
+ * calling {@link #decreaseIndentLevel()}.
+ *
+ * <p>Indentation is applied after each newline character for the given indent level.
+ *
+ * @hide
+ */
+public class IndentingStringBuilder {
+ private final StringBuilder mStringBuilder = new StringBuilder();
+
+ // Indicates whether next non-newline character should have an indent applied before it.
+ private boolean mIndentNext = false;
+ private int mIndentLevel = 0;
+
+ /** Increases the indent level by one for appended strings. */
+ @NonNull
+ public IndentingStringBuilder increaseIndentLevel() {
+ mIndentLevel++;
+ return this;
+ }
+
+ /** Decreases the indent level by one for appended strings. */
+ @NonNull
+ public IndentingStringBuilder decreaseIndentLevel() throws IllegalStateException {
+ if (mIndentLevel == 0) {
+ throw new IllegalStateException("Cannot set indent level below 0.");
+ }
+ mIndentLevel--;
+ return this;
+ }
+
+ /**
+ * Appends provided {@code String} at the current indentation level.
+ *
+ * <p>Indentation is applied after each newline character.
+ */
+ @NonNull
+ public IndentingStringBuilder append(@NonNull String str) {
+ applyIndentToString(str);
+ return this;
+ }
+
+ /**
+ * Appends provided {@code Object}, represented as a {@code String}, at the current indentation
+ * level.
+ *
+ * <p>Indentation is applied after each newline character.
+ */
+ @NonNull
+ public IndentingStringBuilder append(@NonNull Object obj) {
+ applyIndentToString(obj.toString());
+ return this;
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return mStringBuilder.toString();
+ }
+
+ /** Adds indent string to the {@link StringBuilder} instance for current indent level. */
+ private void applyIndent() {
+ for (int i = 0; i < mIndentLevel; i++) {
+ mStringBuilder.append(" ");
+ }
+ }
+
+ /**
+ * Applies indent, for current indent level, after each newline character.
+ *
+ * <p>Consecutive newline characters are not indented.
+ */
+ private void applyIndentToString(@NonNull String str) {
+ int index = str.indexOf("\n");
+ if (index == 0) {
+ // String begins with new line character: append newline and slide past newline.
+ mStringBuilder.append("\n");
+ mIndentNext = true;
+ if (str.length() > 1) {
+ applyIndentToString(str.substring(index + 1));
+ }
+ } else if (index >= 1) {
+ // String contains new line character: divide string between newline, append new line,
+ // and recurse on each string.
+ String beforeIndentString = str.substring(0, index);
+ applyIndentToString(beforeIndentString);
+ mStringBuilder.append("\n");
+ mIndentNext = true;
+ if (str.length() > index + 1) {
+ String afterIndentString = str.substring(index + 1);
+ applyIndentToString(afterIndentString);
+ }
+ } else {
+ // String does not contain newline character: append string.
+ if (mIndentNext) {
+ applyIndent();
+ mIndentNext = false;
+ }
+ mStringBuilder.append(str);
+ }
+ }
+}
diff --git a/apex/appsearch/service/Android.bp b/apex/appsearch/service/Android.bp
index 8d606c5..e5675f6 100644
--- a/apex/appsearch/service/Android.bp
+++ b/apex/appsearch/service/Android.bp
@@ -28,31 +28,41 @@
}
java_library {
- name: "service-appsearch",
- srcs: [
- "java/**/*.java",
- ":statslog-appsearch-java-gen",
+ name: "statslog-appsearch-lib",
+ srcs: [":statslog-appsearch-java-gen"],
+ libs: [
+ "framework-statsd.stubs.module_lib",
],
+ sdk_version: "system_server_current",
+ apex_available: ["com.android.appsearch"],
+}
+
+java_library {
+ name: "service-appsearch",
+ srcs: ["java/**/*.java"],
+ sdk_version: "system_server_current",
static_libs: [
"icing-java-proto-lite",
"libicing-java",
- // This list must be kept in sync with jarjar.txt
+ "statslog-appsearch-lib",
+ // Entries below this line are outside of the appsearch package tree and must be kept in
+ // sync with jarjar.txt
"modules-utils-preconditions",
],
libs: [
- "framework",
- "framework-appsearch",
- "framework-statsd.stubs.module_lib",
- "services.core",
- "services.usage",
+ "framework-appsearch.impl",
"unsupportedappusage", // TODO(b/181887768) should be removed
],
required: [
"libicing",
],
+ defaults: ["framework-system-server-module-defaults"],
+ permitted_packages: [
+ "com.android.server.appsearch",
+ "com.google.android.icing",
+ ],
jarjar_rules: "jarjar-rules.txt",
visibility: [
- "//frameworks/base/apex/appsearch:__subpackages__",
// These are required until appsearch is properly unbundled.
"//frameworks/base/services/tests/mockingservicestests",
"//frameworks/base/services/tests/servicestests",
diff --git a/apex/appsearch/service/jarjar-rules.txt b/apex/appsearch/service/jarjar-rules.txt
index 569d7c5..c79ea22 100644
--- a/apex/appsearch/service/jarjar-rules.txt
+++ b/apex/appsearch/service/jarjar-rules.txt
@@ -1,5 +1,8 @@
+# Rename all icing classes to match our module name. OEMs could start using icing lib for some other
+# purpose in system service, which would cause class collisions when loading our apex into the
+# system service.
rule com.google.protobuf.** com.android.server.appsearch.protobuf.@1
-rule com.google.android.icing.proto.** com.android.server.appsearch.proto.@1
+rule com.google.android.icing.proto.** com.android.server.appsearch.icing.proto.@1
# Rename all com.android.internal.util classes to prevent class name collisions
# between this module and the other versions of the utility classes linked into
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 e524429..ec37c3f 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -37,6 +37,7 @@
import android.app.appsearch.aidl.IAppSearchBatchResultCallback;
import android.app.appsearch.aidl.IAppSearchManager;
import android.app.appsearch.aidl.IAppSearchResultCallback;
+import android.app.appsearch.exceptions.AppSearchException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -58,10 +59,8 @@
import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalManagerRegistry;
import com.android.server.SystemService;
-import com.android.server.appsearch.external.localstorage.AppSearchImpl;
import com.android.server.appsearch.external.localstorage.stats.CallStats;
-import com.android.server.appsearch.stats.LoggerInstanceManager;
-import com.android.server.appsearch.stats.PlatformLogger;
+import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
import com.android.server.appsearch.util.PackageUtil;
import com.android.server.usage.StorageStatsManagerLocal;
import com.android.server.usage.StorageStatsManagerLocal.StorageStatsAugmenter;
@@ -83,14 +82,16 @@
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
-/** TODO(b/142567528): add comments when implement this class */
+/**
+ * The main service implementation which contains AppSearch's platform functionality.
+ * @hide
+ */
public class AppSearchManagerService extends SystemService {
private static final String TAG = "AppSearchManagerService";
private final Context mContext;
private PackageManager mPackageManager;
- private ImplInstanceManager mImplInstanceManager;
private UserManager mUserManager;
- private LoggerInstanceManager mLoggerInstanceManager;
+ private AppSearchUserInstanceManager mAppSearchUserInstanceManager;
// Never call shutdownNow(). It will cancel the futures it's returned. And since
// Executor#execute won't return anything, we will hang forever waiting for the execution.
@@ -115,9 +116,8 @@
public void onStart() {
publishBinderService(Context.APP_SEARCH_SERVICE, new Stub());
mPackageManager = getContext().getPackageManager();
- mImplInstanceManager = ImplInstanceManager.getInstance(mContext);
+ mAppSearchUserInstanceManager = AppSearchUserInstanceManager.getInstance();
mUserManager = mContext.getSystemService(UserManager.class);
- mLoggerInstanceManager = LoggerInstanceManager.getInstance();
registerReceivers();
LocalManagerRegistry.getManager(StorageStatsManagerLocal.class)
.registerStorageStatsAugmenter(new AppSearchStorageStatsAugmenter(), TAG);
@@ -179,8 +179,7 @@
*/
private void handleUserRemoved(@NonNull UserHandle userHandle) {
try {
- mImplInstanceManager.closeAndRemoveAppSearchImplForUser(userHandle);
- mLoggerInstanceManager.removePlatformLoggerForUser(userHandle);
+ mAppSearchUserInstanceManager.closeAndRemoveUserInstance(userHandle);
Log.i(TAG, "Removed AppSearchImpl instance for: " + userHandle);
} catch (Throwable t) {
Log.e(TAG, "Unable to remove data for: " + userHandle, t);
@@ -218,18 +217,19 @@
UserHandle userHandle = UserHandle.getUserHandleForUid(uid);
try {
if (isUserLocked(userHandle)) {
- //TODO(b/186151459) clear the uninstalled package data when user is unlocked.
+ // We cannot access a locked user's directry and remove package data from it.
+ // We should remove those uninstalled package data when the user is unlocking.
return;
}
- if (ImplInstanceManager.getAppSearchDir(userHandle).exists()) {
- // Only clear the package's data if AppSearch exists for this user.
- PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger(mContext,
- userHandle, AppSearchConfig.getInstance(EXECUTOR));
- AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(mContext,
- userHandle, logger);
+ // Only clear the package's data if AppSearch exists for this user.
+ if (AppSearchUserInstanceManager.getAppSearchDir(userHandle).exists()) {
+ Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0);
+ AppSearchUserInstance instance =
+ mAppSearchUserInstanceManager.getOrCreateUserInstance(
+ userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR));
//TODO(b/145759910) clear visibility setting for package.
- impl.clearPackageData(packageName);
- logger.removeCachedUidForPackage(packageName);
+ instance.getAppSearchImpl().clearPackageData(packageName);
+ instance.getLogger().removeCachedUidForPackage(packageName);
}
} catch (Throwable t) {
Log.e(TAG, "Unable to remove data for package: " + packageName, t);
@@ -239,9 +239,33 @@
@Override
public void onUserUnlocking(@NonNull TargetUser user) {
Objects.requireNonNull(user);
+ UserHandle userHandle = user.getUserHandle();
synchronized (mUnlockedUsersLocked) {
- mUnlockedUsersLocked.add(user.getUserHandle());
+ mUnlockedUsersLocked.add(userHandle);
}
+ EXECUTOR.execute(() -> {
+ try {
+ // Only clear the package's data if AppSearch exists for this user.
+ if (AppSearchUserInstanceManager.getAppSearchDir(userHandle).exists()) {
+ Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0);
+ AppSearchUserInstance instance =
+ mAppSearchUserInstanceManager.getOrCreateUserInstance(
+ userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR));
+ List<PackageInfo> installedPackageInfos = userContext
+ .getPackageManager()
+ .getInstalledPackages(/*flags=*/0);
+ Set<String> packagesToKeep = new ArraySet<>(installedPackageInfos.size());
+ for (int i = 0; i < installedPackageInfos.size(); i++) {
+ packagesToKeep.add(installedPackageInfos.get(i).packageName);
+ }
+ packagesToKeep.add(VisibilityStore.PACKAGE_NAME);
+ //TODO(b/145759910) clear visibility setting for package.
+ instance.getAppSearchImpl().prunePackageData(packagesToKeep);
+ }
+ } catch (Throwable t) {
+ Log.e(TAG, "Unable to prune packages for " + user, t);
+ }
+ });
}
@Override
@@ -252,7 +276,7 @@
UserHandle userHandle = user.getUserHandle();
mUnlockedUsersLocked.remove(userHandle);
try {
- mImplInstanceManager.closeAndRemoveAppSearchImplForUser(userHandle);
+ mAppSearchUserInstanceManager.closeAndRemoveUserInstance(userHandle);
} catch (Throwable t) {
Log.e(TAG, "Error handling user stopping.", t);
}
@@ -284,7 +308,7 @@
@NonNull String databaseName,
@NonNull List<Bundle> schemaBundles,
@NonNull List<String> schemasNotDisplayedBySystem,
- @NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles,
+ @NonNull Map<String, List<Bundle>> schemasVisibleToPackagesBundles,
boolean forceOverride,
int schemaVersion,
@NonNull UserHandle userHandle,
@@ -294,7 +318,7 @@
Objects.requireNonNull(databaseName);
Objects.requireNonNull(schemaBundles);
Objects.requireNonNull(schemasNotDisplayedBySystem);
- Objects.requireNonNull(schemasPackageAccessibleBundles);
+ Objects.requireNonNull(schemasVisibleToPackagesBundles);
Objects.requireNonNull(userHandle);
Objects.requireNonNull(callback);
@@ -303,36 +327,38 @@
UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
- PlatformLogger logger = null;
+ AppSearchUserInstance instance = null;
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
+ Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
verifyUserUnlocked(callingUser);
- verifyCallingPackage(callingUser, callingUid, packageName);
+ verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+ verifyNotInstantApp(userContext, packageName);
List<AppSearchSchema> schemas = new ArrayList<>(schemaBundles.size());
for (int i = 0; i < schemaBundles.size(); i++) {
schemas.add(new AppSearchSchema(schemaBundles.get(i)));
}
- Map<String, List<PackageIdentifier>> schemasPackageAccessible =
- new ArrayMap<>(schemasPackageAccessibleBundles.size());
+ Map<String, List<PackageIdentifier>> schemasVisibleToPackages =
+ new ArrayMap<>(schemasVisibleToPackagesBundles.size());
for (Map.Entry<String, List<Bundle>> entry :
- schemasPackageAccessibleBundles.entrySet()) {
+ schemasVisibleToPackagesBundles.entrySet()) {
List<PackageIdentifier> packageIdentifiers =
new ArrayList<>(entry.getValue().size());
for (int i = 0; i < entry.getValue().size(); i++) {
packageIdentifiers.add(
new PackageIdentifier(entry.getValue().get(i)));
}
- schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
+ schemasVisibleToPackages.put(entry.getKey(), packageIdentifiers);
}
- AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser);
- logger = mLoggerInstanceManager.getPlatformLogger(callingUser);
- SetSchemaResponse setSchemaResponse = impl.setSchema(
+ instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ SetSchemaResponse setSchemaResponse = instance.getAppSearchImpl().setSchema(
packageName,
databaseName,
schemas,
+ instance.getVisibilityStore(),
schemasNotDisplayedBySystem,
- schemasPackageAccessible,
+ schemasVisibleToPackages,
forceOverride,
schemaVersion);
++operationSuccessCount;
@@ -343,12 +369,12 @@
statusCode = throwableToFailedResult(t).getResultCode();
invokeCallbackOnError(callback, t);
} finally {
- if (logger != null) {
+ if (instance != null) {
int estimatedBinderLatencyMillis =
2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
int totalLatencyMillis =
(int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
- logger.logStats(new CallStats.Builder()
+ instance.getLogger().logStats(new CallStats.Builder()
.setPackageName(packageName)
.setDatabase(databaseName)
.setStatusCode(statusCode)
@@ -381,10 +407,14 @@
UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
try {
+ Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
verifyUserUnlocked(callingUser);
- verifyCallingPackage(callingUser, callingUid, packageName);
- AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser);
- GetSchemaResponse response = impl.getSchema(packageName, databaseName);
+ verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+ verifyNotInstantApp(userContext, packageName);
+ AppSearchUserInstance instance =
+ mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ GetSchemaResponse response =
+ instance.getAppSearchImpl().getSchema(packageName, databaseName);
invokeCallbackOnResult(
callback,
AppSearchResult.newSuccessfulResult(response.getBundle()));
@@ -409,12 +439,16 @@
UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
try {
+ Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
verifyUserUnlocked(callingUser);
- verifyCallingPackage(callingUser, callingUid, packageName);
- AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser);
- List<String> namespaces = impl.getNamespaces(packageName, databaseName);
- invokeCallbackOnResult(callback,
- AppSearchResult.newSuccessfulResult(namespaces));
+ verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+ verifyNotInstantApp(userContext, packageName);
+ AppSearchUserInstance instance =
+ mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ List<String> namespaces =
+ instance.getAppSearchImpl().getNamespaces(packageName, databaseName);
+ invokeCallbackOnResult(
+ callback, AppSearchResult.newSuccessfulResult(namespaces));
} catch (Throwable t) {
invokeCallbackOnError(callback, t);
}
@@ -440,25 +474,26 @@
UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
- PlatformLogger logger = null;
+ AppSearchUserInstance instance = null;
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
+ Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
verifyUserUnlocked(callingUser);
- verifyCallingPackage(callingUser, callingUid, packageName);
+ verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+ verifyNotInstantApp(userContext, packageName);
AppSearchBatchResult.Builder<String, Void> resultBuilder =
new AppSearchBatchResult.Builder<>();
- AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser);
- logger = mLoggerInstanceManager.getPlatformLogger(callingUser);
+ instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
for (int i = 0; i < documentBundles.size(); i++) {
GenericDocument document = new GenericDocument(documentBundles.get(i));
try {
- impl.putDocument(packageName, databaseName, document, logger);
- resultBuilder.setSuccess(document.getId(), /*result=*/ null);
+ instance.getAppSearchImpl().putDocument(
+ packageName, databaseName, document, instance.getLogger());
+ resultBuilder.setSuccess(document.getId(), /*value=*/ null);
++operationSuccessCount;
} catch (Throwable t) {
- resultBuilder.setResult(document.getId(),
- throwableToFailedResult(t));
+ resultBuilder.setResult(document.getId(), throwableToFailedResult(t));
AppSearchResult<Void> result = throwableToFailedResult(t);
resultBuilder.setResult(document.getId(), result);
// Since we can only include one status code in the atom,
@@ -468,19 +503,19 @@
}
}
// Now that the batch has been written. Persist the newly written data.
- impl.persistToDisk(PersistType.Code.LITE);
+ instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE);
invokeCallbackOnResult(callback, resultBuilder.build());
} catch (Throwable t) {
++operationFailureCount;
statusCode = throwableToFailedResult(t).getResultCode();
invokeCallbackOnError(callback, t);
} finally {
- if (logger != null) {
+ if (instance != null) {
int estimatedBinderLatencyMillis =
2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
int totalLatencyMillis =
(int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
- logger.logStats(new CallStats.Builder()
+ instance.getLogger().logStats(new CallStats.Builder()
.setPackageName(packageName)
.setDatabase(databaseName)
.setStatusCode(statusCode)
@@ -521,26 +556,26 @@
UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
- PlatformLogger logger = null;
+ AppSearchUserInstance instance = null;
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
+ Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
verifyUserUnlocked(callingUser);
- verifyCallingPackage(callingUser, callingUid, packageName);
+ verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+ verifyNotInstantApp(userContext, packageName);
AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
new AppSearchBatchResult.Builder<>();
- AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser);
- logger = mLoggerInstanceManager.getPlatformLogger(callingUser);
+ instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
for (int i = 0; i < ids.size(); i++) {
String id = ids.get(i);
try {
- GenericDocument document =
- impl.getDocument(
- packageName,
- databaseName,
- namespace,
- id,
- typePropertyPaths);
+ GenericDocument document = instance.getAppSearchImpl().getDocument(
+ packageName,
+ databaseName,
+ namespace,
+ id,
+ typePropertyPaths);
++operationSuccessCount;
resultBuilder.setSuccess(id, document.getBundle());
} catch (Throwable t) {
@@ -558,12 +593,12 @@
statusCode = throwableToFailedResult(t).getResultCode();
invokeCallbackOnError(callback, t);
} finally {
- if (logger != null) {
+ if (instance != null) {
int estimatedBinderLatencyMillis =
2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
int totalLatencyMillis =
(int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
- logger.logStats(new CallStats.Builder()
+ instance.getLogger().logStats(new CallStats.Builder()
.setPackageName(packageName)
.setDatabase(databaseName)
.setStatusCode(statusCode)
@@ -602,21 +637,21 @@
UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
- PlatformLogger logger = null;
+ AppSearchUserInstance instance = null;
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
+ Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
verifyUserUnlocked(callingUser);
- verifyCallingPackage(callingUser, callingUid, packageName);
- AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser);
- logger = mLoggerInstanceManager.getPlatformLogger(callingUser);
- SearchResultPage searchResultPage =
- impl.query(
- packageName,
- databaseName,
- queryExpression,
- new SearchSpec(searchSpecBundle),
- logger);
+ verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+ verifyNotInstantApp(userContext, packageName);
+ instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ SearchResultPage searchResultPage = instance.getAppSearchImpl().query(
+ packageName,
+ databaseName,
+ queryExpression,
+ new SearchSpec(searchSpecBundle),
+ instance.getLogger());
++operationSuccessCount;
invokeCallbackOnResult(
callback,
@@ -626,12 +661,12 @@
statusCode = throwableToFailedResult(t).getResultCode();
invokeCallbackOnError(callback, t);
} finally {
- if (logger != null) {
+ if (instance != null) {
int estimatedBinderLatencyMillis =
2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
int totalLatencyMillis =
(int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
- logger.logStats(new CallStats.Builder()
+ instance.getLogger().logStats(new CallStats.Builder()
.setPackageName(packageName)
.setDatabase(databaseName)
.setStatusCode(statusCode)
@@ -668,21 +703,26 @@
UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
- PlatformLogger logger = null;
+ AppSearchUserInstance instance = null;
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
+ Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
verifyUserUnlocked(callingUser);
- verifyCallingPackage(callingUser, callingUid, packageName);
- logger = mLoggerInstanceManager.getPlatformLogger(callingUser);
- AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser);
- SearchResultPage searchResultPage =
- impl.globalQuery(
- queryExpression,
- new SearchSpec(searchSpecBundle),
- packageName,
- callingUid,
- logger);
+ verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+ verifyNotInstantApp(userContext, packageName);
+ instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+
+ boolean callerHasSystemAccess =
+ instance.getVisibilityStore().doesCallerHaveSystemAccess(packageName);
+ SearchResultPage searchResultPage = instance.getAppSearchImpl().globalQuery(
+ queryExpression,
+ new SearchSpec(searchSpecBundle),
+ packageName,
+ instance.getVisibilityStore(),
+ callingUid,
+ callerHasSystemAccess,
+ instance.getLogger());
++operationSuccessCount;
invokeCallbackOnResult(
callback,
@@ -692,12 +732,12 @@
statusCode = throwableToFailedResult(t).getResultCode();
invokeCallbackOnError(callback, t);
} finally {
- if (logger != null) {
+ if (instance != null) {
int estimatedBinderLatencyMillis =
2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
int totalLatencyMillis =
(int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
- logger.logStats(new CallStats.Builder()
+ instance.getLogger().logStats(new CallStats.Builder()
.setPackageName(packageName)
.setStatusCode(statusCode)
.setTotalLatencyMillis(totalLatencyMillis)
@@ -716,9 +756,11 @@
@Override
public void getNextPage(
+ @NonNull String packageName,
long nextPageToken,
@NonNull UserHandle userHandle,
@NonNull IAppSearchResultCallback callback) {
+ Objects.requireNonNull(packageName);
Objects.requireNonNull(userHandle);
Objects.requireNonNull(callback);
@@ -728,9 +770,14 @@
// opened it
EXECUTOR.execute(() -> {
try {
+ Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
verifyUserUnlocked(callingUser);
- AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser);
- SearchResultPage searchResultPage = impl.getNextPage(nextPageToken);
+ verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+ verifyNotInstantApp(userContext, packageName);
+ AppSearchUserInstance instance =
+ mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ SearchResultPage searchResultPage =
+ instance.getAppSearchImpl().getNextPage(nextPageToken);
invokeCallbackOnResult(
callback,
AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
@@ -741,16 +788,22 @@
}
@Override
- public void invalidateNextPageToken(long nextPageToken, @NonNull UserHandle userHandle) {
+ public void invalidateNextPageToken(@NonNull String packageName, long nextPageToken,
+ @NonNull UserHandle userHandle) {
+ Objects.requireNonNull(packageName);
Objects.requireNonNull(userHandle);
int callingUid = Binder.getCallingUid();
UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
try {
+ Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
verifyUserUnlocked(callingUser);
- AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser);
- impl.invalidateNextPageToken(nextPageToken);
+ verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+ verifyNotInstantApp(userContext, packageName);
+ AppSearchUserInstance instance =
+ mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ instance.getAppSearchImpl().invalidateNextPageToken(nextPageToken);
} catch (Throwable t) {
Log.e(TAG, "Unable to invalidate the query page token", t);
}
@@ -778,12 +831,16 @@
UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
try {
- verifyCallingPackage(callingUser, callingUid, packageName);
- AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser);
+ Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
+ verifyUserUnlocked(callingUser);
+ verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+ verifyNotInstantApp(userContext, packageName);
+ AppSearchUserInstance instance =
+ mAppSearchUserInstanceManager.getUserInstance(callingUser);
// we don't need to append the file. The file is always brand new.
try (DataOutputStream outputStream = new DataOutputStream(
new FileOutputStream(fileDescriptor.getFileDescriptor()))) {
- SearchResultPage searchResultPage = impl.query(
+ SearchResultPage searchResultPage = instance.getAppSearchImpl().query(
packageName,
databaseName,
queryExpression,
@@ -795,7 +852,7 @@
outputStream, searchResultPage.getResults().get(i)
.getGenericDocument().getBundle());
}
- searchResultPage = impl.getNextPage(
+ searchResultPage = instance.getAppSearchImpl().getNextPage(
searchResultPage.getNextPageToken());
}
}
@@ -823,8 +880,12 @@
UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
try {
- verifyCallingPackage(callingUser, callingUid, packageName);
- AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser);
+ Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
+ verifyUserUnlocked(callingUser);
+ verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+ verifyNotInstantApp(userContext, packageName);
+ AppSearchUserInstance instance =
+ mAppSearchUserInstanceManager.getUserInstance(callingUser);
GenericDocument document;
ArrayList<Bundle> migrationFailureBundles = new ArrayList<>();
@@ -839,8 +900,8 @@
break;
}
try {
- impl.putDocument(packageName, databaseName, document,
- /*logger=*/ null);
+ instance.getAppSearchImpl().putDocument(
+ packageName, databaseName, document, /*logger=*/ null);
} catch (Throwable t) {
migrationFailureBundles.add(new SetSchemaResponse.MigrationFailure(
document.getNamespace(),
@@ -851,7 +912,7 @@
}
}
}
- impl.persistToDisk(PersistType.Code.FULL);
+ instance.getAppSearchImpl().persistToDisk(PersistType.Code.FULL);
invokeCallbackOnResult(callback,
AppSearchResult.newSuccessfulResult(migrationFailureBundles));
} catch (Throwable t) {
@@ -881,18 +942,26 @@
UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
try {
+ Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
verifyUserUnlocked(callingUser);
+ verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+ verifyNotInstantApp(userContext, packageName);
+ AppSearchUserInstance instance =
+ mAppSearchUserInstanceManager.getUserInstance(callingUser);
- if (systemUsage) {
- // TODO(b/183031844): Validate that the call comes from the system
+ if (systemUsage
+ && !instance.getVisibilityStore()
+ .doesCallerHaveSystemAccess(packageName)) {
+ throw new AppSearchException(
+ AppSearchResult.RESULT_SECURITY_ERROR,
+ packageName + " does not have access to report system usage");
}
- AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser);
- impl.reportUsage(
+ instance.getAppSearchImpl().reportUsage(
packageName, databaseName, namespace, documentId,
usageTimeMillis, systemUsage);
invokeCallbackOnResult(
- callback, AppSearchResult.newSuccessfulResult(/*result=*/ null));
+ callback, AppSearchResult.newSuccessfulResult(/*value=*/ null));
} catch (Throwable t) {
invokeCallbackOnError(callback, t);
}
@@ -920,20 +989,21 @@
UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
- PlatformLogger logger = null;
+ AppSearchUserInstance instance = null;
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
+ Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
verifyUserUnlocked(callingUser);
- verifyCallingPackage(callingUser, callingUid, packageName);
+ verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+ verifyNotInstantApp(userContext, packageName);
AppSearchBatchResult.Builder<String, Void> resultBuilder =
new AppSearchBatchResult.Builder<>();
- AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser);
- logger = mLoggerInstanceManager.getPlatformLogger(callingUser);
+ instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
for (int i = 0; i < ids.size(); i++) {
String id = ids.get(i);
try {
- impl.remove(
+ instance.getAppSearchImpl().remove(
packageName,
databaseName,
namespace,
@@ -951,19 +1021,19 @@
}
}
// Now that the batch has been written. Persist the newly written data.
- impl.persistToDisk(PersistType.Code.LITE);
+ instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE);
invokeCallbackOnResult(callback, resultBuilder.build());
} catch (Throwable t) {
++operationFailureCount;
statusCode = throwableToFailedResult(t).getResultCode();
invokeCallbackOnError(callback, t);
} finally {
- if (logger != null) {
+ if (instance != null) {
int estimatedBinderLatencyMillis =
2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
int totalLatencyMillis =
(int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
- logger.logStats(new CallStats.Builder()
+ instance.getLogger().logStats(new CallStats.Builder()
.setPackageName(packageName)
.setDatabase(databaseName)
.setStatusCode(statusCode)
@@ -1003,22 +1073,23 @@
UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
- PlatformLogger logger = null;
+ AppSearchUserInstance instance = null;
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
+ Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
verifyUserUnlocked(callingUser);
- verifyCallingPackage(callingUser, callingUid, packageName);
- AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser);
- logger = mLoggerInstanceManager.getPlatformLogger(callingUser);
- impl.removeByQuery(
+ verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+ verifyNotInstantApp(userContext, packageName);
+ instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ instance.getAppSearchImpl().removeByQuery(
packageName,
databaseName,
queryExpression,
new SearchSpec(searchSpecBundle),
/*removeStatsBuilder=*/ null);
// Now that the batch has been written. Persist the newly written data.
- impl.persistToDisk(PersistType.Code.LITE);
+ instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE);
++operationSuccessCount;
invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
} catch (Throwable t) {
@@ -1026,12 +1097,12 @@
statusCode = throwableToFailedResult(t).getResultCode();
invokeCallbackOnError(callback, t);
} finally {
- if (logger != null) {
+ if (instance != null) {
int estimatedBinderLatencyMillis =
2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
int totalLatencyMillis =
(int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
- logger.logStats(new CallStats.Builder()
+ instance.getLogger().logStats(new CallStats.Builder()
.setPackageName(packageName)
.setDatabase(databaseName)
.setStatusCode(statusCode)
@@ -1064,11 +1135,14 @@
UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
try {
+ Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
verifyUserUnlocked(callingUser);
- verifyCallingPackage(callingUser, callingUid, packageName);
- AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser);
- StorageInfo storageInfo = impl.getStorageInfoForDatabase(packageName,
- databaseName);
+ verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+ verifyNotInstantApp(userContext, packageName);
+ AppSearchUserInstance instance =
+ mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ StorageInfo storageInfo = instance.getAppSearchImpl()
+ .getStorageInfoForDatabase(packageName, databaseName);
Bundle storageInfoBundle = storageInfo.getBundle();
invokeCallbackOnResult(
callback, AppSearchResult.newSuccessfulResult(storageInfoBundle));
@@ -1080,8 +1154,10 @@
@Override
public void persistToDisk(
+ @NonNull String packageName,
@NonNull UserHandle userHandle,
@ElapsedRealtimeLong long binderCallStartTimeMillis) {
+ Objects.requireNonNull(packageName);
Objects.requireNonNull(userHandle);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
@@ -1089,26 +1165,28 @@
UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
- PlatformLogger logger = null;
+ AppSearchUserInstance instance = null;
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
+ Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
verifyUserUnlocked(callingUser);
- AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUser);
- logger = mLoggerInstanceManager.getPlatformLogger(callingUser);
- impl.persistToDisk(PersistType.Code.FULL);
+ verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+ verifyNotInstantApp(userContext, packageName);
+ instance = mAppSearchUserInstanceManager.getUserInstance(callingUser);
+ instance.getAppSearchImpl().persistToDisk(PersistType.Code.FULL);
++operationSuccessCount;
} catch (Throwable t) {
++operationFailureCount;
statusCode = throwableToFailedResult(t).getResultCode();
Log.e(TAG, "Unable to persist the data to disk", t);
} finally {
- if (logger != null) {
+ if (instance != null) {
int estimatedBinderLatencyMillis =
2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
int totalLatencyMillis =
(int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
- logger.logStats(new CallStats.Builder()
+ instance.getLogger().logStats(new CallStats.Builder()
.setStatusCode(statusCode)
.setTotalLatencyMillis(totalLatencyMillis)
.setCallType(CallStats.CALL_TYPE_FLUSH)
@@ -1126,26 +1204,30 @@
@Override
public void initialize(
+ @NonNull String packageName,
@NonNull UserHandle userHandle,
@ElapsedRealtimeLong long binderCallStartTimeMillis,
@NonNull IAppSearchResultCallback callback) {
+ Objects.requireNonNull(packageName);
Objects.requireNonNull(userHandle);
Objects.requireNonNull(callback);
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
int callingUid = Binder.getCallingUid();
UserHandle callingUser = handleIncomingUser(userHandle, callingUid);
+
EXECUTOR.execute(() -> {
@AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
- PlatformLogger logger = null;
+ AppSearchUserInstance instance = null;
int operationSuccessCount = 0;
int operationFailureCount = 0;
try {
+ Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0);
verifyUserUnlocked(callingUser);
- logger = mLoggerInstanceManager.getOrCreatePlatformLogger(
- mContext, callingUser,
- AppSearchConfig.getInstance(EXECUTOR));
- mImplInstanceManager.getOrCreateAppSearchImpl(mContext, callingUser, logger);
+ verifyCallingPackage(userContext, callingUser, callingUid, packageName);
+ verifyNotInstantApp(userContext, packageName);
+ instance = mAppSearchUserInstanceManager.getOrCreateUserInstance(
+ userContext, callingUser, AppSearchConfig.getInstance(EXECUTOR));
++operationSuccessCount;
invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
} catch (Throwable t) {
@@ -1153,12 +1235,12 @@
statusCode = throwableToFailedResult(t).getResultCode();
invokeCallbackOnError(callback, t);
} finally {
- if (logger != null) {
+ if (instance != null) {
int estimatedBinderLatencyMillis =
2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis);
int totalLatencyMillis =
(int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis);
- logger.logStats(new CallStats.Builder()
+ instance.getLogger().logStats(new CallStats.Builder()
.setStatusCode(statusCode)
.setTotalLatencyMillis(totalLatencyMillis)
.setCallType(CallStats.CALL_TYPE_INITIALIZE)
@@ -1175,14 +1257,15 @@
}
private void verifyCallingPackage(
+ @NonNull Context userContext,
@NonNull UserHandle actualCallingUser,
int actualCallingUid,
@NonNull String claimedCallingPackage) {
Objects.requireNonNull(actualCallingUser);
Objects.requireNonNull(claimedCallingPackage);
- int claimedCallingUid = PackageUtil.getPackageUidAsUser(
- mContext, claimedCallingPackage, actualCallingUser);
+ int claimedCallingUid = PackageUtil.getPackageUid(
+ userContext, claimedCallingPackage);
if (claimedCallingUid == INVALID_UID) {
throw new SecurityException(
"Specified calling package [" + claimedCallingPackage + "] not found");
@@ -1255,9 +1338,6 @@
* @param callingUid The actual uid of the caller as determined by Binder.
* @return the user handle that the call should run as. Will always be a concrete user.
*/
- // TODO(b/173553485) verifying that the caller has permission to access target user's data
- // TODO(b/173553485) Handle ACTION_USER_REMOVED broadcast
- // TODO(b/173553485) Implement SystemService.onUserStopping()
@NonNull
private UserHandle handleIncomingUser(@NonNull UserHandle requestedUser, int callingUid) {
int callingPid = Binder.getCallingPid();
@@ -1291,6 +1371,21 @@
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL);
}
+ /**
+ * Helper for ensuring instant apps can't make calls to AppSearch.
+ *
+ * @param userContext Context of the user making the call.
+ * @param packageName Package name of the caller.
+ * @throws SecurityException if the caller is an instant app.
+ */
+ private void verifyNotInstantApp(@NonNull Context userContext, @NonNull String packageName) {
+ PackageManager callingPackageManager = userContext.getPackageManager();
+ if (callingPackageManager.isInstantApp(packageName)) {
+ throw new SecurityException("Caller not allowed to create AppSearch session"
+ + "; userHandle=" + userContext.getUser() + ", callingPackage=" + packageName);
+ }
+ }
+
// TODO(b/179160886): Cache the previous storage stats.
private class AppSearchStorageStatsAugmenter implements StorageStatsAugmenter {
@Override
@@ -1305,12 +1400,12 @@
try {
verifyUserUnlocked(userHandle);
- PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger(
- mContext, userHandle,
- AppSearchConfig.getInstance(EXECUTOR));
- AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(
- mContext, userHandle, logger);
- stats.dataSize += impl.getStorageInfoForPackage(packageName).getSizeBytes();
+ Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0);
+ AppSearchUserInstance instance =
+ mAppSearchUserInstanceManager.getOrCreateUserInstance(
+ userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR));
+ stats.dataSize += instance.getAppSearchImpl()
+ .getStorageInfoForPackage(packageName).getSizeBytes();
} catch (Throwable t) {
Log.e(
TAG,
@@ -1334,14 +1429,13 @@
if (packagesForUid == null) {
return;
}
- PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger(
- mContext, userHandle,
- AppSearchConfig.getInstance(EXECUTOR));
- AppSearchImpl impl = mImplInstanceManager.getOrCreateAppSearchImpl(
- mContext, userHandle, logger);
+ Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0);
+ AppSearchUserInstance instance =
+ mAppSearchUserInstanceManager.getOrCreateUserInstance(
+ userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR));
for (int i = 0; i < packagesForUid.length; i++) {
- stats.dataSize +=
- impl.getStorageInfoForPackage(packagesForUid[i]).getSizeBytes();
+ stats.dataSize += instance.getAppSearchImpl()
+ .getStorageInfoForPackage(packagesForUid[i]).getSizeBytes();
}
} catch (Throwable t) {
Log.e(TAG, "Unable to augment storage stats for uid " + uid, t);
@@ -1364,14 +1458,14 @@
if (packagesForUser == null) {
return;
}
- PlatformLogger logger = mLoggerInstanceManager.getOrCreatePlatformLogger(
- mContext, userHandle,
- AppSearchConfig.getInstance(EXECUTOR));
- AppSearchImpl impl =
- mImplInstanceManager.getOrCreateAppSearchImpl(mContext, userHandle, logger);
+ Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0);
+ AppSearchUserInstance instance =
+ mAppSearchUserInstanceManager.getOrCreateUserInstance(
+ userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR));
for (int i = 0; i < packagesForUser.size(); i++) {
String packageName = packagesForUser.get(i).packageName;
- stats.dataSize += impl.getStorageInfoForPackage(packageName).getSizeBytes();
+ stats.dataSize += instance.getAppSearchImpl()
+ .getStorageInfoForPackage(packageName).getSizeBytes();
}
} catch (Throwable t) {
Log.e(TAG, "Unable to augment storage stats for " + userHandle, t);
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java
new file mode 100644
index 0000000..56e2af5
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.appsearch;
+
+import android.annotation.NonNull;
+
+import com.android.server.appsearch.external.localstorage.AppSearchImpl;
+import com.android.server.appsearch.stats.PlatformLogger;
+import com.android.server.appsearch.visibilitystore.VisibilityStoreImpl;
+
+import java.util.Objects;
+
+/**
+ * Container for AppSearch classes that should only be initialized once per device-user and make up
+ * the core of the AppSearch system.
+ */
+public final class AppSearchUserInstance {
+ private final PlatformLogger mLogger;
+ private final AppSearchImpl mAppSearchImpl;
+ private final VisibilityStoreImpl mVisibilityStore;
+
+ AppSearchUserInstance(
+ @NonNull PlatformLogger logger,
+ @NonNull AppSearchImpl appSearchImpl,
+ @NonNull VisibilityStoreImpl visibilityStore) {
+ mLogger = Objects.requireNonNull(logger);
+ mAppSearchImpl = Objects.requireNonNull(appSearchImpl);
+ mVisibilityStore = Objects.requireNonNull(visibilityStore);
+ }
+
+ @NonNull
+ public PlatformLogger getLogger() {
+ return mLogger;
+ }
+
+ @NonNull
+ public AppSearchImpl getAppSearchImpl() {
+ return mAppSearchImpl;
+ }
+
+ @NonNull
+ public VisibilityStoreImpl getVisibilityStore() {
+ return mVisibilityStore;
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java
new file mode 100644
index 0000000..e067d4b
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appsearch;
+
+import android.annotation.NonNull;
+import android.app.appsearch.exceptions.AppSearchException;
+import android.content.Context;
+import android.os.Environment;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.appsearch.external.localstorage.AppSearchImpl;
+import com.android.server.appsearch.external.localstorage.FrameworkOptimizeStrategy;
+import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
+import com.android.server.appsearch.stats.PlatformLogger;
+import com.android.server.appsearch.visibilitystore.VisibilityStoreImpl;
+
+import java.io.File;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Manages the lifecycle of AppSearch classes that should only be initialized once per device-user
+ * and make up the core of the AppSearch system.
+ *
+ * @hide
+ */
+public final class AppSearchUserInstanceManager {
+ private static final String TAG = "AppSearchUserInstanceMa";
+
+ private static volatile AppSearchUserInstanceManager sAppSearchUserInstanceManager;
+
+ @GuardedBy("mInstancesLocked")
+ private final Map<UserHandle, AppSearchUserInstance> mInstancesLocked = new ArrayMap<>();
+
+ private AppSearchUserInstanceManager() {}
+
+ /**
+ * Gets an instance of AppSearchUserInstanceManager to be used.
+ *
+ * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the
+ * existing instance will be returned.
+ */
+ @NonNull
+ public static AppSearchUserInstanceManager getInstance() {
+ if (sAppSearchUserInstanceManager == null) {
+ synchronized (AppSearchUserInstanceManager.class) {
+ if (sAppSearchUserInstanceManager == null) {
+ sAppSearchUserInstanceManager = new AppSearchUserInstanceManager();
+ }
+ }
+ }
+ return sAppSearchUserInstanceManager;
+ }
+
+ /**
+ * Returns AppSearch directory in the credential encrypted system directory for the given user.
+ *
+ * <p>This folder should only be accessed after unlock.
+ */
+ public static File getAppSearchDir(@NonNull UserHandle userHandle) {
+ // Duplicates the implementation of Environment#getDataSystemCeDirectory
+ // TODO(b/191059409): Unhide Environment#getDataSystemCeDirectory and switch to it.
+ File systemCeDir = new File(Environment.getDataDirectory(), "system_ce");
+ File systemCeUserDir = new File(systemCeDir, String.valueOf(userHandle.getIdentifier()));
+ return new File(systemCeUserDir, "appsearch");
+ }
+
+ /**
+ * Gets an instance of AppSearchUserInstance for the given user, or creates one if none exists.
+ *
+ * <p>If no AppSearchUserInstance exists for the unlocked user, Icing will be initialized and
+ * one will be created.
+ *
+ * @param userContext Context of the user calling AppSearch
+ * @param userHandle The multi-user handle of the device user calling AppSearch
+ * @param config Flag manager for AppSearch
+ * @return An initialized {@link AppSearchUserInstance} for this user
+ */
+ @NonNull
+ public AppSearchUserInstance getOrCreateUserInstance(
+ @NonNull Context userContext,
+ @NonNull UserHandle userHandle,
+ @NonNull AppSearchConfig config)
+ throws AppSearchException {
+ Objects.requireNonNull(userContext);
+ Objects.requireNonNull(userHandle);
+ Objects.requireNonNull(config);
+
+ synchronized (mInstancesLocked) {
+ AppSearchUserInstance instance = mInstancesLocked.get(userHandle);
+ if (instance == null) {
+ instance = createUserInstance(userContext, userHandle, config);
+ mInstancesLocked.put(userHandle, instance);
+ }
+ return instance;
+ }
+ }
+
+ /**
+ * Closes and removes an {@link AppSearchUserInstance} for the given user.
+ *
+ * <p>All mutations applied to the underlying {@link AppSearchImpl} will be persisted to disk.
+ *
+ * @param userHandle The multi-user user handle of the user that need to be removed.
+ */
+ public void closeAndRemoveUserInstance(@NonNull UserHandle userHandle) {
+ Objects.requireNonNull(userHandle);
+ synchronized (mInstancesLocked) {
+ AppSearchUserInstance instance = mInstancesLocked.remove(userHandle);
+ if (instance != null) {
+ instance.getAppSearchImpl().close();
+ }
+ }
+ }
+
+ /**
+ * Gets an {@link AppSearchUserInstance} for the given user.
+ *
+ * <p>This method should only be called by an initialized SearchSession, which has already
+ * called {@link #getOrCreateUserInstance} before.
+ *
+ * @param userHandle The multi-user handle of the device user calling AppSearch
+ * @return An initialized {@link AppSearchUserInstance} for this user
+ * @throws IllegalStateException if {@link AppSearchUserInstance} haven't created for the given
+ * user.
+ */
+ @NonNull
+ public AppSearchUserInstance getUserInstance(@NonNull UserHandle userHandle) {
+ Objects.requireNonNull(userHandle);
+ synchronized (mInstancesLocked) {
+ AppSearchUserInstance instance = mInstancesLocked.get(userHandle);
+ if (instance == null) {
+ // Impossible scenario, user cannot call an uninitialized SearchSession,
+ // getInstance should always find the instance for the given user and never try to
+ // create an instance for this user again.
+ throw new IllegalStateException(
+ "AppSearchUserInstance has never been created for: " + userHandle);
+ }
+ return instance;
+ }
+ }
+
+ @NonNull
+ private AppSearchUserInstance createUserInstance(
+ @NonNull Context userContext,
+ @NonNull UserHandle userHandle,
+ @NonNull AppSearchConfig config)
+ throws AppSearchException {
+ long totalLatencyStartMillis = SystemClock.elapsedRealtime();
+ InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
+
+ // Initialize the classes that make up AppSearchUserInstance
+ PlatformLogger logger = new PlatformLogger(userContext, config);
+
+ File appSearchDir = getAppSearchDir(userHandle);
+ File icingDir = new File(appSearchDir, "icing");
+ Log.i(TAG, "Creating new AppSearch instance at: " + icingDir);
+ AppSearchImpl appSearchImpl =
+ AppSearchImpl.create(icingDir, initStatsBuilder, new FrameworkOptimizeStrategy());
+
+ long prepareVisibilityStoreLatencyStartMillis = SystemClock.elapsedRealtime();
+ VisibilityStoreImpl visibilityStore =
+ VisibilityStoreImpl.create(appSearchImpl, userContext);
+ long prepareVisibilityStoreLatencyEndMillis = SystemClock.elapsedRealtime();
+
+ initStatsBuilder
+ .setTotalLatencyMillis(
+ (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis))
+ .setPrepareVisibilityStoreLatencyMillis(
+ (int)
+ (prepareVisibilityStoreLatencyEndMillis
+ - prepareVisibilityStoreLatencyStartMillis));
+ logger.logStats(initStatsBuilder.build());
+
+ return new AppSearchUserInstance(logger, appSearchImpl, visibilityStore);
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
deleted file mode 100644
index 2181dab..0000000
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.appsearch;
-
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.appsearch.exceptions.AppSearchException;
-import android.content.Context;
-import android.os.Environment;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.server.appsearch.external.localstorage.AppSearchImpl;
-import com.android.server.appsearch.external.localstorage.AppSearchLogger;
-import com.android.server.appsearch.external.localstorage.FrameworkOptimizeStrategy;
-
-import java.io.File;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Manages the lifecycle of instances of {@link AppSearchImpl}.
- *
- * <p>These instances are managed per unique device-user.
- * @hide
- */
-public final class ImplInstanceManager {
- private static final String TAG = "AppSearchImplInstanceMa";
-
- private static ImplInstanceManager sImplInstanceManager;
-
- @GuardedBy("mInstancesLocked")
- private final Map<UserHandle, AppSearchImpl> mInstancesLocked = new ArrayMap<>();
-
- /**
- * Gets an instance of ImplInstanceManager to be used.
- *
- * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the
- * existing instance will be returned.
- */
- @NonNull
- public static ImplInstanceManager getInstance(@NonNull Context context) {
- if (sImplInstanceManager == null) {
- synchronized (ImplInstanceManager.class) {
- if (sImplInstanceManager == null) {
- sImplInstanceManager = new ImplInstanceManager();
- }
- }
- }
- return sImplInstanceManager;
- }
-
- /**
- * Returns AppSearch directory in the credential encrypted system directory for the given user.
- *
- * <p>This folder should only be accessed after unlock.
- */
- public static File getAppSearchDir(@NonNull UserHandle userHandle) {
- // Duplicates the implementation of Environment#getDataSystemCeDirectory
- // TODO(b/191059409): Unhide Environment#getDataSystemCeDirectory and switch to it.
- File systemCeDir = new File(Environment.getDataDirectory(), "system_ce");
- File systemCeUserDir = new File(systemCeDir, String.valueOf(userHandle.getIdentifier()));
- return new File(systemCeUserDir, "appSearch");
- }
-
- /**
- * Gets an instance of AppSearchImpl for the given user, or creates one if none exists.
- *
- * <p>If no AppSearchImpl instance exists for the unlocked user, Icing will be initialized and
- * one will be created.
- *
- * @param context The system context
- * @param userHandle The multi-user handle of the device user calling AppSearch
- * @return An initialized {@link AppSearchImpl} for this user
- */
- @NonNull
- public AppSearchImpl getOrCreateAppSearchImpl(
- @NonNull Context context,
- @NonNull UserHandle userHandle,
- @Nullable AppSearchLogger logger) throws AppSearchException {
- Objects.requireNonNull(context);
- Objects.requireNonNull(userHandle);
-
- synchronized (mInstancesLocked) {
- AppSearchImpl instance = mInstancesLocked.get(userHandle);
- if (instance == null) {
- Context userContext = context.createContextAsUser(userHandle, /*flags=*/ 0);
- instance = createImpl(userContext, userHandle, logger);
- mInstancesLocked.put(userHandle, instance);
- }
- return instance;
- }
- }
-
- /**
- * Close and remove an instance of {@link AppSearchImpl} for the given user.
- *
- * <p>All mutation apply to this {@link AppSearchImpl} will be persisted to disk.
- *
- * @param userHandle The multi-user user handle of the user that need to be removed.
- */
- public void closeAndRemoveAppSearchImplForUser(@NonNull UserHandle userHandle) {
- Objects.requireNonNull(userHandle);
- synchronized (mInstancesLocked) {
- AppSearchImpl appSearchImpl = mInstancesLocked.get(userHandle);
- if (appSearchImpl != null) {
- appSearchImpl.close();
- mInstancesLocked.remove(userHandle);
- }
- }
- }
-
- /**
- * Gets an instance of AppSearchImpl for the given user.
- *
- * <p>This method should only be called by an initialized SearchSession, which has been already
- * created the AppSearchImpl instance for the given user.
- *
- * @param userHandle The multi-user handle of the device user calling AppSearch
- * @return An initialized {@link AppSearchImpl} for this user
- * @throws IllegalStateException if {@link AppSearchImpl} haven't created for the given user.
- */
- @NonNull
- public AppSearchImpl getAppSearchImpl(@NonNull UserHandle userHandle) {
- Objects.requireNonNull(userHandle);
- synchronized (mInstancesLocked) {
- AppSearchImpl instance = mInstancesLocked.get(userHandle);
- if (instance == null) {
- // Impossible scenario, user cannot call an uninitialized SearchSession,
- // getInstance should always find the instance for the given user and never try to
- // create an instance for this user again.
- throw new IllegalStateException(
- "AppSearchImpl has never been created for: " + userHandle);
- }
- return instance;
- }
- }
-
- private AppSearchImpl createImpl(
- @NonNull Context userContext,
- @NonNull UserHandle userHandle,
- @Nullable AppSearchLogger logger)
- throws AppSearchException {
- File appSearchDir = getAppSearchDir(userHandle);
- File icingDir = new File(appSearchDir, "icing");
- Log.i(TAG, "Creating new AppSearch instance at: " + icingDir);
- return AppSearchImpl.create(
- icingDir,
- userContext,
- /*logger=*/ null,
- new FrameworkOptimizeStrategy());
- }
-}
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 4a1a9ae..9dee179 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
@@ -39,7 +39,6 @@
import android.app.appsearch.StorageInfo;
import android.app.appsearch.exceptions.AppSearchException;
import android.app.appsearch.util.LogUtil;
-import android.content.Context;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.ArrayMap;
@@ -59,7 +58,7 @@
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
import com.android.server.appsearch.external.localstorage.stats.SearchStats;
-import com.android.server.appsearch.visibilitystore.VisibilityStore;
+import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
import com.google.android.icing.IcingSearchEngine;
import com.google.android.icing.proto.DeleteByQueryResultProto;
@@ -157,9 +156,6 @@
@VisibleForTesting
final IcingSearchEngine mIcingSearchEngineLocked;
- @GuardedBy("mReadWriteLock")
- private final VisibilityStore mVisibilityStoreLocked;
-
// This map contains schema types and SchemaTypeConfigProtos for all package-database
// prefixes. It maps each package-database prefix to an inner-map. The inner-map maps each
// prefixed schema type to its respective SchemaTypeConfigProto.
@@ -195,56 +191,27 @@
* <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.
+ * @param initStatsBuilder collects stats for initialization if provided.
*/
@NonNull
public static AppSearchImpl create(
@NonNull File icingDir,
- @NonNull Context userContext,
- @Nullable AppSearchLogger logger,
+ @Nullable InitializeStats.Builder initStatsBuilder,
@NonNull OptimizeStrategy optimizeStrategy)
throws AppSearchException {
- Objects.requireNonNull(icingDir);
- Objects.requireNonNull(userContext);
- Objects.requireNonNull(optimizeStrategy);
-
- long totalLatencyStartMillis = SystemClock.elapsedRealtime();
- InitializeStats.Builder initStatsBuilder = null;
- if (logger != null) {
- initStatsBuilder = new InitializeStats.Builder();
- }
-
- AppSearchImpl appSearchImpl =
- new AppSearchImpl(
- icingDir, userContext, initStatsBuilder, optimizeStrategy);
-
- long prepareVisibilityStoreLatencyStartMillis = SystemClock.elapsedRealtime();
- appSearchImpl.initializeVisibilityStore();
- long prepareVisibilityStoreLatencyEndMillis = SystemClock.elapsedRealtime();
-
- if (logger != null) {
- initStatsBuilder
- .setTotalLatencyMillis(
- (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis))
- .setPrepareVisibilityStoreLatencyMillis(
- (int)
- (prepareVisibilityStoreLatencyEndMillis
- - prepareVisibilityStoreLatencyStartMillis));
- logger.logStats(initStatsBuilder.build());
- }
-
- return appSearchImpl;
+ return new AppSearchImpl(icingDir, initStatsBuilder, optimizeStrategy);
}
/** @param initStatsBuilder collects stats for initialization if provided. */
private AppSearchImpl(
@NonNull File icingDir,
- @NonNull Context userContext,
@Nullable InitializeStats.Builder initStatsBuilder,
@NonNull OptimizeStrategy optimizeStrategy)
throws AppSearchException {
- mReadWriteLock.writeLock().lock();
+ Objects.requireNonNull(icingDir);
+ mOptimizeStrategy = Objects.requireNonNull(optimizeStrategy);
+ mReadWriteLock.writeLock().lock();
try {
// We synchronize here because we don't want to call IcingSearchEngine.initialize() more
// than once. It's unnecessary and can be a costly operation.
@@ -258,9 +225,6 @@
"Constructing IcingSearchEngine, response",
Objects.hashCode(mIcingSearchEngineLocked));
- mVisibilityStoreLocked = new VisibilityStore(this, userContext);
- mOptimizeStrategy = optimizeStrategy;
-
// The core initialization procedure. If any part of this fails, we bail into
// resetLocked(), deleting all data (but hopefully allowing AppSearchImpl to come up).
try {
@@ -342,23 +306,6 @@
}
}
- /**
- * Initialize the visibility store in AppSearchImpl.
- *
- * @throws AppSearchException on IcingSearchEngine error.
- */
- void initializeVisibilityStore() throws AppSearchException {
- mReadWriteLock.writeLock().lock();
- try {
- throwIfClosedLocked();
- mLogUtil.piiTrace("Initializing VisibilityStore, request");
- mVisibilityStoreLocked.initialize();
- mLogUtil.piiTrace("Initializing VisibilityStore, response");
- } finally {
- mReadWriteLock.writeLock().unlock();
- }
- }
-
@GuardedBy("mReadWriteLock")
private void throwIfClosedLocked() {
if (mClosedLocked) {
@@ -399,9 +346,11 @@
* @param packageName The package name that owns the schemas.
* @param databaseName The name of the database where this schema lives.
* @param schemas Schemas to set for this app.
- * @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform
+ * @param visibilityStore If set, {@code schemasNotDisplayedBySystem} and {@code
+ * schemasVisibleToPackages} will be saved here if the schema is successfully applied.
+ * @param schemasNotDisplayedBySystem Schema types that should not be surfaced on platform
* surfaces.
- * @param schemasPackageAccessible Schema types that are visible to the specified packages.
+ * @param schemasVisibleToPackages Schema types that are visible to the specified packages.
* @param forceOverride Whether to force-apply the schema even if it is incompatible. Documents
* which do not comply with the new schema will be deleted.
* @param version The overall version number of the request.
@@ -416,8 +365,9 @@
@NonNull String packageName,
@NonNull String databaseName,
@NonNull List<AppSearchSchema> schemas,
- @NonNull List<String> schemasNotPlatformSurfaceable,
- @NonNull Map<String, List<PackageIdentifier>> schemasPackageAccessible,
+ @Nullable VisibilityStore visibilityStore,
+ @NonNull List<String> schemasNotDisplayedBySystem,
+ @NonNull Map<String, List<PackageIdentifier>> schemasVisibleToPackages,
boolean forceOverride,
int version)
throws AppSearchException {
@@ -479,25 +429,27 @@
removeFromMap(mSchemaMapLocked, prefix, schemaType);
}
- Set<String> prefixedSchemasNotPlatformSurfaceable =
- new ArraySet<>(schemasNotPlatformSurfaceable.size());
- for (int i = 0; i < schemasNotPlatformSurfaceable.size(); i++) {
- prefixedSchemasNotPlatformSurfaceable.add(
- prefix + schemasNotPlatformSurfaceable.get(i));
- }
+ if (visibilityStore != null) {
+ Set<String> prefixedSchemasNotDisplayedBySystem =
+ new ArraySet<>(schemasNotDisplayedBySystem.size());
+ for (int i = 0; i < schemasNotDisplayedBySystem.size(); i++) {
+ prefixedSchemasNotDisplayedBySystem.add(
+ prefix + schemasNotDisplayedBySystem.get(i));
+ }
- Map<String, List<PackageIdentifier>> prefixedSchemasPackageAccessible =
- new ArrayMap<>(schemasPackageAccessible.size());
- for (Map.Entry<String, List<PackageIdentifier>> entry :
- schemasPackageAccessible.entrySet()) {
- prefixedSchemasPackageAccessible.put(prefix + entry.getKey(), entry.getValue());
- }
+ Map<String, List<PackageIdentifier>> prefixedSchemasVisibleToPackages =
+ new ArrayMap<>(schemasVisibleToPackages.size());
+ for (Map.Entry<String, List<PackageIdentifier>> entry :
+ schemasVisibleToPackages.entrySet()) {
+ prefixedSchemasVisibleToPackages.put(prefix + entry.getKey(), entry.getValue());
+ }
- mVisibilityStoreLocked.setVisibility(
- packageName,
- databaseName,
- prefixedSchemasNotPlatformSurfaceable,
- prefixedSchemasPackageAccessible);
+ visibilityStore.setVisibility(
+ packageName,
+ databaseName,
+ prefixedSchemasNotDisplayedBySystem,
+ prefixedSchemasVisibleToPackages);
+ }
return SetSchemaResponseToProtoConverter.toSetSchemaResponse(
setSchemaResultProto, prefix);
@@ -785,6 +737,9 @@
if (!filterPackageNames.isEmpty() && !filterPackageNames.contains(packageName)) {
// Client wanted to query over some packages that weren't its own. This isn't
// allowed through local query so we can return early with no results.
+ if (logger != null) {
+ sStatsBuilder.setStatusCode(AppSearchResult.RESULT_SECURITY_ERROR);
+ }
return new SearchResultPage(Bundle.EMPTY);
}
@@ -816,8 +771,12 @@
* @param queryExpression Query String to search.
* @param searchSpec Spec for setting filters, raw query etc.
* @param callerPackageName Package name of the caller, should belong to the {@code
- * userContext}.
+ * callerUserHandle}.
+ * @param visibilityStore Optional visibility store to obtain system and package visibility
+ * settings from
* @param callerUid UID of the client making the globalQuery call.
+ * @param callerHasSystemAccess Whether the caller has been positively identified as having
+ * access to schemas marked system surfaceable.
* @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.
@@ -828,7 +787,9 @@
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec,
@NonNull String callerPackageName,
+ @Nullable VisibilityStore visibilityStore,
int callerUid,
+ boolean callerHasSystemAccess,
@Nullable AppSearchLogger logger)
throws AppSearchException {
long totalLatencyStartMillis = SystemClock.elapsedRealtime();
@@ -885,15 +846,18 @@
if (packageName.equals(callerPackageName)) {
// Callers can always retrieve their own data
allow = true;
+ } else if (visibilityStore == null) {
+ // If there's no visibility store, there's no extra access
+ allow = false;
} else {
String databaseName = getDatabaseName(prefixedSchema);
allow =
- mVisibilityStoreLocked.isSchemaSearchableByCaller(
+ visibilityStore.isSchemaSearchableByCaller(
packageName,
databaseName,
prefixedSchema,
- callerPackageName,
- callerUid);
+ callerUid,
+ callerHasSystemAccess);
}
if (!allow) {
@@ -1488,9 +1452,6 @@
/**
* Clears documents and schema across all packages and databaseNames.
*
- * <p>This method also clear all data in {@link VisibilityStore}, an {@link
- * #initializeVisibilityStore()} must be called after this.
- *
* <p>This method belongs to mutate group.
*
* @throws AppSearchException on IcingSearchEngine error.
@@ -1507,16 +1468,12 @@
mOptimizeIntervalCountLocked = 0;
mSchemaMapLocked.clear();
mNamespaceMapLocked.clear();
-
if (initStatsBuilder != null) {
initStatsBuilder
.setHasReset(true)
.setResetStatusCode(statusProtoToResultCode(resetResultProto.getStatus()));
}
- // Must be called after everything else since VisibilityStore may repopulate
- // IcingSearchEngine with an initial schema.
- mVisibilityStoreLocked.handleReset();
checkSuccess(resetResultProto.getStatus());
}
@@ -2075,13 +2032,6 @@
return result;
}
- @GuardedBy("mReadWriteLock")
- @NonNull
- @VisibleForTesting
- VisibilityStore getVisibilityStoreLocked() {
- return mVisibilityStoreLocked;
- }
-
/**
* Converts an erroneous status code from the Icing status enums to the AppSearchResult enums.
*
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SchemaMigrationStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SchemaMigrationStats.java
new file mode 100644
index 0000000..6e1e2d5
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SchemaMigrationStats.java
@@ -0,0 +1,189 @@
+/*
+ * 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.NonNull;
+import android.app.appsearch.SetSchemaRequest;
+
+import java.util.Objects;
+
+/**
+ * Class holds detailed stats for Schema migration.
+ *
+ * @hide
+ */
+// TODO(b/173532925): Hides getter and setter functions for accessing {@code
+// mFirstSetSchemaLatencyMillis} and {@code mSecondSetSchemaLatencyMillis} field.
+
+public final class SchemaMigrationStats {
+ /** GetSchema latency in milliseconds. */
+ private final int mGetSchemaLatencyMillis;
+
+ /**
+ * Latency of querying all documents that need to be migrated to new version and transforming
+ * documents to new version in milliseconds.
+ */
+ private final int mQueryAndTransformLatencyMillis;
+
+ private final int mFirstSetSchemaLatencyMillis;
+
+ private final int mSecondSetSchemaLatencyMillis;
+
+ /** Latency of putting migrated document to Icing lib in milliseconds. */
+ private final int mSaveDocumentLatencyMillis;
+
+ private final int mMigratedDocumentCount;
+
+ private final int mSavedDocumentCount;
+
+ SchemaMigrationStats(@NonNull Builder builder) {
+ Objects.requireNonNull(builder);
+ mGetSchemaLatencyMillis = builder.mGetSchemaLatencyMillis;
+ mQueryAndTransformLatencyMillis = builder.mQueryAndTransformLatencyMillis;
+ mFirstSetSchemaLatencyMillis = builder.mFirstSetSchemaLatencyMillis;
+ mSecondSetSchemaLatencyMillis = builder.mSecondSetSchemaLatencyMillis;
+ mSaveDocumentLatencyMillis = builder.mSaveDocumentLatencyMillis;
+ mMigratedDocumentCount = builder.mMigratedDocumentCount;
+ mSavedDocumentCount = builder.mSavedDocumentCount;
+ }
+
+ /** Returns GetSchema latency in milliseconds. */
+ public int getGetSchemaLatencyMillis() {
+ return mGetSchemaLatencyMillis;
+ }
+
+ /**
+ * Returns latency of querying all documents that need to be migrated to new version and
+ * transforming documents to new version in milliseconds.
+ */
+ public int getQueryAndTransformLatencyMillis() {
+ return mQueryAndTransformLatencyMillis;
+ }
+
+ /**
+ * Returns latency of first SetSchema action in milliseconds.
+ *
+ * <p>If all schema fields are backward compatible, the schema will be successful set to Icing.
+ * Otherwise, we will retrieve incompatible types here.
+ *
+ * <p>Please see {@link SetSchemaRequest} for what is "incompatible".
+ */
+ public int getFirstSetSchemaLatencyMillis() {
+ return mFirstSetSchemaLatencyMillis;
+ }
+
+ /**
+ * Returns latency of second SetSchema action in milliseconds.
+ *
+ * <p>If all schema fields are backward compatible, the schema will be successful set to Icing
+ * in the first setSchema action and this value will be 0. Otherwise, schema types will be set
+ * to Icing by this action.
+ */
+ public int getSecondSetSchemaLatencyMillis() {
+ return mSecondSetSchemaLatencyMillis;
+ }
+
+ /** Returns latency of putting migrated document to Icing lib in milliseconds. */
+ public int getSaveDocumentLatencyMillis() {
+ return mSaveDocumentLatencyMillis;
+ }
+
+ /** Returns number of migrated documents. */
+ public int getMigratedDocumentCount() {
+ return mMigratedDocumentCount;
+ }
+
+ /** Returns number of updated documents which are saved in Icing lib. */
+ public int getSavedDocumentCount() {
+ return mSavedDocumentCount;
+ }
+
+ /** Builder for {@link SchemaMigrationStats}. */
+ public static class Builder {
+ int mGetSchemaLatencyMillis;
+ int mQueryAndTransformLatencyMillis;
+ int mFirstSetSchemaLatencyMillis;
+ int mSecondSetSchemaLatencyMillis;
+ int mSaveDocumentLatencyMillis;
+ int mMigratedDocumentCount;
+ int mSavedDocumentCount;
+
+ /** Sets latency for the GetSchema action in milliseconds. */
+ @NonNull
+ public SchemaMigrationStats.Builder setGetSchemaLatencyMillis(int getSchemaLatencyMillis) {
+ mGetSchemaLatencyMillis = getSchemaLatencyMillis;
+ return this;
+ }
+
+ /**
+ * Sets latency for querying all documents that need to be migrated to new version and
+ * transforming documents to new version in milliseconds.
+ */
+ @NonNull
+ public SchemaMigrationStats.Builder setQueryAndTransformLatencyMillis(
+ int queryAndTransformLatencyMillis) {
+ mQueryAndTransformLatencyMillis = queryAndTransformLatencyMillis;
+ return this;
+ }
+
+ /** Sets latency of first SetSchema action in milliseconds. */
+ @NonNull
+ public SchemaMigrationStats.Builder setFirstSetSchemaLatencyMillis(
+ int firstSetSchemaLatencyMillis) {
+ mFirstSetSchemaLatencyMillis = firstSetSchemaLatencyMillis;
+ return this;
+ }
+
+ /** Sets latency of second SetSchema action in milliseconds. */
+ @NonNull
+ public SchemaMigrationStats.Builder setSecondSetSchemaLatencyMillis(
+ int secondSetSchemaLatencyMillis) {
+ mSecondSetSchemaLatencyMillis = secondSetSchemaLatencyMillis;
+ return this;
+ }
+
+ /** Sets latency for putting migrated document to Icing lib in milliseconds. */
+ @NonNull
+ public SchemaMigrationStats.Builder setSaveDocumentLatencyMillis(
+ int saveDocumentLatencyMillis) {
+ mSaveDocumentLatencyMillis = saveDocumentLatencyMillis;
+ return this;
+ }
+
+ /** Sets number of migrated documents. */
+ @NonNull
+ public SchemaMigrationStats.Builder setMigratedDocumentCount(int migratedDocumentCount) {
+ mMigratedDocumentCount = migratedDocumentCount;
+ return this;
+ }
+
+ /** Sets number of updated documents which are saved in Icing lib. */
+ @NonNull
+ public SchemaMigrationStats.Builder setSavedDocumentCount(int savedDocumentCount) {
+ mSavedDocumentCount = savedDocumentCount;
+ return this;
+ }
+
+ /**
+ * Builds a new {@link SchemaMigrationStats} from the {@link SchemaMigrationStats.Builder}.
+ */
+ @NonNull
+ public SchemaMigrationStats build() {
+ return new SchemaMigrationStats(/* builder= */ this);
+ }
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java
index 56a546a..9d789a8 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java
@@ -17,6 +17,7 @@
package com.android.server.appsearch.external.localstorage.stats;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.appsearch.AppSearchResult;
import java.util.Objects;
@@ -38,6 +39,12 @@
*/
@AppSearchResult.ResultCode private final int mStatusCode;
+ /**
+ * Stores stats of SchemaMigration in SetSchema process. Is {@code null} if no schema migration
+ * is needed.
+ */
+ @Nullable private final SchemaMigrationStats mSchemaMigrationStats;
+
private final int mTotalLatencyMillis;
/** Overall time used for the native function call. */
@@ -63,6 +70,7 @@
mPackageName = builder.mPackageName;
mDatabase = builder.mDatabase;
mStatusCode = builder.mStatusCode;
+ mSchemaMigrationStats = builder.mSchemaMigrationStats;
mTotalLatencyMillis = builder.mTotalLatencyMillis;
mNativeLatencyMillis = builder.mNativeLatencyMillis;
mNewTypeCount = builder.mNewTypeCount;
@@ -90,6 +98,15 @@
return mStatusCode;
}
+ /**
+ * Returns the status of schema migration, if migration is executed during the SetSchema
+ * process. Otherwise, returns {@code null}.
+ */
+ @Nullable
+ public SchemaMigrationStats getSchemaMigrationStats() {
+ return mSchemaMigrationStats;
+ }
+
/** Returns the total latency of the SetSchema action. */
public int getTotalLatencyMillis() {
return mTotalLatencyMillis;
@@ -140,6 +157,7 @@
@NonNull final String mPackageName;
@NonNull final String mDatabase;
@AppSearchResult.ResultCode int mStatusCode;
+ @Nullable SchemaMigrationStats mSchemaMigrationStats;
int mTotalLatencyMillis;
int mNativeLatencyMillis;
int mNewTypeCount;
@@ -161,7 +179,14 @@
return this;
}
- /** Sets total latency for the SetSchema action. */
+ /** Sets the status of schema migration. */
+ @NonNull
+ public Builder setSchemaMigrationStats(@NonNull SchemaMigrationStats schemaMigrationStats) {
+ mSchemaMigrationStats = Objects.requireNonNull(schemaMigrationStats);
+ return this;
+ }
+
+ /** Sets total latency for the SetSchema action in milliseconds. */
@NonNull
public Builder setTotalLatencyMillis(int totalLatencyMillis) {
mTotalLatencyMillis = totalLatencyMillis;
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStore.java
new file mode 100644
index 0000000..fb89250
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStore.java
@@ -0,0 +1,72 @@
+/*
+ * 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.visibilitystore;
+
+import android.annotation.NonNull;
+import android.app.appsearch.PackageIdentifier;
+import android.app.appsearch.exceptions.AppSearchException;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * An interface for classes that store and validate document visibility data.
+ *
+ * @hide
+ */
+public interface VisibilityStore {
+ /**
+ * These cannot have any of the special characters used by AppSearchImpl (e.g. {@code
+ * AppSearchImpl#PACKAGE_DELIMITER} or {@code AppSearchImpl#DATABASE_DELIMITER}.
+ */
+ String PACKAGE_NAME = "VS#Pkg";
+
+ @VisibleForTesting String DATABASE_NAME = "VS#Db";
+
+ /**
+ * Sets visibility settings for the given database. Any previous visibility settings will be
+ * overwritten.
+ *
+ * @param packageName Package of app that owns the schemas.
+ * @param databaseName Database that owns the schemas.
+ * @param schemasNotDisplayedBySystem Set of prefixed schemas that should be hidden from
+ * platform surfaces.
+ * @param schemasVisibleToPackages Map of prefixed schemas to a list of package identifiers that
+ * have access to the schema.
+ * @throws AppSearchException on AppSearchImpl error.
+ */
+ void setVisibility(
+ @NonNull String packageName,
+ @NonNull String databaseName,
+ @NonNull Set<String> schemasNotDisplayedBySystem,
+ @NonNull Map<String, List<PackageIdentifier>> schemasVisibleToPackages)
+ throws AppSearchException;
+
+ /**
+ * Checks whether the given package has access to system-surfaceable schemas.
+ *
+ * @param callerUid UID of the app that wants to see the data.
+ */
+ boolean isSchemaSearchableByCaller(
+ @NonNull String packageName,
+ @NonNull String databaseName,
+ @NonNull String prefixedSchema,
+ int callerUid,
+ boolean callerHasSystemAccess);
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/LoggerInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/LoggerInstanceManager.java
deleted file mode 100644
index ea00f50..0000000
--- a/apex/appsearch/service/java/com/android/server/appsearch/stats/LoggerInstanceManager.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.appsearch.stats;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.server.appsearch.AppSearchConfig;
-import com.android.server.appsearch.AppSearchManagerService;
-
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Manages the lifecycle of instances of {@link PlatformLogger}.
- *
- * <p>These instances are managed per unique device-user.
- */
-public final class LoggerInstanceManager {
- private static volatile LoggerInstanceManager sLoggerInstanceManager;
-
- @GuardedBy("mInstancesLocked")
- private final Map<UserHandle, PlatformLogger> mInstancesLocked = new ArrayMap<>();
-
- private LoggerInstanceManager() {
- }
-
- /**
- * Gets an instance of {@link LoggerInstanceManager} to be used.
- *
- * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the
- * existing instance will be returned.
- */
- @NonNull
- public static LoggerInstanceManager getInstance() {
- if (sLoggerInstanceManager == null) {
- synchronized (LoggerInstanceManager.class) {
- if (sLoggerInstanceManager == null) {
- sLoggerInstanceManager =
- new LoggerInstanceManager();
- }
- }
- }
- return sLoggerInstanceManager;
- }
-
- /**
- * Gets an instance of PlatformLogger for the given user, or creates one if none exists.
- *
- * @param context The context
- * @param userHandle The multi-user handle of the device user calling AppSearch
- * @return An initialized {@link PlatformLogger} for this user
- */
- @NonNull
- public PlatformLogger getOrCreatePlatformLogger(
- @NonNull Context context, @NonNull UserHandle userHandle,
- @NonNull AppSearchConfig config) {
- Objects.requireNonNull(userHandle);
- synchronized (mInstancesLocked) {
- PlatformLogger instance = mInstancesLocked.get(userHandle);
- if (instance == null) {
- instance = new PlatformLogger(context, userHandle, config);
- mInstancesLocked.put(userHandle, instance);
- }
- return instance;
- }
- }
-
- /**
- * Gets an instance of PlatformLogger for the given user.
- *
- * <p>This method should only be called by an initialized SearchSession, which has been already
- * created the PlatformLogger instance for the given user.
- *
- * @param userHandle The multi-user handle of the device user calling AppSearch
- * @return An initialized {@link PlatformLogger} for this user
- * @throws IllegalStateException if {@link PlatformLogger} haven't created for the given user.
- */
- @NonNull
- public PlatformLogger getPlatformLogger(@NonNull UserHandle userHandle) {
- Objects.requireNonNull(userHandle);
- synchronized (mInstancesLocked) {
- PlatformLogger instance = mInstancesLocked.get(userHandle);
- if (instance == null) {
- // Impossible scenario, user cannot call an uninitialized SearchSession,
- // getInstance should always find the instance for the given user and never try to
- // create an instance for this user again.
- throw new IllegalStateException(
- "PlatformLogger has never been created for: " + userHandle);
- }
- return instance;
- }
- }
-
- /**
- * Remove an instance of {@link PlatformLogger} for the given user.
- *
- * <p>This method should only be called if {@link AppSearchManagerService} receives an
- * ACTION_USER_REMOVED, which the logger instance of given user should be removed.
- *
- * @param userHandle The multi-user handle of the user that need to be removed.
- */
- public void removePlatformLoggerForUser(@NonNull UserHandle userHandle) {
- Objects.requireNonNull(userHandle);
- synchronized (mInstancesLocked) {
- mInstancesLocked.remove(userHandle);
- }
- }
-}
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 31fead5e..322bd11 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
@@ -22,7 +22,6 @@
import android.content.Context;
import android.os.Process;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseIntArray;
@@ -55,11 +54,8 @@
public final class PlatformLogger implements AppSearchLogger {
private static final String TAG = "AppSearchPlatformLogger";
- // Context of the system service.
- private final Context mContext;
-
- // User we're logging for.
- private final UserHandle mUserHandle;
+ // Context of the user we're logging for.
+ private final Context mUserContext;
// Manager holding the configuration flags
private final AppSearchConfig mConfig;
@@ -120,10 +116,9 @@
* Westworld constructor
*/
public PlatformLogger(
- @NonNull Context context, @NonNull UserHandle userHandle,
+ @NonNull Context userContext,
@NonNull AppSearchConfig config) {
- mContext = Objects.requireNonNull(context);
- mUserHandle = Objects.requireNonNull(userHandle);
+ mUserContext = Objects.requireNonNull(userContext);
mConfig = Objects.requireNonNull(config);
}
@@ -451,7 +446,7 @@
private int getPackageUidAsUserLocked(@NonNull String packageName) {
Integer packageUid = mPackageUidCacheLocked.get(packageName);
if (packageUid == null) {
- packageUid = PackageUtil.getPackageUidAsUser(mContext, packageName, mUserHandle);
+ packageUid = PackageUtil.getPackageUid(mUserContext, packageName);
if (packageUid != Process.INVALID_UID) {
mPackageUidCacheLocked.put(packageName, packageUid);
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/util/PackageUtil.java b/apex/appsearch/service/java/com/android/server/appsearch/util/PackageUtil.java
index 53a1bed..714ffb6 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/util/PackageUtil.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/util/PackageUtil.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Process;
-import android.os.UserHandle;
/**
* Utilities for interacting with {@link android.content.pm.PackageManager},
@@ -32,16 +31,6 @@
private PackageUtil() {}
/**
- * Finds the UID of the {@code packageName}. Returns {@link Process#INVALID_UID} if unable to
- * find the UID.
- */
- public static int getPackageUidAsUser(
- @NonNull Context context, @NonNull String packageName, @NonNull UserHandle user) {
- Context userContext = context.createContextAsUser(user, /*flags=*/ 0);
- return getPackageUid(userContext, packageName);
- }
-
- /**
* Finds the UID of the {@code packageName} in the given {@code context}. Returns
* {@link Process#INVALID_UID} if unable to find the UID.
*/
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotDisplayedBySystemMap.java
similarity index 92%
rename from apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java
rename to apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotDisplayedBySystemMap.java
index 5ad4276..95905af 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotDisplayedBySystemMap.java
@@ -27,7 +27,7 @@
*
* This object is not thread safe.
*/
-class NotPlatformSurfaceableMap {
+class NotDisplayedBySystemMap {
/**
* Maps packages to databases to the set of prefixed schemas that are platform-hidden within
* that database.
@@ -39,7 +39,7 @@
*
* <p>Any existing mappings for this prefix are overwritten.
*/
- public void setNotPlatformSurfaceable(
+ public void setNotDisplayedBySystem(
@NonNull String packageName,
@NonNull String databaseName,
@NonNull Set<String> prefixedSchemas) {
@@ -55,7 +55,7 @@
* Returns whether the given prefixed schema is platform surfaceable (has not opted out) in the
* given database.
*/
- public boolean isSchemaPlatformSurfaceable(
+ public boolean isSchemaDisplayedBySystem(
@NonNull String packageName,
@NonNull String databaseName,
@NonNull String prefixedSchema) {
@@ -73,9 +73,4 @@
// isn't one of those opt-outs, it's surfaceable.
return !schemaTypes.contains(prefixedSchema);
}
-
- /** Discards all data in the map. */
- public void clear() {
- mMap.clear();
- }
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java
index 1771b1d..b010870 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java
@@ -28,10 +28,10 @@
/**
* Property that holds the list of platform-hidden schemas, as part of the visibility settings.
*/
- private static final String NOT_PLATFORM_SURFACEABLE_PROPERTY = "notPlatformSurfaceable";
+ private static final String NOT_DISPLAYED_BY_SYSTEM_PROPERTY = "notPlatformSurfaceable";
/** Property that holds nested documents of package accessible schemas. */
- private static final String PACKAGE_ACCESSIBLE_PROPERTY = "packageAccessible";
+ private static final String VISIBLE_TO_PACKAGES_PROPERTY = "packageAccessible";
/**
* Schema for the VisibilityStore's documents.
@@ -41,11 +41,11 @@
*/
public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
.addProperty(new AppSearchSchema.StringPropertyConfig.Builder(
- NOT_PLATFORM_SURFACEABLE_PROPERTY)
+ NOT_DISPLAYED_BY_SYSTEM_PROPERTY)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
.build())
.addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder(
- PACKAGE_ACCESSIBLE_PROPERTY, PackageAccessibleDocument.SCHEMA_TYPE)
+ VISIBLE_TO_PACKAGES_PROPERTY, VisibleToPackagesDocument.SCHEMA_TYPE)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
.build())
.build();
@@ -55,13 +55,13 @@
}
@Nullable
- public String[] getNotPlatformSurfaceableSchemas() {
- return getPropertyStringArray(NOT_PLATFORM_SURFACEABLE_PROPERTY);
+ public String[] getNotDisplayedBySystem() {
+ return getPropertyStringArray(NOT_DISPLAYED_BY_SYSTEM_PROPERTY);
}
@Nullable
- public GenericDocument[] getPackageAccessibleSchemas() {
- return getPropertyDocumentArray(PACKAGE_ACCESSIBLE_PROPERTY);
+ public GenericDocument[] getVisibleToPackages() {
+ return getPropertyDocumentArray(VISIBLE_TO_PACKAGES_PROPERTY);
}
/** Builder for {@link VisibilityDocument}. */
@@ -72,17 +72,15 @@
/** Sets which prefixed schemas have opted out of platform surfacing. */
@NonNull
- public Builder setSchemasNotPlatformSurfaceable(
- @NonNull String[] notPlatformSurfaceableSchemas) {
- return setPropertyString(
- NOT_PLATFORM_SURFACEABLE_PROPERTY, notPlatformSurfaceableSchemas);
+ public Builder setNotDisplayedBySystem(@NonNull String[] notDisplayedBySystemSchemas) {
+ return setPropertyString(NOT_DISPLAYED_BY_SYSTEM_PROPERTY, notDisplayedBySystemSchemas);
}
/** Sets which prefixed schemas have configured package access. */
@NonNull
- public Builder setPackageAccessibleSchemas(
- @NonNull PackageAccessibleDocument[] packageAccessibleSchemas) {
- return setPropertyDocument(PACKAGE_ACCESSIBLE_PROPERTY, packageAccessibleSchemas);
+ public Builder setVisibleToPackages(
+ @NonNull VisibleToPackagesDocument[] visibleToPackagesDocuments) {
+ return setPropertyDocument(VISIBLE_TO_PACKAGES_PROPERTY, visibleToPackagesDocuments);
}
}
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java
similarity index 64%
rename from apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java
rename to apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java
index af09210..ce142d6 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStore.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java
@@ -30,9 +30,9 @@
import android.util.ArrayMap;
import android.util.ArraySet;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.server.appsearch.external.localstorage.AppSearchImpl;
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
+import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
import com.android.server.appsearch.util.PackageUtil;
import com.google.android.icing.proto.PersistType;
@@ -60,26 +60,12 @@
* <p>This class doesn't handle any locking itself. Its callers should handle the locking at a
* higher level.
*
- * <p>NOTE: This class holds an instance of AppSearchImpl and AppSearchImpl holds an instance of
- * this class. Take care to not cause any circular dependencies.
- *
* @hide
*/
-public class VisibilityStore {
- /** No-op uid that won't have any visibility settings. */
- public static final int NO_OP_UID = -1;
-
+public class VisibilityStoreImpl implements VisibilityStore {
/** Version for the visibility schema */
private static final int SCHEMA_VERSION = 0;
- /**
- * These cannot have any of the special characters used by AppSearchImpl (e.g. {@code
- * AppSearchImpl#PACKAGE_DELIMITER} or {@code AppSearchImpl#DATABASE_DELIMITER}.
- */
- @VisibleForTesting public static final String PACKAGE_NAME = "VS#Pkg";
-
- @VisibleForTesting public static final String DATABASE_NAME = "VS#Db";
-
/** Namespace of documents that contain visibility settings */
private static final String NAMESPACE = "";
@@ -92,64 +78,58 @@
private final Context mUserContext;
/** Stores the schemas that are platform-hidden. All values are prefixed. */
- private final NotPlatformSurfaceableMap mNotPlatformSurfaceableMap =
- new NotPlatformSurfaceableMap();
+ private final NotDisplayedBySystemMap mNotDisplayedBySystemMap = new NotDisplayedBySystemMap();
- /** Stores the schemas that are package accessible. All values are prefixed. */
- private final PackageAccessibleMap mPackageAccessibleMap = new PackageAccessibleMap();
+ /** Stores the schemas that are visible to 3p packages. All values are prefixed. */
+ private final VisibleToPackagesMap mVisibleToPackagesMap = new VisibleToPackagesMap();
/**
- * Creates an uninitialized VisibilityStore object. Callers must also call {@link #initialize()}
- * before using the object.
+ * Creates and initializes VisibilityStore.
*
* @param appSearchImpl AppSearchImpl instance
* @param userContext Context of the user that the call is being made as
*/
- public VisibilityStore(@NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext) {
- mAppSearchImpl = Objects.requireNonNull(appSearchImpl);
- mUserContext = Objects.requireNonNull(userContext);
+ @NonNull
+ public static VisibilityStoreImpl create(
+ @NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext)
+ throws AppSearchException {
+ return new VisibilityStoreImpl(appSearchImpl, userContext);
}
- /**
- * Initializes schemas and member variables to track visibility settings.
- *
- * <p>This is kept separate from the constructor because this will call methods on
- * AppSearchImpl. Some may even then recursively call back into VisibilityStore (for example,
- * {@link AppSearchImpl#setSchema} will call {@link #setVisibility}. We need to have both
- * AppSearchImpl and VisibilityStore fully initialized for this call flow to work.
- *
- * @throws AppSearchException AppSearchException on AppSearchImpl error.
- */
- public void initialize() throws AppSearchException {
+ private VisibilityStoreImpl(@NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext)
+ throws AppSearchException {
+ mAppSearchImpl = Objects.requireNonNull(appSearchImpl);
+ mUserContext = Objects.requireNonNull(userContext);
+
GetSchemaResponse getSchemaResponse = mAppSearchImpl.getSchema(PACKAGE_NAME, DATABASE_NAME);
boolean hasVisibilityType = false;
- boolean hasPackageAccessibleType = false;
+ boolean hasVisibleToPackagesType = false;
for (AppSearchSchema schema : getSchemaResponse.getSchemas()) {
if (schema.getSchemaType().equals(VisibilityDocument.SCHEMA_TYPE)) {
hasVisibilityType = true;
- } else if (schema.getSchemaType().equals(PackageAccessibleDocument.SCHEMA_TYPE)) {
- hasPackageAccessibleType = true;
+ } else if (schema.getSchemaType().equals(VisibleToPackagesDocument.SCHEMA_TYPE)) {
+ hasVisibleToPackagesType = true;
}
- if (hasVisibilityType && hasPackageAccessibleType) {
+ if (hasVisibilityType && hasVisibleToPackagesType) {
// Found both our types, can exit early.
break;
}
}
- if (!hasVisibilityType || !hasPackageAccessibleType) {
+ if (!hasVisibilityType || !hasVisibleToPackagesType) {
// Schema type doesn't exist yet. Add it.
mAppSearchImpl.setSchema(
PACKAGE_NAME,
DATABASE_NAME,
- Arrays.asList(VisibilityDocument.SCHEMA, PackageAccessibleDocument.SCHEMA),
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ Arrays.asList(VisibilityDocument.SCHEMA, VisibleToPackagesDocument.SCHEMA),
+ /*visibilityStore=*/ null, // Avoid recursive calls
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ SCHEMA_VERSION);
}
// Populate visibility settings set
- mNotPlatformSurfaceableMap.clear();
for (Map.Entry<String, Set<String>> entry :
mAppSearchImpl.getPackageToDatabases().entrySet()) {
String packageName = entry.getKey();
@@ -182,26 +162,25 @@
}
// Update platform visibility settings
- String[] notPlatformSurfaceableSchemas =
- visibilityDocument.getNotPlatformSurfaceableSchemas();
- if (notPlatformSurfaceableSchemas != null) {
- mNotPlatformSurfaceableMap.setNotPlatformSurfaceable(
+ String[] notDisplayedBySystemSchemas = visibilityDocument.getNotDisplayedBySystem();
+ if (notDisplayedBySystemSchemas != null) {
+ mNotDisplayedBySystemMap.setNotDisplayedBySystem(
packageName,
databaseName,
- new ArraySet<>(notPlatformSurfaceableSchemas));
+ new ArraySet<>(notDisplayedBySystemSchemas));
}
// Update 3p package visibility settings
Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap = new ArrayMap<>();
- GenericDocument[] packageAccessibleDocuments =
- visibilityDocument.getPackageAccessibleSchemas();
- if (packageAccessibleDocuments != null) {
- for (int i = 0; i < packageAccessibleDocuments.length; i++) {
- PackageAccessibleDocument packageAccessibleDocument =
- new PackageAccessibleDocument(packageAccessibleDocuments[i]);
+ GenericDocument[] visibleToPackagesDocuments =
+ visibilityDocument.getVisibleToPackages();
+ if (visibleToPackagesDocuments != null) {
+ for (int i = 0; i < visibleToPackagesDocuments.length; i++) {
+ VisibleToPackagesDocument visibleToPackagesDocument =
+ new VisibleToPackagesDocument(visibleToPackagesDocuments[i]);
PackageIdentifier packageIdentifier =
- packageAccessibleDocument.getPackageIdentifier();
- String prefixedSchema = packageAccessibleDocument.getAccessibleSchemaType();
+ visibleToPackagesDocument.getPackageIdentifier();
+ String prefixedSchema = visibleToPackagesDocument.getAccessibleSchemaType();
Set<PackageIdentifier> packageIdentifiers =
schemaToPackageIdentifierMap.get(prefixedSchema);
if (packageIdentifiers == null) {
@@ -211,61 +190,50 @@
schemaToPackageIdentifierMap.put(prefixedSchema, packageIdentifiers);
}
}
- mPackageAccessibleMap.setPackageAccessible(
+ mVisibleToPackagesMap.setVisibleToPackages(
packageName, databaseName, schemaToPackageIdentifierMap);
}
}
}
- /**
- * Sets visibility settings for the given database. Any previous visibility settings will be
- * overwritten.
- *
- * @param packageName Package of app that owns the {@code schemasNotPlatformSurfaceable}.
- * @param databaseName Database that owns the {@code schemasNotPlatformSurfaceable}.
- * @param schemasNotPlatformSurfaceable Set of prefixed schemas that should be hidden from the
- * platform.
- * @param schemasPackageAccessible Map of prefixed schemas to a list of package identifiers that
- * have access to the schema.
- * @throws AppSearchException on AppSearchImpl error.
- */
+ @Override
public void setVisibility(
@NonNull String packageName,
@NonNull String databaseName,
- @NonNull Set<String> schemasNotPlatformSurfaceable,
- @NonNull Map<String, List<PackageIdentifier>> schemasPackageAccessible)
+ @NonNull Set<String> schemasNotDisplayedBySystem,
+ @NonNull Map<String, List<PackageIdentifier>> schemasVisibleToPackages)
throws AppSearchException {
Objects.requireNonNull(packageName);
Objects.requireNonNull(databaseName);
- Objects.requireNonNull(schemasNotPlatformSurfaceable);
- Objects.requireNonNull(schemasPackageAccessible);
+ Objects.requireNonNull(schemasNotDisplayedBySystem);
+ Objects.requireNonNull(schemasVisibleToPackages);
// Persist the document
VisibilityDocument.Builder visibilityDocument =
new VisibilityDocument.Builder(
NAMESPACE, /*id=*/ getVisibilityDocumentId(packageName, databaseName));
- if (!schemasNotPlatformSurfaceable.isEmpty()) {
- visibilityDocument.setSchemasNotPlatformSurfaceable(
- schemasNotPlatformSurfaceable.toArray(new String[0]));
+ if (!schemasNotDisplayedBySystem.isEmpty()) {
+ visibilityDocument.setNotDisplayedBySystem(
+ schemasNotDisplayedBySystem.toArray(new String[0]));
}
Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap = new ArrayMap<>();
- List<PackageAccessibleDocument> packageAccessibleDocuments = new ArrayList<>();
+ List<VisibleToPackagesDocument> visibleToPackagesDocuments = new ArrayList<>();
for (Map.Entry<String, List<PackageIdentifier>> entry :
- schemasPackageAccessible.entrySet()) {
+ schemasVisibleToPackages.entrySet()) {
for (int i = 0; i < entry.getValue().size(); i++) {
- PackageAccessibleDocument packageAccessibleDocument =
- new PackageAccessibleDocument.Builder(NAMESPACE, /*id=*/ "")
+ VisibleToPackagesDocument visibleToPackagesDocument =
+ new VisibleToPackagesDocument.Builder(NAMESPACE, /*id=*/ "")
.setAccessibleSchemaType(entry.getKey())
.setPackageIdentifier(entry.getValue().get(i))
.build();
- packageAccessibleDocuments.add(packageAccessibleDocument);
+ visibleToPackagesDocuments.add(visibleToPackagesDocument);
}
schemaToPackageIdentifierMap.put(entry.getKey(), new ArraySet<>(entry.getValue()));
}
- if (!packageAccessibleDocuments.isEmpty()) {
- visibilityDocument.setPackageAccessibleSchemas(
- packageAccessibleDocuments.toArray(new PackageAccessibleDocument[0]));
+ if (!visibleToPackagesDocuments.isEmpty()) {
+ visibilityDocument.setVisibleToPackages(
+ visibleToPackagesDocuments.toArray(new VisibleToPackagesDocument[0]));
}
mAppSearchImpl.putDocument(
@@ -274,50 +242,47 @@
mAppSearchImpl.persistToDisk(PersistType.Code.LITE);
// Update derived data structures.
- mNotPlatformSurfaceableMap.setNotPlatformSurfaceable(
- packageName, databaseName, schemasNotPlatformSurfaceable);
- mPackageAccessibleMap.setPackageAccessible(
+ mNotDisplayedBySystemMap.setNotDisplayedBySystem(
+ packageName, databaseName, schemasNotDisplayedBySystem);
+ mVisibleToPackagesMap.setVisibleToPackages(
packageName, databaseName, schemaToPackageIdentifierMap);
}
/**
- * Checks whether {@code prefixedSchema} can be searched over by the {@code callerUid}.
+ * Checks whether the given package has access to system-surfaceable schemas.
*
- * @param packageName Package that owns the schema.
- * @param databaseName Database within the package that owns the schema.
- * @param prefixedSchema Prefixed schema type the caller is trying to access.
* @param callerPackageName Package name of the caller.
- * @param callerUid Uid of the caller.
*/
+ public boolean doesCallerHaveSystemAccess(@NonNull String callerPackageName) {
+ Objects.requireNonNull(callerPackageName);
+ return mUserContext.getPackageManager()
+ .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, callerPackageName)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ @Override
public boolean isSchemaSearchableByCaller(
@NonNull String packageName,
@NonNull String databaseName,
@NonNull String prefixedSchema,
- @NonNull String callerPackageName,
- int callerUid) {
+ int callerUid,
+ boolean callerHasSystemAccess) {
Objects.requireNonNull(packageName);
Objects.requireNonNull(databaseName);
Objects.requireNonNull(prefixedSchema);
- Objects.requireNonNull(callerPackageName);
if (packageName.equals(PACKAGE_NAME)) {
return false; // VisibilityStore schemas are for internal bookkeeping.
}
- // TODO(b/180058203): If we can cache or pass in that a caller has the
- // READ_GLOBAL_SEARCH_DATA permission, then we can save this package manager lookup for
- // each schema we may check in the loop.
- if (mNotPlatformSurfaceableMap.isSchemaPlatformSurfaceable(
- packageName, databaseName, prefixedSchema)
- && mUserContext
- .getPackageManager()
- .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, callerPackageName)
- == PackageManager.PERMISSION_GRANTED) {
+ if (callerHasSystemAccess
+ && mNotDisplayedBySystemMap.isSchemaDisplayedBySystem(
+ packageName, databaseName, prefixedSchema)) {
return true;
}
// May not be platform surfaceable, but might still be accessible through 3p access.
- return isSchemaPackageAccessible(packageName, databaseName, prefixedSchema, callerUid);
+ return isSchemaVisibleToPackages(packageName, databaseName, prefixedSchema, callerUid);
}
/**
@@ -329,13 +294,13 @@
* certificate was once used to sign the package, the package will still be granted access. This
* does not handle packages that have been signed by multiple certificates.
*/
- private boolean isSchemaPackageAccessible(
+ private boolean isSchemaVisibleToPackages(
@NonNull String packageName,
@NonNull String databaseName,
@NonNull String prefixedSchema,
int callerUid) {
Set<PackageIdentifier> packageIdentifiers =
- mPackageAccessibleMap.getAccessiblePackages(
+ mVisibleToPackagesMap.getAccessiblePackages(
packageName, databaseName, prefixedSchema);
if (packageIdentifiers.isEmpty()) {
return false;
@@ -373,16 +338,6 @@
}
/**
- * Handles an {@code AppSearchImpl#reset()} by clearing any cached state.
- *
- * <p>{@link #initialize()} must be called after this.
- */
- public void handleReset() {
- mNotPlatformSurfaceableMap.clear();
- mPackageAccessibleMap.clear();
- }
-
- /**
* Adds a prefix to create a visibility store document's id.
*
* @param packageName Package to which the visibility doc refers
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleDocument.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesDocument.java
similarity index 90%
rename from apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleDocument.java
rename to apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesDocument.java
index 0b4e196..8d50339 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleDocument.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesDocument.java
@@ -26,7 +26,7 @@
*
* @see android.app.appsearch.SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage
*/
-class PackageAccessibleDocument extends GenericDocument {
+class VisibleToPackagesDocument extends GenericDocument {
/** Schema type for nested documents that hold package accessible information. */
public static final String SCHEMA_TYPE = "PackageAccessibleType";
@@ -59,7 +59,7 @@
.build())
.build();
- public PackageAccessibleDocument(@NonNull GenericDocument genericDocument) {
+ VisibleToPackagesDocument(@NonNull GenericDocument genericDocument) {
super(genericDocument);
}
@@ -76,9 +76,9 @@
return new PackageIdentifier(packageName, sha256Cert);
}
- /** Builder for {@link PackageAccessibleDocument} instances. */
- public static class Builder extends GenericDocument.Builder<PackageAccessibleDocument.Builder> {
- public Builder(@NonNull String namespace, @NonNull String id) {
+ /** Builder for {@link VisibleToPackagesDocument} instances. */
+ public static class Builder extends GenericDocument.Builder<VisibleToPackagesDocument.Builder> {
+ Builder(@NonNull String namespace, @NonNull String id) {
super(namespace, id, SCHEMA_TYPE);
}
@@ -98,8 +98,8 @@
@Override
@NonNull
- public PackageAccessibleDocument build() {
- return new PackageAccessibleDocument(super.build());
+ public VisibleToPackagesDocument build() {
+ return new VisibleToPackagesDocument(super.build());
}
}
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesMap.java
similarity index 94%
rename from apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java
rename to apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesMap.java
index 2b39347..e2b51a7 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesMap.java
@@ -29,7 +29,7 @@
*
* This object is not thread safe.
*/
-class PackageAccessibleMap {
+class VisibleToPackagesMap {
/**
* Maps packages to databases to prefixed schemas to PackageIdentifiers that have access to that
* schema.
@@ -42,7 +42,7 @@
*
* <p>Any existing mappings for this prefix are overwritten.
*/
- public void setPackageAccessible(
+ public void setVisibleToPackages(
@NonNull String packageName,
@NonNull String databaseName,
@NonNull Map<String, Set<PackageIdentifier>> schemaToPackageIdentifier) {
@@ -82,9 +82,4 @@
}
return accessiblePackages;
}
-
- /** Discards all data in the map. */
- public void clear() {
- mMap.clear();
- }
}
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 78d39cc..6555107 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-31a54dba5bda4d0109ea91eb1ac047c937cbaae3
+c7387d9b58726a23a0608a9365fb3a1b57b7b8a1
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
index d6ce3eb..7b74578 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
@@ -51,8 +51,6 @@
* @param request the schema to set or update the AppSearch database to.
* @return a {@link ListenableFuture} which resolves to a {@link SetSchemaResponse} object.
*/
- // TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are
- // exposed.
@NonNull
ListenableFuture<SetSchemaResponse> setSchema(@NonNull SetSchemaRequest request);
diff --git a/apex/jobscheduler/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
index 1efe5cb..4843415 100644
--- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java
+++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java
@@ -1285,14 +1285,20 @@
}
/**
- * Called to check if the caller has the permission
- * {@link Manifest.permission#SCHEDULE_EXACT_ALARM}.
- *
- * Apps can start {@link android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM} to
+ * Called to check if the caller can schedule exact alarms.
+ * <p>
+ * Apps targeting {@link Build.VERSION_CODES#S} or higher can schedule exact alarms if they
+ * have the {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission. These apps can also
+ * start {@link android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM} to
* request this from the user.
+ * <p>
+ * Apps targeting lower sdk versions, can always schedule exact alarms.
*
- * @return {@code true} if the caller has the permission, {@code false} otherwise.
+ * @return {@code true} if the caller can schedule exact alarms.
* @see android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM
+ * @see #setExact(int, long, PendingIntent)
+ * @see #setExactAndAllowWhileIdle(int, long, PendingIntent)
+ * @see #setAlarmClock(AlarmClockInfo, PendingIntent)
*/
public boolean canScheduleExactAlarms() {
return hasScheduleExactAlarm(mContext.getOpPackageName(), mContext.getUserId());
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 0e9efbc..ac20187 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -442,6 +442,7 @@
private long mNextIdlePendingDelay;
private long mNextIdleDelay;
private long mNextLightIdleDelay;
+ private long mNextLightIdleDelayFlex;
private long mNextLightAlarmTime;
private long mNextSensingTimeoutAlarmTime;
@@ -886,16 +887,20 @@
*/
public final class Constants implements DeviceConfig.OnPropertiesChangedListener {
// Key names stored in the settings value.
- private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT
- = "light_after_inactive_to";
+ private static final String KEY_FLEX_TIME_SHORT = "flex_time_short";
+ private static final String KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT =
+ "light_after_inactive_to";
private static final String KEY_LIGHT_PRE_IDLE_TIMEOUT = "light_pre_idle_to";
private static final String KEY_LIGHT_IDLE_TIMEOUT = "light_idle_to";
+ private static final String KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX =
+ "light_idle_to_initial_flex";
+ private static final String KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX = "light_max_idle_to_flex";
private static final String KEY_LIGHT_IDLE_FACTOR = "light_idle_factor";
private static final String KEY_LIGHT_MAX_IDLE_TIMEOUT = "light_max_idle_to";
- private static final String KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET
- = "light_idle_maintenance_min_budget";
- private static final String KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET
- = "light_idle_maintenance_max_budget";
+ private static final String KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET =
+ "light_idle_maintenance_min_budget";
+ private static final String KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET =
+ "light_idle_maintenance_max_budget";
private static final String KEY_MIN_LIGHT_MAINTENANCE_TIME = "min_light_maintenance_time";
private static final String KEY_MIN_DEEP_MAINTENANCE_TIME = "min_deep_maintenance_time";
private static final String KEY_INACTIVE_TIMEOUT = "inactive_to";
@@ -903,6 +908,7 @@
private static final String KEY_LOCATING_TIMEOUT = "locating_to";
private static final String KEY_LOCATION_ACCURACY = "location_accuracy";
private static final String KEY_MOTION_INACTIVE_TIMEOUT = "motion_inactive_to";
+ private static final String KEY_MOTION_INACTIVE_TIMEOUT_FLEX = "motion_inactive_to_flex";
private static final String KEY_IDLE_AFTER_INACTIVE_TIMEOUT = "idle_after_inactive_to";
private static final String KEY_IDLE_PENDING_TIMEOUT = "idle_pending_to";
private static final String KEY_MAX_IDLE_PENDING_TIMEOUT = "max_idle_pending_to";
@@ -929,13 +935,20 @@
"pre_idle_factor_long";
private static final String KEY_PRE_IDLE_FACTOR_SHORT =
"pre_idle_factor_short";
+ private static final String KEY_USE_WINDOW_ALARMS = "use_window_alarms";
+ private static final long DEFAULT_FLEX_TIME_SHORT =
+ !COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
private static final long DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT =
- !COMPRESS_TIME ? 3 * 60 * 1000L : 15 * 1000L;
+ !COMPRESS_TIME ? 60 * 1000L : 15 * 1000L;
private static final long DEFAULT_LIGHT_PRE_IDLE_TIMEOUT =
!COMPRESS_TIME ? 3 * 60 * 1000L : 30 * 1000L;
private static final long DEFAULT_LIGHT_IDLE_TIMEOUT =
!COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L;
+ private static final long DEFAULT_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX =
+ !COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
+ private static final long DEFAULT_LIGHT_MAX_IDLE_TIMEOUT_FLEX =
+ !COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L;
private static final float DEFAULT_LIGHT_IDLE_FACTOR = 2f;
private static final long DEFAULT_LIGHT_MAX_IDLE_TIMEOUT =
!COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L;
@@ -958,6 +971,8 @@
private static final float DEFAULT_LOCATION_ACCURACY = 20f;
private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT =
!COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L;
+ private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT_FLEX =
+ !COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT =
(30 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY =
@@ -983,6 +998,14 @@
private static final boolean DEFAULT_WAIT_FOR_UNLOCK = true;
private static final float DEFAULT_PRE_IDLE_FACTOR_LONG = 1.67f;
private static final float DEFAULT_PRE_IDLE_FACTOR_SHORT = .33f;
+ private static final boolean DEFAULT_USE_WINDOW_ALARMS = true;
+
+ /**
+ * A somewhat short alarm window size that we will tolerate for various alarm timings.
+ *
+ * @see #KEY_FLEX_TIME_SHORT
+ */
+ public long FLEX_TIME_SHORT = DEFAULT_FLEX_TIME_SHORT;
/**
* This is the time, after becoming inactive, that we go in to the first
@@ -1002,13 +1025,28 @@
public long LIGHT_PRE_IDLE_TIMEOUT = DEFAULT_LIGHT_PRE_IDLE_TIMEOUT;
/**
- * This is the initial time that we will run in idle maintenance mode.
+ * This is the initial time that we will run in light idle maintenance mode.
*
* @see #KEY_LIGHT_IDLE_TIMEOUT
*/
public long LIGHT_IDLE_TIMEOUT = DEFAULT_LIGHT_IDLE_TIMEOUT;
/**
+ * This is the initial alarm window size that we will tolerate for light idle maintenance
+ * timing.
+ *
+ * @see #KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX
+ */
+ public long LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = DEFAULT_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX;
+
+ /**
+ * This is the maximum value that {@link #LIGHT_IDLE_TIMEOUT_INITIAL_FLEX} should take.
+ *
+ * @see #KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX
+ */
+ public long LIGHT_MAX_IDLE_TIMEOUT_FLEX = DEFAULT_LIGHT_MAX_IDLE_TIMEOUT_FLEX;
+
+ /**
* Scaling factor to apply to the light idle mode time each time we complete a cycle.
*
* @see #KEY_LIGHT_IDLE_FACTOR
@@ -1016,7 +1054,7 @@
public float LIGHT_IDLE_FACTOR = DEFAULT_LIGHT_IDLE_FACTOR;
/**
- * This is the maximum time we will run in idle maintenance mode.
+ * This is the maximum time we will stay in light idle mode.
*
* @see #KEY_LIGHT_MAX_IDLE_TIMEOUT
*/
@@ -1099,13 +1137,22 @@
/**
* This is the time, after seeing motion, that we wait after becoming inactive from
* that until we start looking for motion again.
+ *
* @see #KEY_MOTION_INACTIVE_TIMEOUT
*/
public long MOTION_INACTIVE_TIMEOUT = DEFAULT_MOTION_INACTIVE_TIMEOUT;
/**
+ * This is the alarm window size we will tolerate for motion detection timings.
+ *
+ * @see #KEY_MOTION_INACTIVE_TIMEOUT_FLEX
+ */
+ public long MOTION_INACTIVE_TIMEOUT_FLEX = DEFAULT_MOTION_INACTIVE_TIMEOUT_FLEX;
+
+ /**
* This is the time, after the inactive timeout elapses, that we will wait looking
* for motion until we truly consider the device to be idle.
+ *
* @see #KEY_IDLE_AFTER_INACTIVE_TIMEOUT
*/
public long IDLE_AFTER_INACTIVE_TIMEOUT = DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT;
@@ -1204,6 +1251,12 @@
public boolean WAIT_FOR_UNLOCK = DEFAULT_WAIT_FOR_UNLOCK;
+ /**
+ * Whether to use window alarms. True to use window alarms (call AlarmManager.setWindow()).
+ * False to use the legacy inexact alarms (call AlarmManager.set()).
+ */
+ public boolean USE_WINDOW_ALARMS = DEFAULT_USE_WINDOW_ALARMS;
+
private final boolean mSmallBatteryDevice;
public Constants() {
@@ -1227,6 +1280,10 @@
continue;
}
switch (name) {
+ case KEY_FLEX_TIME_SHORT:
+ FLEX_TIME_SHORT = properties.getLong(
+ KEY_FLEX_TIME_SHORT, DEFAULT_FLEX_TIME_SHORT);
+ break;
case KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT:
LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = properties.getLong(
KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
@@ -1240,9 +1297,19 @@
LIGHT_IDLE_TIMEOUT = properties.getLong(
KEY_LIGHT_IDLE_TIMEOUT, DEFAULT_LIGHT_IDLE_TIMEOUT);
break;
+ case KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX:
+ LIGHT_IDLE_TIMEOUT_INITIAL_FLEX = properties.getLong(
+ KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX,
+ DEFAULT_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX);
+ break;
+ case KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX:
+ LIGHT_MAX_IDLE_TIMEOUT_FLEX = properties.getLong(
+ KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX,
+ DEFAULT_LIGHT_MAX_IDLE_TIMEOUT_FLEX);
+ break;
case KEY_LIGHT_IDLE_FACTOR:
- LIGHT_IDLE_FACTOR = properties.getFloat(
- KEY_LIGHT_IDLE_FACTOR, DEFAULT_LIGHT_IDLE_FACTOR);
+ LIGHT_IDLE_FACTOR = Math.max(1, properties.getFloat(
+ KEY_LIGHT_IDLE_FACTOR, DEFAULT_LIGHT_IDLE_FACTOR));
break;
case KEY_LIGHT_MAX_IDLE_TIMEOUT:
LIGHT_MAX_IDLE_TIMEOUT = properties.getLong(
@@ -1291,6 +1358,11 @@
MOTION_INACTIVE_TIMEOUT = properties.getLong(
KEY_MOTION_INACTIVE_TIMEOUT, DEFAULT_MOTION_INACTIVE_TIMEOUT);
break;
+ case KEY_MOTION_INACTIVE_TIMEOUT_FLEX:
+ MOTION_INACTIVE_TIMEOUT_FLEX = properties.getLong(
+ KEY_MOTION_INACTIVE_TIMEOUT_FLEX,
+ DEFAULT_MOTION_INACTIVE_TIMEOUT_FLEX);
+ break;
case KEY_IDLE_AFTER_INACTIVE_TIMEOUT:
final long defaultIdleAfterInactiveTimeout = mSmallBatteryDevice
? DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY
@@ -1362,6 +1434,10 @@
PRE_IDLE_FACTOR_SHORT = properties.getFloat(
KEY_PRE_IDLE_FACTOR_SHORT, DEFAULT_PRE_IDLE_FACTOR_SHORT);
break;
+ case KEY_USE_WINDOW_ALARMS:
+ USE_WINDOW_ALARMS = properties.getBoolean(
+ KEY_USE_WINDOW_ALARMS, DEFAULT_USE_WINDOW_ALARMS);
+ break;
default:
Slog.e(TAG, "Unknown configuration key: " + name);
break;
@@ -1373,6 +1449,10 @@
void dump(PrintWriter pw) {
pw.println(" Settings:");
+ pw.print(" "); pw.print(KEY_FLEX_TIME_SHORT); pw.print("=");
+ TimeUtils.formatDuration(FLEX_TIME_SHORT, pw);
+ pw.println();
+
pw.print(" ");
pw.print(KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
pw.print("=");
@@ -1387,6 +1467,14 @@
TimeUtils.formatDuration(LIGHT_IDLE_TIMEOUT, pw);
pw.println();
+ pw.print(" "); pw.print(KEY_LIGHT_IDLE_TIMEOUT_INITIAL_FLEX); pw.print("=");
+ TimeUtils.formatDuration(LIGHT_IDLE_TIMEOUT_INITIAL_FLEX, pw);
+ pw.println();
+
+ pw.print(" "); pw.print(KEY_LIGHT_MAX_IDLE_TIMEOUT_FLEX); pw.print("=");
+ TimeUtils.formatDuration(LIGHT_MAX_IDLE_TIMEOUT_FLEX, pw);
+ pw.println();
+
pw.print(" "); pw.print(KEY_LIGHT_IDLE_FACTOR); pw.print("=");
pw.print(LIGHT_IDLE_FACTOR);
pw.println();
@@ -1431,6 +1519,10 @@
TimeUtils.formatDuration(MOTION_INACTIVE_TIMEOUT, pw);
pw.println();
+ pw.print(" "); pw.print(KEY_MOTION_INACTIVE_TIMEOUT_FLEX); pw.print("=");
+ TimeUtils.formatDuration(MOTION_INACTIVE_TIMEOUT_FLEX, pw);
+ pw.println();
+
pw.print(" "); pw.print(KEY_IDLE_AFTER_INACTIVE_TIMEOUT); pw.print("=");
TimeUtils.formatDuration(IDLE_AFTER_INACTIVE_TIMEOUT, pw);
pw.println();
@@ -1489,6 +1581,9 @@
pw.print(" "); pw.print(KEY_PRE_IDLE_FACTOR_SHORT); pw.print("=");
pw.println(PRE_IDLE_FACTOR_SHORT);
+
+ pw.print(" "); pw.print(KEY_USE_WINDOW_ALARMS); pw.print("=");
+ pw.println(USE_WINDOW_ALARMS);
}
}
@@ -3199,7 +3294,8 @@
mLightState = LIGHT_STATE_INACTIVE;
if (DEBUG) Slog.d(TAG, "Moved from LIGHT_STATE_ACTIVE to LIGHT_STATE_INACTIVE");
resetLightIdleManagementLocked();
- scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
+ scheduleLightAlarmLocked(mConstants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
+ mConstants.FLEX_TIME_SHORT);
EventLogTags.writeDeviceIdleLight(mLightState, "no activity");
}
}
@@ -3219,6 +3315,7 @@
private void resetLightIdleManagementLocked() {
mNextLightIdleDelay = 0;
+ mNextLightIdleDelayFlex = 0;
mCurLightIdleBudget = 0;
cancelLightAlarmLocked();
}
@@ -3265,13 +3362,15 @@
mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
// Reset the upcoming idle delays.
mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
+ mNextLightIdleDelayFlex = mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX;
mMaintenanceStartTime = 0;
if (!isOpsInactiveLocked()) {
// We have some active ops going on... give them a chance to finish
// before going in to our first idle.
mLightState = LIGHT_STATE_PRE_IDLE;
EventLogTags.writeDeviceIdleLight(mLightState, reason);
- scheduleLightAlarmLocked(mConstants.LIGHT_PRE_IDLE_TIMEOUT);
+ scheduleLightAlarmLocked(mConstants.LIGHT_PRE_IDLE_TIMEOUT,
+ mConstants.FLEX_TIME_SHORT);
break;
}
// Nothing active, fall through to immediately idle.
@@ -3290,12 +3389,11 @@
}
}
mMaintenanceStartTime = 0;
- scheduleLightAlarmLocked(mNextLightIdleDelay);
+ scheduleLightAlarmLocked(mNextLightIdleDelay, mNextLightIdleDelayFlex);
mNextLightIdleDelay = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT,
- (long)(mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR));
- if (mNextLightIdleDelay < mConstants.LIGHT_IDLE_TIMEOUT) {
- mNextLightIdleDelay = mConstants.LIGHT_IDLE_TIMEOUT;
- }
+ (long) (mNextLightIdleDelay * mConstants.LIGHT_IDLE_FACTOR));
+ mNextLightIdleDelayFlex = Math.min(mConstants.LIGHT_MAX_IDLE_TIMEOUT_FLEX,
+ (long) (mNextLightIdleDelayFlex * mConstants.LIGHT_IDLE_FACTOR));
if (DEBUG) Slog.d(TAG, "Moved to LIGHT_STATE_IDLE.");
mLightState = LIGHT_STATE_IDLE;
EventLogTags.writeDeviceIdleLight(mLightState, reason);
@@ -3315,7 +3413,7 @@
} else if (mCurLightIdleBudget > mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET) {
mCurLightIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
}
- scheduleLightAlarmLocked(mCurLightIdleBudget);
+ scheduleLightAlarmLocked(mCurLightIdleBudget, mConstants.FLEX_TIME_SHORT);
if (DEBUG) Slog.d(TAG,
"Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE.");
mLightState = LIGHT_STATE_IDLE_MAINTENANCE;
@@ -3326,7 +3424,7 @@
// We'd like to do maintenance, but currently don't have network
// connectivity... let's try to wait until the network comes back.
// We'll only wait for another full idle period, however, and then give up.
- scheduleLightAlarmLocked(mNextLightIdleDelay);
+ scheduleLightAlarmLocked(mNextLightIdleDelay, mNextLightIdleDelayFlex / 2);
if (DEBUG) Slog.d(TAG, "Moved to LIGHT_WAITING_FOR_NETWORK.");
mLightState = LIGHT_STATE_WAITING_FOR_NETWORK;
EventLogTags.writeDeviceIdleLight(mLightState, reason);
@@ -3844,40 +3942,75 @@
mAlarmManager.setIdleUntil(AlarmManager.ELAPSED_REALTIME_WAKEUP,
mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
} else {
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
+ if (mConstants.USE_WINDOW_ALARMS) {
+ mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ mConstants.FLEX_TIME_SHORT,
+ mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
+ } else {
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ mNextAlarmTime, "DeviceIdleController.deep", mDeepAlarmListener, mHandler);
+ }
}
}
- void scheduleLightAlarmLocked(long delay) {
- if (DEBUG) Slog.d(TAG, "scheduleLightAlarmLocked(" + delay + ")");
+ void scheduleLightAlarmLocked(long delay, long flex) {
+ if (DEBUG) {
+ Slog.d(TAG, "scheduleLightAlarmLocked(" + delay
+ + (mConstants.USE_WINDOW_ALARMS ? "/" + flex : "") + ")");
+ }
mNextLightAlarmTime = SystemClock.elapsedRealtime() + delay;
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler);
+ if (mConstants.USE_WINDOW_ALARMS) {
+ mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNextLightAlarmTime, flex,
+ "DeviceIdleController.light", mLightAlarmListener, mHandler);
+ } else {
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNextLightAlarmTime,
+ "DeviceIdleController.light", mLightAlarmListener, mHandler);
+ }
}
private void scheduleMotionRegistrationAlarmLocked() {
if (DEBUG) Slog.d(TAG, "scheduleMotionRegistrationAlarmLocked");
long nextMotionRegistrationAlarmTime =
mInjector.getElapsedRealtime() + mConstants.MOTION_INACTIVE_TIMEOUT / 2;
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionRegistrationAlarmTime,
- "DeviceIdleController.motion_registration", mMotionRegistrationAlarmListener,
- mHandler);
+ if (mConstants.USE_WINDOW_ALARMS) {
+ mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ nextMotionRegistrationAlarmTime, mConstants.MOTION_INACTIVE_TIMEOUT_FLEX,
+ "DeviceIdleController.motion_registration", mMotionRegistrationAlarmListener,
+ mHandler);
+ } else {
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionRegistrationAlarmTime,
+ "DeviceIdleController.motion_registration", mMotionRegistrationAlarmListener,
+ mHandler);
+ }
}
private void scheduleMotionTimeoutAlarmLocked() {
if (DEBUG) Slog.d(TAG, "scheduleMotionAlarmLocked");
long nextMotionTimeoutAlarmTime =
mInjector.getElapsedRealtime() + mConstants.MOTION_INACTIVE_TIMEOUT;
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionTimeoutAlarmTime,
- "DeviceIdleController.motion", mMotionTimeoutAlarmListener, mHandler);
+ if (mConstants.USE_WINDOW_ALARMS) {
+ mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ nextMotionTimeoutAlarmTime,
+ mConstants.MOTION_INACTIVE_TIMEOUT_FLEX,
+ "DeviceIdleController.motion", mMotionTimeoutAlarmListener, mHandler);
+ } else {
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextMotionTimeoutAlarmTime,
+ "DeviceIdleController.motion", mMotionTimeoutAlarmListener, mHandler);
+ }
}
void scheduleSensingTimeoutAlarmLocked(long delay) {
if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")");
mNextSensingTimeoutAlarmTime = SystemClock.elapsedRealtime() + delay;
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNextSensingTimeoutAlarmTime,
- "DeviceIdleController.sensing", mSensingTimeoutAlarmListener, mHandler);
+ if (mConstants.USE_WINDOW_ALARMS) {
+ mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ mNextSensingTimeoutAlarmTime,
+ mConstants.FLEX_TIME_SHORT,
+ "DeviceIdleController.sensing", mSensingTimeoutAlarmListener, mHandler);
+ } else {
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNextSensingTimeoutAlarmTime,
+ "DeviceIdleController.sensing", mSensingTimeoutAlarmListener, mHandler);
+ }
}
private static int[] buildAppIdArray(ArrayMap<String, Integer> systemApps,
@@ -4852,7 +4985,13 @@
if (mNextLightIdleDelay != 0) {
pw.print(" mNextIdleDelay=");
TimeUtils.formatDuration(mNextLightIdleDelay, pw);
- pw.println();
+ if (mConstants.USE_WINDOW_ALARMS) {
+ pw.print(" (flex=");
+ TimeUtils.formatDuration(mNextLightIdleDelayFlex, pw);
+ pw.println(")");
+ } else {
+ pw.println();
+ }
}
if (mNextLightAlarmTime != 0) {
pw.print(" mNextLightAlarmTime=");
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 fe0c7f7..70e548d 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -2006,16 +2006,12 @@
windowLength = INTERVAL_DAY;
} else if ((flags & FLAG_PRIORITIZE) == 0 && windowLength < minAllowedWindow) {
// Prioritized alarms are exempt from minimum window limits.
- if (CompatChanges.isChangeEnabled(
+ if (!isExemptFromMinWindowRestrictions(callingUid) && CompatChanges.isChangeEnabled(
AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, callingPackage,
UserHandle.getUserHandleForUid(callingUid))) {
Slog.w(TAG, "Window length " + windowLength + "ms too short; expanding to "
+ minAllowedWindow + "ms.");
windowLength = minAllowedWindow;
- } else {
- // TODO (b/185199076): Remove temporary log to catch breaking apps.
- Slog.wtf(TAG, "Short window " + windowLength + "ms specified by "
- + callingPackage);
}
}
maxElapsed = triggerElapsed + windowLength;
@@ -2409,6 +2405,13 @@
}
/**
+ * Returns true if the given uid can set window to be as small as it wants.
+ */
+ boolean isExemptFromMinWindowRestrictions(int uid) {
+ return isExemptFromExactAlarmPermission(uid);
+ }
+
+ /**
* Returns true if the given uid does not require SCHEDULE_EXACT_ALARM to set exact,
* allow-while-idle alarms.
*/
@@ -2569,6 +2572,9 @@
throw new SecurityException("Uid " + callingUid
+ " cannot query hasScheduleExactAlarm for uid " + uid);
}
+ if (!isExactAlarmChangeEnabled(packageName, userId)) {
+ return true;
+ }
return (uid > 0) ? hasScheduleExactAlarmInternal(packageName, uid) : false;
}
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 96cbed7..78670c7 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -73,7 +73,9 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import android.util.SparseLongArray;
import android.util.SparseSetArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -686,26 +688,92 @@
final Constants mConstants;
final ConstantsObserver mConstantsObserver;
- private static final Comparator<JobStatus> sPendingJobComparator = (o1, o2) -> {
- // Jobs with an override state set (via adb) should be put first as tests/developers
- // expect the jobs to run immediately.
- if (o1.overrideState != o2.overrideState) {
- // Higher override state (OVERRIDE_FULL) should be before lower state (OVERRIDE_SOFT)
- return o2.overrideState - o1.overrideState;
- }
- if (o1.getSourceUid() == o2.getSourceUid()) {
- final boolean o1FGJ = o1.isRequestedExpeditedJob();
- if (o1FGJ != o2.isRequestedExpeditedJob()) {
- // Attempt to run requested expedited jobs ahead of regular jobs, regardless of
- // expedited job quota.
- return o1FGJ ? -1 : 1;
+ @VisibleForTesting
+ class PendingJobComparator implements Comparator<JobStatus> {
+ private final SparseBooleanArray mUidHasEjCache = new SparseBooleanArray();
+ private final SparseLongArray mEarliestRegEnqueueTimeCache = new SparseLongArray();
+
+ /**
+ * Refresh sorting determinants based on the current state of {@link #mPendingJobs}.
+ */
+ @GuardedBy("mLock")
+ @VisibleForTesting
+ void refreshLocked() {
+ mUidHasEjCache.clear();
+ mEarliestRegEnqueueTimeCache.clear();
+ for (int i = 0; i < mPendingJobs.size(); ++i) {
+ final JobStatus job = mPendingJobs.get(i);
+ final int uid = job.getSourceUid();
+ if (job.isRequestedExpeditedJob()) {
+ mUidHasEjCache.put(uid, true);
+ } else {
+ final long earliestEnqueueTime =
+ mEarliestRegEnqueueTimeCache.get(uid, Long.MAX_VALUE);
+ mEarliestRegEnqueueTimeCache.put(uid,
+ Math.min(earliestEnqueueTime, job.enqueueTime));
+ }
}
}
- if (o1.enqueueTime < o2.enqueueTime) {
- return -1;
+
+ @Override
+ public int compare(JobStatus o1, JobStatus o2) {
+ if (o1 == o2) {
+ return 0;
+ }
+ // Jobs with an override state set (via adb) should be put first as tests/developers
+ // expect the jobs to run immediately.
+ if (o1.overrideState != o2.overrideState) {
+ // Higher override state (OVERRIDE_FULL) should be before lower state
+ // (OVERRIDE_SOFT)
+ return o2.overrideState - o1.overrideState;
+ }
+ final boolean o1EJ = o1.isRequestedExpeditedJob();
+ final boolean o2EJ = o2.isRequestedExpeditedJob();
+ if (o1.getSourceUid() == o2.getSourceUid()) {
+ if (o1EJ != o2EJ) {
+ // Attempt to run requested expedited jobs ahead of regular jobs, regardless of
+ // expedited job quota.
+ return o1EJ ? -1 : 1;
+ }
+ }
+ final boolean uid1HasEj = mUidHasEjCache.get(o1.getSourceUid());
+ final boolean uid2HasEj = mUidHasEjCache.get(o2.getSourceUid());
+ if ((uid1HasEj || uid2HasEj) && (o1EJ || o2EJ)) {
+ // We MUST prioritize EJs ahead of regular jobs within a single app. Since we do
+ // that, in order to satisfy the transitivity constraint of the comparator, if
+ // any UID has an EJ, we must ensure that the EJ is ordered ahead of the regular
+ // job of a different app IF the app with an EJ had another job that came before
+ // the differing app. For example, if app A has regJob1 at t1 and eJob3 at t3 and
+ // app B has regJob2 at t2, eJob3 must be ordered before regJob2 because it will be
+ // ordered before regJob1.
+ // Regular jobs don't need to jump the line.
+
+ final long uid1EarliestRegEnqueueTime = Math.min(o1.enqueueTime,
+ mEarliestRegEnqueueTimeCache.get(o1.getSourceUid(), Long.MAX_VALUE));
+ final long uid2EarliestRegEnqueueTime = Math.min(o2.enqueueTime,
+ mEarliestRegEnqueueTimeCache.get(o2.getSourceUid(), Long.MAX_VALUE));
+
+ if (o1EJ && o2EJ) {
+ if (uid1EarliestRegEnqueueTime < uid2EarliestRegEnqueueTime) {
+ return -1;
+ } else if (uid1EarliestRegEnqueueTime > uid2EarliestRegEnqueueTime) {
+ return 1;
+ }
+ } else if (o1EJ && uid1EarliestRegEnqueueTime < o2.enqueueTime) {
+ return -1;
+ } else if (o2EJ && uid2EarliestRegEnqueueTime < o1.enqueueTime) {
+ return 1;
+ }
+ }
+ if (o1.enqueueTime < o2.enqueueTime) {
+ return -1;
+ }
+ return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
}
- return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
- };
+ }
+
+ @VisibleForTesting
+ final PendingJobComparator mPendingJobComparator = new PendingJobComparator();
static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
int where = Collections.binarySearch(array, newItem, comparator);
@@ -1115,7 +1183,7 @@
// This is a new job, we can just immediately put it on the pending
// list and try to run it.
mJobPackageTracker.notePending(jobStatus);
- addOrderedItem(mPendingJobs, jobStatus, sPendingJobComparator);
+ addOrderedItem(mPendingJobs, jobStatus, mPendingJobComparator);
maybeRunPendingJobsLocked();
} else {
evaluateControllerStatesLocked(jobStatus);
@@ -1919,7 +1987,7 @@
if (js != null) {
if (isReadyToBeExecutedLocked(js)) {
mJobPackageTracker.notePending(js);
- addOrderedItem(mPendingJobs, js, sPendingJobComparator);
+ addOrderedItem(mPendingJobs, js, mPendingJobComparator);
}
} else {
Slog.e(TAG, "Given null job to check individually");
@@ -2064,6 +2132,7 @@
* Run through list of jobs and execute all possible - at least one is expired so we do
* as many as we can.
*/
+ @GuardedBy("mLock")
private void queueReadyJobsForExecutionLocked() {
// This method will check and capture all ready jobs, so we don't need to keep any messages
// in the queue.
@@ -2079,7 +2148,7 @@
mPendingJobs.clear();
stopNonReadyActiveJobsLocked();
mJobs.forEachJob(mReadyQueueFunctor);
- mReadyQueueFunctor.postProcess();
+ mReadyQueueFunctor.postProcessLocked();
if (DEBUG) {
final int queuedJobs = mPendingJobs.size();
@@ -2106,16 +2175,19 @@
}
}
- public void postProcess() {
+ @GuardedBy("mLock")
+ private void postProcessLocked() {
noteJobsPending(newReadyJobs);
mPendingJobs.addAll(newReadyJobs);
if (mPendingJobs.size() > 1) {
- mPendingJobs.sort(sPendingJobComparator);
+ mPendingJobComparator.refreshLocked();
+ mPendingJobs.sort(mPendingJobComparator);
}
newReadyJobs.clear();
}
}
+
private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
/**
@@ -2180,7 +2252,9 @@
}
}
- public void postProcess() {
+ @GuardedBy("mLock")
+ @VisibleForTesting
+ void postProcessLocked() {
if (unbatchedCount > 0
|| forceBatchedCount >= mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT) {
if (DEBUG) {
@@ -2189,7 +2263,8 @@
noteJobsPending(runnableJobs);
mPendingJobs.addAll(runnableJobs);
if (mPendingJobs.size() > 1) {
- mPendingJobs.sort(sPendingJobComparator);
+ mPendingJobComparator.refreshLocked();
+ mPendingJobs.sort(mPendingJobComparator);
}
} else {
if (DEBUG) {
@@ -2210,6 +2285,7 @@
}
private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
+ @GuardedBy("mLock")
private void maybeQueueReadyJobsForExecutionLocked() {
if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
@@ -2217,7 +2293,7 @@
mPendingJobs.clear();
stopNonReadyActiveJobsLocked();
mJobs.forEachJob(mMaybeQueueFunctor);
- mMaybeQueueFunctor.postProcess();
+ mMaybeQueueFunctor.postProcessLocked();
}
/** Returns true if both the calling and source users for the job are started. */
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index f741596..7a28407 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -16,6 +16,9 @@
package com.android.server.job;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
+
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static com.android.server.job.JobSchedulerService.sSystemClock;
@@ -30,6 +33,7 @@
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -63,6 +67,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
+import java.util.StringJoiner;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -387,6 +392,36 @@
}
/**
+ * Returns a single string representation of the contents of the specified intArray.
+ * If the intArray is [1, 2, 4] as the input, the return result will be the string "1,2,4".
+ */
+ @VisibleForTesting
+ static String intArrayToString(int[] values) {
+ final StringJoiner sj = new StringJoiner(",");
+ for (final int value : values) {
+ sj.add(String.valueOf(value));
+ }
+ return sj.toString();
+ }
+
+
+ /**
+ * Converts a string containing a comma-separated list of decimal representations
+ * of ints into an array of int. If the string is not correctly formatted,
+ * or if any value doesn't fit into an int, NumberFormatException is thrown.
+ */
+ @VisibleForTesting
+ static int[] stringToIntArray(String str) {
+ if (TextUtils.isEmpty(str)) return new int[0];
+ final String[] arr = str.split(",");
+ final int[] values = new int[arr.length];
+ for (int i = 0; i < arr.length; i++) {
+ values[i] = Integer.parseInt(arr[i]);
+ }
+ return values;
+ }
+
+ /**
* Runnable that writes {@link #mJobSet} out to xml.
* NOTE: This Runnable locks on mLock
*/
@@ -549,15 +584,12 @@
out.startTag(null, XML_TAG_PARAMS_CONSTRAINTS);
if (jobStatus.hasConnectivityConstraint()) {
final NetworkRequest network = jobStatus.getJob().getRequiredNetwork();
- // STOPSHIP b/183071974: improve the scheme for backward compatibility and
- // mainline cleanliness.
- out.attribute(null, "net-capabilities", Long.toString(
- BitUtils.packBits(network.getCapabilities())));
- out.attribute(null, "net-unwanted-capabilities", Long.toString(
- BitUtils.packBits(network.getForbiddenCapabilities())));
-
- out.attribute(null, "net-transport-types", Long.toString(
- BitUtils.packBits(network.getTransportTypes())));
+ out.attribute(null, "net-capabilities-csv", intArrayToString(
+ network.getCapabilities()));
+ out.attribute(null, "net-forbidden-capabilities-csv", intArrayToString(
+ network.getForbiddenCapabilities()));
+ out.attribute(null, "net-transport-types-csv", intArrayToString(
+ network.getTransportTypes()));
}
if (jobStatus.hasIdleConstraint()) {
out.attribute(null, "idle", Boolean.toString(true));
@@ -831,7 +863,14 @@
} catch (NumberFormatException e) {
Slog.d(TAG, "Error reading constraints, skipping.");
return null;
+ } catch (XmlPullParserException e) {
+ Slog.d(TAG, "Error Parser Exception.", e);
+ return null;
+ } catch (IOException e) {
+ Slog.d(TAG, "Error I/O Exception.", e);
+ return null;
}
+
parser.next(); // Consume </constraints>
// Read out execution parameters tag.
@@ -973,31 +1012,79 @@
return new JobInfo.Builder(jobId, cname);
}
- private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) {
+ /**
+ * In S, there has been a change in format to make the code more robust and more
+ * maintainable.
+ * If the capabities are bits 4, 14, 15, the format in R, it is a long string as
+ * netCapabilitiesLong = '49168' from the old XML file attribute "net-capabilities".
+ * The format in S is the int array string as netCapabilitiesIntArray = '4,14,15'
+ * from the new XML file attribute "net-capabilities-array".
+ * For backward compatibility, when reading old XML the old format is still supported in
+ * reading, but in order to avoid issues with OEM-defined flags, the accepted capabilities
+ * are limited to that(maxNetCapabilityInR & maxTransportInR) defined in R.
+ */
+ private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser)
+ throws XmlPullParserException, IOException {
String val;
+ String netCapabilitiesLong = null;
+ String netForbiddenCapabilitiesLong = null;
+ String netTransportTypesLong = null;
- final String netCapabilities = parser.getAttributeValue(null, "net-capabilities");
- final String netforbiddenCapabilities = parser.getAttributeValue(
- null, "net-unwanted-capabilities");
- final String netTransportTypes = parser.getAttributeValue(null, "net-transport-types");
- if (netCapabilities != null && netTransportTypes != null) {
+ final String netCapabilitiesIntArray = parser.getAttributeValue(
+ null, "net-capabilities-csv");
+ final String netForbiddenCapabilitiesIntArray = parser.getAttributeValue(
+ null, "net-forbidden-capabilities-csv");
+ final String netTransportTypesIntArray = parser.getAttributeValue(
+ null, "net-transport-types-csv");
+ if (netCapabilitiesIntArray == null || netTransportTypesIntArray == null) {
+ netCapabilitiesLong = parser.getAttributeValue(null, "net-capabilities");
+ netForbiddenCapabilitiesLong = parser.getAttributeValue(
+ null, "net-unwanted-capabilities");
+ netTransportTypesLong = parser.getAttributeValue(null, "net-transport-types");
+ }
+
+ if ((netCapabilitiesIntArray != null) && (netTransportTypesIntArray != null)) {
final NetworkRequest.Builder builder = new NetworkRequest.Builder()
.clearCapabilities();
- final long forbiddenCapabilities = netforbiddenCapabilities != null
- ? Long.parseLong(netforbiddenCapabilities)
- : BitUtils.packBits(builder.build().getForbiddenCapabilities());
- // We're okay throwing NFE here; caught by caller
- for (int capability : BitUtils.unpackBits(Long.parseLong(netCapabilities))) {
+
+ for (int capability : stringToIntArray(netCapabilitiesIntArray)) {
builder.addCapability(capability);
}
- for (int forbiddenCapability : BitUtils.unpackBits(
- Long.parseLong(netforbiddenCapabilities))) {
+
+ for (int forbiddenCapability : stringToIntArray(netForbiddenCapabilitiesIntArray)) {
builder.addForbiddenCapability(forbiddenCapability);
}
- for (int transport : BitUtils.unpackBits(Long.parseLong(netTransportTypes))) {
+
+ for (int transport : stringToIntArray(netTransportTypesIntArray)) {
builder.addTransportType(transport);
}
jobBuilder.setRequiredNetwork(builder.build());
+ } else if (netCapabilitiesLong != null && netTransportTypesLong != null) {
+ final NetworkRequest.Builder builder = new NetworkRequest.Builder()
+ .clearCapabilities();
+ final int maxNetCapabilityInR = NET_CAPABILITY_TEMPORARILY_NOT_METERED;
+ // We're okay throwing NFE here; caught by caller
+ for (int capability : BitUtils.unpackBits(Long.parseLong(
+ netCapabilitiesLong))) {
+ if (capability <= maxNetCapabilityInR) {
+ builder.addCapability(capability);
+ }
+ }
+ for (int forbiddenCapability : BitUtils.unpackBits(Long.parseLong(
+ netForbiddenCapabilitiesLong))) {
+ if (forbiddenCapability <= maxNetCapabilityInR) {
+ builder.addForbiddenCapability(forbiddenCapability);
+ }
+ }
+
+ final int maxTransportInR = TRANSPORT_TEST;
+ for (int transport : BitUtils.unpackBits(Long.parseLong(
+ netTransportTypesLong))) {
+ if (transport <= maxTransportInR) {
+ builder.addTransportType(transport);
+ }
+ }
+ jobBuilder.setRequiredNetwork(builder.build());
} else {
// Read legacy values
val = parser.getAttributeValue(null, "connectivity");
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
index 548a1ac..31a0853 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -82,7 +82,11 @@
@Override
public void evaluateStateLocked(JobStatus jobStatus) {
- updateSingleJobRestrictionLocked(jobStatus, sElapsedRealtimeClock.millis(), UNKNOWN);
+ if (jobStatus.isRequestedExpeditedJob()) {
+ // Only requested-EJs could have their run-in-bg constraint change outside of something
+ // coming through the ForceAppStandbyListener.
+ updateSingleJobRestrictionLocked(jobStatus, sElapsedRealtimeClock.millis(), UNKNOWN);
+ }
}
@Override
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index 37b3c04..d532e20 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -20,6 +20,7 @@
import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
import static android.app.usage.UsageStatsManager.REASON_SUB_MASK;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
@@ -259,8 +260,10 @@
int bucketingReason = REASON_MAIN_USAGE | usageReason;
final boolean isUserUsage = isUserUsage(bucketingReason);
- if (appUsageHistory.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage) {
- // Only user usage should bring an app out of the RESTRICTED bucket.
+ if (appUsageHistory.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage
+ && (appUsageHistory.bucketingReason & REASON_MAIN_MASK) != REASON_MAIN_TIMEOUT) {
+ // Only user usage should bring an app out of the RESTRICTED bucket, unless the app
+ // just timed out into RESTRICTED.
newBucket = STANDBY_BUCKET_RESTRICTED;
bucketingReason = appUsageHistory.bucketingReason;
} else {
diff --git a/api/Android.bp b/api/Android.bp
index db1f64c..2ea180e 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -24,6 +24,41 @@
default_applicable_licenses: ["frameworks_base_license"],
}
+python_binary_host {
+ name: "api_versions_trimmer",
+ srcs: ["api_versions_trimmer.py"],
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ embedded_launcher: false,
+ },
+ },
+}
+
+python_test_host {
+ name: "api_versions_trimmer_unittests",
+ main: "api_versions_trimmer_unittests.py",
+ srcs: [
+ "api_versions_trimmer_unittests.py",
+ "api_versions_trimmer.py",
+ ],
+ test_options: {
+ unit_test: true,
+ },
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ embedded_launcher: false,
+ },
+ },
+}
+
metalava_cmd = "$(location metalava)"
// Silence reflection warnings. See b/168689341
metalava_cmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
@@ -69,7 +104,10 @@
dest: "current.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/public/api",
dest: "android.txt",
},
@@ -151,7 +189,10 @@
dest: "removed.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/public/api",
dest: "removed.txt",
},
@@ -187,7 +228,10 @@
dest: "system-current.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system/api",
dest: "android.txt",
},
@@ -242,7 +286,10 @@
dest: "system-removed.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system/api",
dest: "removed.txt",
},
@@ -279,7 +326,10 @@
dest: "module-lib-current.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/module-lib/api",
dest: "android.txt",
},
@@ -336,7 +386,10 @@
dest: "module-lib-removed.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/module-lib/api",
dest: "removed.txt",
},
@@ -377,7 +430,10 @@
dest: "system-server-current.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system-server/api",
dest: "android.txt",
},
@@ -401,9 +457,50 @@
dest: "system-server-removed.txt",
},
{
- targets: ["sdk", "win_sdk"],
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
dir: "apistubs/android/system-server/api",
dest: "removed.txt",
},
],
}
+
+// This rule will filter classes present in the jar files of mainline modules
+// from the lint database in api-versions.xml.
+// This is done to reduce the number of false positive NewApi findings in
+// java libraries that compile against the module SDK
+genrule {
+ name: "api-versions-xml-public-filtered",
+ srcs: [
+ // Note: order matters: first parameter is the full api-versions.xml
+ // after that the stubs files in any order
+ // stubs files are all modules that export API surfaces EXCEPT ART
+ ":framework-doc-stubs{.api_versions.xml}",
+ ":android.net.ipsec.ike.stubs{.jar}",
+ ":conscrypt.module.public.api.stubs{.jar}",
+ ":framework-appsearch.stubs{.jar}",
+ ":framework-connectivity.stubs{.jar}",
+ ":framework-graphics.stubs{.jar}",
+ ":framework-media.stubs{.jar}",
+ ":framework-mediaprovider.stubs{.jar}",
+ ":framework-permission.stubs{.jar}",
+ ":framework-permission-s.stubs{.jar}",
+ ":framework-scheduling.stubs{.jar}",
+ ":framework-sdkextensions.stubs{.jar}",
+ ":framework-statsd.stubs{.jar}",
+ ":framework-tethering.stubs{.jar}",
+ ":framework-wifi.stubs{.jar}",
+ ":i18n.module.public.api.stubs{.jar}",
+ ],
+ out: ["api-versions-public-filtered.xml"],
+ tools: ["api_versions_trimmer"],
+ cmd: "$(location api_versions_trimmer) $(out) $(in)",
+ dist: {
+ targets: [
+ "sdk",
+ "win_sdk",
+ ],
+ },
+}
diff --git a/api/OWNERS b/api/OWNERS
index 88d0b61..a027270 100644
--- a/api/OWNERS
+++ b/api/OWNERS
@@ -1 +1,6 @@
+hansson@google.com
+
+# Modularization team
+file:platform/packages/modules/common:/OWNERS
+
per-file Android.bp = file:platform/build/soong:/OWNERS
diff --git a/api/api_versions_trimmer.py b/api/api_versions_trimmer.py
new file mode 100755
index 0000000..9afd95a
--- /dev/null
+++ b/api/api_versions_trimmer.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+"""Script to remove mainline APIs from the api-versions.xml."""
+
+import argparse
+import re
+import xml.etree.ElementTree as ET
+import zipfile
+
+
+def read_classes(stubs):
+ """Read classes from the stubs file.
+
+ Args:
+ stubs: argument can be a path to a file (a string), a file-like object or a
+ path-like object
+
+ Returns:
+ a set of the classes found in the file (set of strings)
+ """
+ classes = set()
+ with zipfile.ZipFile(stubs) as z:
+ for info in z.infolist():
+ if (not info.is_dir()
+ and info.filename.endswith(".class")
+ and not info.filename.startswith("META-INF")):
+ # drop ".class" extension
+ classes.add(info.filename[:-6])
+ return classes
+
+
+def filter_method_tag(method, classes_to_remove):
+ """Updates the signature of this method by calling filter_method_signature.
+
+ Updates the method passed into this function.
+
+ Args:
+ method: xml element that represents a method
+ classes_to_remove: set of classes you to remove
+ """
+ filtered = filter_method_signature(method.get("name"), classes_to_remove)
+ method.set("name", filtered)
+
+
+def filter_method_signature(signature, classes_to_remove):
+ """Removes mentions of certain classes from this method signature.
+
+ Replaces any existing classes that need to be removed, with java/lang/Object
+
+ Args:
+ signature: string that is a java representation of a method signature
+ classes_to_remove: set of classes you to remove
+ """
+ regex = re.compile("L.*?;")
+ start = signature.find("(")
+ matches = set(regex.findall(signature[start:]))
+ for m in matches:
+ # m[1:-1] to drop the leading `L` and `;` ending
+ if m[1:-1] in classes_to_remove:
+ signature = signature.replace(m, "Ljava/lang/Object;")
+ return signature
+
+
+def filter_lint_database(database, classes_to_remove, output):
+ """Reads a lint database and writes a filtered version without some classes.
+
+ Reads database from api-versions.xml and removes any references to classes
+ in the second argument. Writes the result (another xml with the same format
+ of the database) to output.
+
+ Args:
+ database: path to xml with lint database to read
+ classes_to_remove: iterable (ideally a set or similar for quick
+ lookups) that enumerates the classes that should be removed
+ output: path to write the filtered database
+ """
+ xml = ET.parse(database)
+ root = xml.getroot()
+ for c in xml.findall("class"):
+ cname = c.get("name")
+ if cname in classes_to_remove:
+ root.remove(c)
+ else:
+ # find the <extends /> tag inside this class to see if the parent
+ # has been removed from the known classes (attribute called name)
+ super_classes = c.findall("extends")
+ for super_class in super_classes:
+ super_class_name = super_class.get("name")
+ if super_class_name in classes_to_remove:
+ super_class.set("name", "java/lang/Object")
+ interfaces = c.findall("implements")
+ for interface in interfaces:
+ interface_name = interface.get("name")
+ if interface_name in classes_to_remove:
+ c.remove(interface)
+ for method in c.findall("method"):
+ filter_method_tag(method, classes_to_remove)
+ xml.write(output)
+
+
+def main():
+ """Run the program."""
+ parser = argparse.ArgumentParser(
+ description=
+ ("Read a lint database (api-versions.xml) and many stubs jar files. "
+ "Produce another database file that doesn't include the classes present "
+ "in the stubs file(s)."))
+ parser.add_argument("output", help="Destination of the result (xml file).")
+ parser.add_argument(
+ "api_versions",
+ help="The lint database (api-versions.xml file) to read data from"
+ )
+ parser.add_argument("stubs", nargs="+", help="The stubs jar file(s)")
+ parsed = parser.parse_args()
+ classes = set()
+ for stub in parsed.stubs:
+ classes.update(read_classes(stub))
+ filter_lint_database(parsed.api_versions, classes, parsed.output)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/api/api_versions_trimmer_unittests.py b/api/api_versions_trimmer_unittests.py
new file mode 100644
index 0000000..4eb929e
--- /dev/null
+++ b/api/api_versions_trimmer_unittests.py
@@ -0,0 +1,307 @@
+#!/usr/bin/env python3
+#
+# 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.
+
+import io
+import re
+import unittest
+import xml.etree.ElementTree as ET
+import zipfile
+
+import api_versions_trimmer
+
+
+def create_in_memory_zip_file(files):
+ f = io.BytesIO()
+ with zipfile.ZipFile(f, "w") as z:
+ for fname in files:
+ with z.open(fname, mode="w") as class_file:
+ class_file.write(b"")
+ return f
+
+
+def indent(elem, level=0):
+ i = "\n" + level * " "
+ j = "\n" + (level - 1) * " "
+ if len(elem):
+ if not elem.text or not elem.text.strip():
+ elem.text = i + " "
+ if not elem.tail or not elem.tail.strip():
+ elem.tail = i
+ for subelem in elem:
+ indent(subelem, level + 1)
+ if not elem.tail or not elem.tail.strip():
+ elem.tail = j
+ else:
+ if level and (not elem.tail or not elem.tail.strip()):
+ elem.tail = j
+ return elem
+
+
+def pretty_print(s):
+ tree = ET.parse(io.StringIO(s))
+ el = indent(tree.getroot())
+ res = ET.tostring(el).decode("utf-8")
+ # remove empty lines inside the result because this still breaks some
+ # comparisons
+ return re.sub(r"\n\s*\n", "\n", res, re.MULTILINE)
+
+
+class ApiVersionsTrimmerUnittests(unittest.TestCase):
+
+ def setUp(self):
+ # so it prints diffs in long strings (xml files)
+ self.maxDiff = None
+
+ def test_read_classes(self):
+ f = create_in_memory_zip_file(
+ ["a/b/C.class",
+ "a/b/D.class",
+ ]
+ )
+ res = api_versions_trimmer.read_classes(f)
+ self.assertEqual({"a/b/C", "a/b/D"}, res)
+
+ def test_read_classes_ignore_dex(self):
+ f = create_in_memory_zip_file(
+ ["a/b/C.class",
+ "a/b/D.class",
+ "a/b/E.dex",
+ "f.dex",
+ ]
+ )
+ res = api_versions_trimmer.read_classes(f)
+ self.assertEqual({"a/b/C", "a/b/D"}, res)
+
+ def test_read_classes_ignore_manifest(self):
+ f = create_in_memory_zip_file(
+ ["a/b/C.class",
+ "a/b/D.class",
+ "META-INFO/G.class"
+ ]
+ )
+ res = api_versions_trimmer.read_classes(f)
+ self.assertEqual({"a/b/C", "a/b/D"}, res)
+
+ def test_filter_method_signature(self):
+ xml = """
+ <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
+ """
+ method = ET.fromstring(xml)
+ classes_to_remove = {"android/accessibilityservice/GestureDescription"}
+ expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
+ api_versions_trimmer.filter_method_tag(method, classes_to_remove)
+ self.assertEqual(expected, method.get("name"))
+
+ def test_filter_method_signature_with_L_in_method(self):
+ xml = """
+ <method name="dispatchLeftGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
+ """
+ method = ET.fromstring(xml)
+ classes_to_remove = {"android/accessibilityservice/GestureDescription"}
+ expected = "dispatchLeftGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
+ api_versions_trimmer.filter_method_tag(method, classes_to_remove)
+ self.assertEqual(expected, method.get("name"))
+
+ def test_filter_method_signature_with_L_in_class(self):
+ xml = """
+ <method name="dispatchGesture(Landroid/accessibilityservice/LeftGestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
+ """
+ method = ET.fromstring(xml)
+ classes_to_remove = {"android/accessibilityservice/LeftGestureDescription"}
+ expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
+ api_versions_trimmer.filter_method_tag(method, classes_to_remove)
+ self.assertEqual(expected, method.get("name"))
+
+ def test_filter_method_signature_with_inner_class(self):
+ xml = """
+ <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription$Inner;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z" since="24"/>
+ """
+ method = ET.fromstring(xml)
+ classes_to_remove = {"android/accessibilityservice/GestureDescription$Inner"}
+ expected = "dispatchGesture(Ljava/lang/Object;Landroid/accessibilityservice/AccessibilityService$GestureResultCallback;Landroid/os/Handler;)Z"
+ api_versions_trimmer.filter_method_tag(method, classes_to_remove)
+ self.assertEqual(expected, method.get("name"))
+
+ def _run_filter_db_test(self, database_str, expected):
+ """Performs the pattern of testing the filter_lint_database method.
+
+ Filters instances of the class "a/b/C" (hard-coded) from the database string
+ and compares the result with the expected result (performs formatting of
+ the xml of both inputs)
+
+ Args:
+ database_str: string, the contents of the lint database (api-versions.xml)
+ expected: string, the expected result after filtering the original
+ database
+ """
+ database = io.StringIO(database_str)
+ classes_to_remove = {"a/b/C"}
+ output = io.BytesIO()
+ api_versions_trimmer.filter_lint_database(
+ database,
+ classes_to_remove,
+ output
+ )
+ expected = pretty_print(expected)
+ res = pretty_print(output.getvalue().decode("utf-8"))
+ self.assertEqual(expected, res)
+
+ def test_filter_lint_database_updates_method_signature_params(self):
+ self._run_filter_db_test(
+ database_str="""
+ <api version="2">
+ <!-- will be removed -->
+ <class name="a/b/C" since="1">
+ <extends name="java/lang/Object"/>
+ </class>
+
+ <class name="a/b/E" since="1">
+ <!-- extends will be modified -->
+ <extends name="a/b/C"/>
+ <!-- first parameter will be modified -->
+ <method name="dispatchGesture(La/b/C;Landroid/os/Handler;)Z" since="24"/>
+ <!-- second should remain untouched -->
+ <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
+sultCallback;Landroid/os/Handler;)Z" since="24"/>
+ </class>
+ </api>
+ """,
+ expected="""
+ <api version="2">
+ <class name="a/b/E" since="1">
+ <extends name="java/lang/Object"/>
+ <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/>
+ <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
+sultCallback;Landroid/os/Handler;)Z" since="24"/>
+ </class>
+ </api>
+ """)
+
+ def test_filter_lint_database_updates_method_signature_return(self):
+ self._run_filter_db_test(
+ database_str="""
+ <api version="2">
+ <!-- will be removed -->
+ <class name="a/b/C" since="1">
+ <extends name="java/lang/Object"/>
+ </class>
+
+ <class name="a/b/E" since="1">
+ <!-- extends will be modified -->
+ <extends name="a/b/C"/>
+ <!-- return type should be changed -->
+ <method name="gestureIdToString(I)La/b/C;" since="24"/>
+ </class>
+ </api>
+ """,
+ expected="""
+ <api version="2">
+ <class name="a/b/E" since="1">
+
+ <extends name="java/lang/Object"/>
+
+ <method name="gestureIdToString(I)Ljava/lang/Object;" since="24"/>
+ </class>
+ </api>
+ """)
+
+ def test_filter_lint_database_removes_implements(self):
+ self._run_filter_db_test(
+ database_str="""
+ <api version="2">
+ <!-- will be removed -->
+ <class name="a/b/C" since="1">
+ <extends name="java/lang/Object"/>
+ </class>
+
+ <class name="a/b/D" since="1">
+ <extends name="java/lang/Object"/>
+ <implements name="a/b/C"/>
+ <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
+sultCallback;Landroid/os/Handler;)Z" since="24"/>
+ </class>
+ </api>
+ """,
+ expected="""
+ <api version="2">
+
+ <class name="a/b/D" since="1">
+ <extends name="java/lang/Object"/>
+ <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
+sultCallback;Landroid/os/Handler;)Z" since="24"/>
+ </class>
+ </api>
+ """)
+
+ def test_filter_lint_database_updates_extends(self):
+ self._run_filter_db_test(
+ database_str="""
+ <api version="2">
+ <!-- will be removed -->
+ <class name="a/b/C" since="1">
+ <extends name="java/lang/Object"/>
+ </class>
+
+ <class name="a/b/E" since="1">
+ <!-- extends will be modified -->
+ <extends name="a/b/C"/>
+ <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/>
+ <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
+sultCallback;Landroid/os/Handler;)Z" since="24"/>
+ </class>
+ </api>
+ """,
+ expected="""
+ <api version="2">
+ <class name="a/b/E" since="1">
+ <extends name="java/lang/Object"/>
+ <method name="dispatchGesture(Ljava/lang/Object;Landroid/os/Handler;)Z" since="24"/>
+ <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
+sultCallback;Landroid/os/Handler;)Z" since="24"/>
+ </class>
+ </api>
+ """)
+
+ def test_filter_lint_database_removes_class(self):
+ self._run_filter_db_test(
+ database_str="""
+ <api version="2">
+ <!-- will be removed -->
+ <class name="a/b/C" since="1">
+ <extends name="java/lang/Object"/>
+ </class>
+
+ <class name="a/b/D" since="1">
+ <extends name="java/lang/Object"/>
+ <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
+sultCallback;Landroid/os/Handler;)Z" since="24"/>
+ </class>
+ </api>
+ """,
+ expected="""
+ <api version="2">
+
+ <class name="a/b/D" since="1">
+ <extends name="java/lang/Object"/>
+ <method name="dispatchGesture(Landroid/accessibilityservice/GestureDescription;Landroid/accessibilityservice/AccessibilityService$GestureRe
+sultCallback;Landroid/os/Handler;)Z" since="24"/>
+ </class>
+ </api>
+ """)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp
index 0eff83c..a157517 100644
--- a/cmds/app_process/Android.bp
+++ b/cmds/app_process/Android.bp
@@ -29,7 +29,16 @@
},
},
- ldflags: ["-Wl,--export-dynamic"],
+ // Symbols exported from the executable in .dynsym interpose symbols in every
+ // linker namespace, including an app's classloader namespace. Provide this
+ // version script to prevent unwanted interposition.
+ //
+ // By default, the static linker doesn't export most of an executable's symbols,
+ // but it will export a symbol that appears to override a symbol in a needed DSO.
+ // This commonly happens with C++ vaguely-linked entities, such as template
+ // functions or type_info variables. Hence, a version script is needed even for
+ // an executable.
+ version_script: "version-script.txt",
shared_libs: [
"libandroid_runtime",
diff --git a/cmds/app_process/version-script.txt b/cmds/app_process/version-script.txt
new file mode 100644
index 0000000..a98066a
--- /dev/null
+++ b/cmds/app_process/version-script.txt
@@ -0,0 +1,4 @@
+{
+ local:
+ *;
+};
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 80664ed..a4ac61b 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1769,6 +1769,7 @@
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createProfileForUser(@Nullable String, @NonNull String, int, int, @Nullable String[]);
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createRestrictedProfile(@Nullable String);
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createUser(@Nullable String, @NonNull String, int);
+ method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.Set<java.lang.String> getPreInstallableSystemPackages(@NonNull String);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public String getUserType();
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 1ce598b..8e1f263 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -419,7 +419,7 @@
private IBinder mLaunchCookie;
private IRemoteTransition mRemoteTransition;
private boolean mOverrideTaskTransition;
- private int mSplashScreenThemeResId;
+ private String mSplashScreenThemeResName;
@SplashScreen.SplashScreenStyle
private int mSplashScreenStyle;
private boolean mRemoveWithTaskOrganizer;
@@ -1174,7 +1174,7 @@
mRemoteTransition = IRemoteTransition.Stub.asInterface(opts.getBinder(
KEY_REMOTE_TRANSITION));
mOverrideTaskTransition = opts.getBoolean(KEY_OVERRIDE_TASK_TRANSITION);
- mSplashScreenThemeResId = opts.getInt(KEY_SPLASH_SCREEN_THEME);
+ mSplashScreenThemeResName = opts.getString(KEY_SPLASH_SCREEN_THEME);
mRemoveWithTaskOrganizer = opts.getBoolean(KEY_REMOVE_WITH_TASK_ORGANIZER);
mLaunchedFromBubble = opts.getBoolean(KEY_LAUNCHED_FROM_BUBBLE);
mTransientLaunch = opts.getBoolean(KEY_TRANSIENT_LAUNCH);
@@ -1368,8 +1368,9 @@
* Gets whether the activity want to be launched as other theme for the splash screen.
* @hide
*/
- public int getSplashScreenThemeResId() {
- return mSplashScreenThemeResId;
+ @Nullable
+ public String getSplashScreenThemeResName() {
+ return mSplashScreenThemeResName;
}
/**
@@ -1945,8 +1946,8 @@
if (mOverrideTaskTransition) {
b.putBoolean(KEY_OVERRIDE_TASK_TRANSITION, mOverrideTaskTransition);
}
- if (mSplashScreenThemeResId != 0) {
- b.putInt(KEY_SPLASH_SCREEN_THEME, mSplashScreenThemeResId);
+ if (mSplashScreenThemeResName != null && !mSplashScreenThemeResName.isEmpty()) {
+ b.putString(KEY_SPLASH_SCREEN_THEME, mSplashScreenThemeResName);
}
if (mRemoveWithTaskOrganizer) {
b.putBoolean(KEY_REMOVE_WITH_TASK_ORGANIZER, mRemoveWithTaskOrganizer);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 02ab314..4182ac3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -36,7 +36,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
import android.app.backup.BackupAgent;
@@ -62,7 +61,6 @@
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Context.CreatePackageOptions;
import android.content.IContentProvider;
import android.content.IIntentReceiver;
import android.content.Intent;
@@ -73,7 +71,6 @@
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.ApplicationInfoFlags;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionInfo;
@@ -221,6 +218,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.net.InetAddress;
@@ -231,6 +230,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -419,6 +419,16 @@
@GuardedBy("mResourcesManager")
@UnsupportedAppUsage
final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages = new ArrayMap<>();
+
+ @GuardedBy("mResourcesManager")
+ private final ArrayMap<List<String>, WeakReference<LoadedApk>> mPackageNonAMS =
+ new ArrayMap<>();
+ @GuardedBy("mResourcesManager")
+ private final ArrayMap<List<String>, WeakReference<LoadedApk>> mResourcePackagesNonAMS =
+ new ArrayMap<>();
+ @GuardedBy("mResourcesManager")
+ private final ReferenceQueue<LoadedApk> mPackageRefQueue = new ReferenceQueue<>();
+
@GuardedBy("mResourcesManager")
final ArrayList<ActivityClientRecord> mRelaunchingActivities = new ArrayList<>();
@GuardedBy("mResourcesManager")
@@ -447,7 +457,7 @@
@GuardedBy("mLock")
ContentProviderHolder mHolder; // Temp holder to be used between notifier and waiter
- Object mLock; // The lock to be used to get notified when the provider is ready
+ final Object mLock; // The lock to be used to get notified when the provider is ready
public ProviderKey(String authority, int userId) {
this.authority = authority;
@@ -1172,6 +1182,7 @@
}
public void scheduleApplicationInfoChanged(ApplicationInfo ai) {
+ mResourcesManager.updatePendingAppInfoUpdates(ai);
mH.removeMessages(H.APPLICATION_INFO_CHANGED, ai);
sendMessage(H.APPLICATION_INFO_CHANGED, ai);
}
@@ -1827,11 +1838,14 @@
@Override
public void notifyContentProviderPublishStatus(@NonNull ContentProviderHolder holder,
- @NonNull String auth, int userId, boolean published) {
- final ProviderKey key = getGetProviderKey(auth, userId);
- synchronized (key.mLock) {
- key.mHolder = holder;
- key.mLock.notifyAll();
+ @NonNull String authorities, int userId, boolean published) {
+ final String auths[] = authorities.split(";");
+ for (String auth: auths) {
+ final ProviderKey key = getGetProviderKey(auth, userId);
+ synchronized (key.mLock) {
+ key.mHolder = holder;
+ key.mLock.notifyAll();
+ }
}
}
@@ -2371,72 +2385,281 @@
return mH;
}
+ /**
+ * If {@code retainReferences} is false, prunes all {@link LoadedApk} representing any of the
+ * specified packages from the package caches.
+ *
+ * @return whether the cache contains a loaded apk representing any of the specified packages
+ */
+ private boolean clearCachedApks() {
+ synchronized (mResourcesManager) {
+ Reference<? extends LoadedApk> enqueuedRef = mPackageRefQueue.poll();
+ if (enqueuedRef == null) {
+ return false;
+ }
+
+ final HashSet<Reference<? extends LoadedApk>> deadReferences = new HashSet<>();
+ for (; enqueuedRef != null; enqueuedRef = mPackageRefQueue.poll()) {
+ deadReferences.add(enqueuedRef);
+ }
+
+ return cleanWeakMapValues(mPackages, deadReferences)
+ || cleanWeakMapValues(mResourcePackages, deadReferences)
+ || cleanWeakMapValues(mPackageNonAMS, deadReferences)
+ || cleanWeakMapValues(mResourcePackages, deadReferences);
+ }
+ }
+
+ private static <K> boolean cleanWeakMapValues(ArrayMap<K, WeakReference<LoadedApk>> map,
+ HashSet<Reference<? extends LoadedApk>> deadReferences) {
+ boolean hasPkgInfo = false;
+ for (int i = map.size() - 1; i >= 0; i--) {
+ if (deadReferences.contains(map.valueAt(i))) {
+ map.removeAt(i);
+ hasPkgInfo = true;
+ }
+ }
+ return hasPkgInfo;
+ }
+
+ /**
+ * Retrieves the previously cached {@link LoadedApk} that was created/updated with the most
+ * recent {@link ApplicationInfo} sent from the activity manager service.
+ */
+ @Nullable
+ private LoadedApk peekLatestCachedApkFromAMS(@NonNull String packageName, boolean includeCode) {
+ synchronized (mResourcesManager) {
+ WeakReference<LoadedApk> ref;
+ if (includeCode) {
+ return ((ref = mPackages.get(packageName)) != null) ? ref.get() : null;
+ } else {
+ return ((ref = mResourcePackages.get(packageName)) != null) ? ref.get() : null;
+ }
+ }
+ }
+
+ /**
+ * Updates the previously cached {@link LoadedApk} that was created/updated using an
+ * {@link ApplicationInfo} sent from activity manager service.
+ *
+ * If {@code appInfo} is null, the most up-to-date {@link ApplicationInfo} will be fetched and
+ * used to update the cached package; otherwise, the specified app info will be used.
+ */
+ private boolean updateLatestCachedApkFromAMS(@NonNull String packageName,
+ @Nullable ApplicationInfo appInfo, boolean updateActivityRecords) {
+ final LoadedApk[] loadedPackages = new LoadedApk[]{
+ peekLatestCachedApkFromAMS(packageName, true),
+ peekLatestCachedApkFromAMS(packageName, false)
+ };
+
+ try {
+ if (appInfo == null) {
+ appInfo = sPackageManager.getApplicationInfo(
+ packageName, PackageManager.GET_SHARED_LIBRARY_FILES,
+ UserHandle.myUserId());
+ }
+ } catch (RemoteException e) {
+ Slog.v(TAG, "Failed to get most recent app info for '" + packageName + "'", e);
+ return false;
+ }
+
+ boolean hasPackage = false;
+ final String[] oldResDirs = new String[loadedPackages.length];
+ for (int i = loadedPackages.length - 1; i >= 0; i--) {
+ final LoadedApk loadedPackage = loadedPackages[i];
+ if (loadedPackage == null) {
+ continue;
+ }
+
+ // If the package is being updated, yet it still has a valid LoadedApk object, the
+ // package was updated with PACKAGE_REMOVED_DONT_KILL. Adjust it's internal references
+ // to the application info and resources.
+ hasPackage = true;
+ if (updateActivityRecords && mActivities.size() > 0) {
+ for (ActivityClientRecord ar : mActivities.values()) {
+ if (ar.activityInfo.applicationInfo.packageName.equals(packageName)) {
+ ar.activityInfo.applicationInfo = appInfo;
+ ar.packageInfo = loadedPackage;
+ }
+ }
+ }
+
+ updateLoadedApk(loadedPackage, appInfo);
+ oldResDirs[i] = loadedPackage.getResDir();
+ }
+ if (hasPackage) {
+ synchronized (mResourcesManager) {
+ mResourcesManager.applyNewResourceDirs(appInfo, oldResDirs);
+ }
+ }
+ return hasPackage;
+ }
+
+ private static List<String> makeNonAMSKey(@NonNull ApplicationInfo appInfo) {
+ final List<String> paths = new ArrayList<>();
+ paths.add(appInfo.sourceDir);
+ if (appInfo.resourceDirs != null) {
+ for (String path : appInfo.resourceDirs) {
+ paths.add(path);
+ }
+ }
+ return paths;
+ }
+
+ /**
+ * Retrieves the previously cached {@link LoadedApk}.
+ *
+ * If {@code isAppInfoFromAMS} is true, then {@code appInfo} will be used to update the paths
+ * of the previously cached {@link LoadedApk} that was created/updated using an
+ * {@link ApplicationInfo} sent from activity manager service.
+ */
+ @Nullable
+ private LoadedApk retrieveCachedApk(@NonNull ApplicationInfo appInfo, boolean includeCode,
+ boolean isAppInfoFromAMS) {
+ if (UserHandle.myUserId() != UserHandle.getUserId(appInfo.uid)) {
+ // Caching not supported across users.
+ return null;
+ }
+
+ if (isAppInfoFromAMS) {
+ LoadedApk loadedPackage = peekLatestCachedApkFromAMS(appInfo.packageName, includeCode);
+ if (loadedPackage != null) {
+ updateLoadedApk(loadedPackage, appInfo);
+ }
+ return loadedPackage;
+ }
+
+ synchronized (mResourcesManager) {
+ WeakReference<LoadedApk> ref;
+ if (includeCode) {
+ return ((ref = mPackageNonAMS.get(makeNonAMSKey(appInfo))) != null)
+ ? ref.get() : null;
+ } else {
+ return ((ref = mResourcePackagesNonAMS.get(makeNonAMSKey(appInfo))) != null)
+ ? ref.get() : null;
+ }
+ }
+ }
+
+ private static boolean isLoadedApkResourceDirsUpToDate(LoadedApk loadedApk,
+ ApplicationInfo appInfo) {
+ boolean baseDirsUpToDate = loadedApk.getResDir().equals(appInfo.sourceDir);
+ boolean resourceDirsUpToDate = Arrays.equals(
+ ArrayUtils.defeatNullable(appInfo.resourceDirs),
+ ArrayUtils.defeatNullable(loadedApk.getOverlayDirs()));
+ boolean overlayPathsUpToDate = Arrays.equals(
+ ArrayUtils.defeatNullable(appInfo.overlayPaths),
+ ArrayUtils.defeatNullable(loadedApk.getOverlayPaths()));
+
+ return (loadedApk.mResources == null || loadedApk.mResources.getAssets().isUpToDate())
+ && baseDirsUpToDate && resourceDirsUpToDate && overlayPathsUpToDate;
+ }
+
+ private void updateLoadedApk(@NonNull LoadedApk loadedPackage,
+ @NonNull ApplicationInfo appInfo) {
+ if (isLoadedApkResourceDirsUpToDate(loadedPackage, appInfo)) {
+ return;
+ }
+
+ final List<String> oldPaths = new ArrayList<>();
+ LoadedApk.makePaths(this, appInfo, oldPaths);
+ loadedPackage.updateApplicationInfo(appInfo, oldPaths);
+ }
+
+ @Nullable
+ private LoadedApk createLoadedPackage(@NonNull ApplicationInfo appInfo,
+ @Nullable CompatibilityInfo compatInfo, @Nullable ClassLoader baseLoader,
+ boolean securityViolation, boolean includeCode, boolean registerPackage,
+ boolean isAppInfoFromAMS) {
+ final LoadedApk packageInfo =
+ new LoadedApk(this, appInfo, compatInfo, baseLoader,
+ securityViolation, includeCode
+ && (appInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
+
+ if (mSystemThread && "android".equals(appInfo.packageName)) {
+ packageInfo.installSystemApplicationInfo(appInfo,
+ getSystemContext().mPackageInfo.getClassLoader());
+ }
+
+ if (UserHandle.myUserId() != UserHandle.getUserId(appInfo.uid)) {
+ // Caching not supported across users
+ return packageInfo;
+ }
+
+ synchronized (mResourcesManager) {
+ if (includeCode) {
+ if (isAppInfoFromAMS) {
+ mPackages.put(appInfo.packageName,
+ new WeakReference<>(packageInfo, mPackageRefQueue));
+ } else {
+ mPackageNonAMS.put(makeNonAMSKey(appInfo),
+ new WeakReference<>(packageInfo, mPackageRefQueue));
+ }
+ } else {
+ if (isAppInfoFromAMS) {
+ mResourcePackages.put(appInfo.packageName,
+ new WeakReference<>(packageInfo, mPackageRefQueue));
+ } else {
+ mResourcePackagesNonAMS.put(makeNonAMSKey(appInfo),
+ new WeakReference<>(packageInfo, mPackageRefQueue));
+ }
+ }
+ return packageInfo;
+ }
+ }
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
- @CreatePackageOptions int flags) {
+ int flags) {
return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId());
}
public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
- @CreatePackageOptions int flags, @UserIdInt int userId) {
- return getPackageInfo(packageName, compatInfo, flags, userId, 0 /* packageFlags */);
- }
-
- public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
- @CreatePackageOptions int flags, @UserIdInt int userId,
- @ApplicationInfoFlags int packageFlags) {
- final boolean differentUser = (UserHandle.myUserId() != userId);
- ApplicationInfo ai = PackageManager.getApplicationInfoAsUserCached(
+ int flags, int userId) {
+ final ApplicationInfo ai = PackageManager.getApplicationInfoAsUserCached(
packageName,
- packageFlags | PackageManager.GET_SHARED_LIBRARY_FILES
+ PackageManager.GET_SHARED_LIBRARY_FILES
| PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
(userId < 0) ? UserHandle.myUserId() : userId);
- synchronized (mResourcesManager) {
- WeakReference<LoadedApk> ref;
- if (differentUser) {
- // Caching not supported across users
- ref = null;
- } else if ((flags & Context.CONTEXT_INCLUDE_CODE) != 0) {
- ref = mPackages.get(packageName);
- } else {
- ref = mResourcePackages.get(packageName);
- }
-
- LoadedApk packageInfo = ref != null ? ref.get() : null;
- if (ai != null && packageInfo != null) {
- if (!isLoadedApkResourceDirsUpToDate(packageInfo, ai)) {
- List<String> oldPaths = new ArrayList<>();
- LoadedApk.makePaths(this, ai, oldPaths);
- packageInfo.updateApplicationInfo(ai, oldPaths);
- }
-
- if (packageInfo.isSecurityViolation()
- && (flags&Context.CONTEXT_IGNORE_SECURITY) == 0) {
- throw new SecurityException(
- "Requesting code from " + packageName
- + " to be run in process "
- + mBoundApplication.processName
- + "/" + mBoundApplication.appInfo.uid);
- }
- return packageInfo;
- }
- }
-
+ LoadedApk packageInfo = null;
if (ai != null) {
- return getPackageInfo(ai, compatInfo, flags);
+ packageInfo = retrieveCachedApk(ai,
+ (flags & Context.CONTEXT_INCLUDE_CODE) != 0,
+ false);
}
- return null;
+ if (packageInfo != null) {
+ if (packageInfo.isSecurityViolation()
+ && (flags & Context.CONTEXT_IGNORE_SECURITY) == 0) {
+ throw new SecurityException(
+ "Requesting code from " + packageName
+ + " to be run in process "
+ + mBoundApplication.processName
+ + "/" + mBoundApplication.appInfo.uid);
+ }
+ return packageInfo;
+ }
+
+ return ai == null ? null : getPackageInfo(ai, compatInfo, flags, false);
}
+ /**
+ * @deprecated Use {@link #getPackageInfo(ApplicationInfo, CompatibilityInfo, int, boolean)}
+ * instead.
+ */
+ @Deprecated
@UnsupportedAppUsage(trackingBug = 171933273)
public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo,
- @CreatePackageOptions int flags) {
+ int flags) {
+ return getPackageInfo(ai, compatInfo, flags, true);
+ }
+
+ public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo,
+ @Context.CreatePackageOptions int flags, boolean isAppInfoFromAMS) {
boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
boolean securityViolation = includeCode && ai.uid != 0
- && ai.uid != Process.SYSTEM_UID && (mBoundApplication != null
- ? !UserHandle.isSameApp(ai.uid, mBoundApplication.appInfo.uid)
- : true);
+ && ai.uid != Process.SYSTEM_UID && (mBoundApplication == null
+ || !UserHandle.isSameApp(ai.uid, mBoundApplication.appInfo.uid));
boolean registerPackage = includeCode && (flags&Context.CONTEXT_REGISTER_PACKAGE) != 0;
if ((flags&(Context.CONTEXT_INCLUDE_CODE
|Context.CONTEXT_IGNORE_SECURITY))
@@ -2446,107 +2669,47 @@
+ " (with uid " + ai.uid + ")";
if (mBoundApplication != null) {
msg = msg + " to be run in process "
- + mBoundApplication.processName + " (with uid "
- + mBoundApplication.appInfo.uid + ")";
+ + mBoundApplication.processName + " (with uid "
+ + mBoundApplication.appInfo.uid + ")";
}
throw new SecurityException(msg);
}
}
return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode,
- registerPackage);
+ registerPackage, isAppInfoFromAMS);
}
@Override
@UnsupportedAppUsage
public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
CompatibilityInfo compatInfo) {
- return getPackageInfo(ai, compatInfo, null, false, true, false);
+ return getPackageInfo(ai, compatInfo, null, false, true, false, true);
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) {
synchronized (mResourcesManager) {
- WeakReference<LoadedApk> ref;
- if (includeCode) {
- ref = mPackages.get(packageName);
- } else {
- ref = mResourcePackages.get(packageName);
- }
- return ref != null ? ref.get() : null;
+ return peekLatestCachedApkFromAMS(packageName, includeCode);
}
}
private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
- boolean registerPackage) {
- final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
- synchronized (mResourcesManager) {
- WeakReference<LoadedApk> ref;
- if (differentUser) {
- // Caching not supported across users
- ref = null;
- } else if (includeCode) {
- ref = mPackages.get(aInfo.packageName);
- } else {
- ref = mResourcePackages.get(aInfo.packageName);
- }
-
- LoadedApk packageInfo = ref != null ? ref.get() : null;
-
- if (packageInfo != null) {
- if (!isLoadedApkResourceDirsUpToDate(packageInfo, aInfo)) {
- List<String> oldPaths = new ArrayList<>();
- LoadedApk.makePaths(this, aInfo, oldPaths);
- packageInfo.updateApplicationInfo(aInfo, oldPaths);
- }
-
- return packageInfo;
- }
-
- if (localLOGV) {
- Slog.v(TAG, (includeCode ? "Loading code package "
- : "Loading resource-only package ") + aInfo.packageName
- + " (in " + (mBoundApplication != null
- ? mBoundApplication.processName : null)
- + ")");
- }
-
- packageInfo =
- new LoadedApk(this, aInfo, compatInfo, baseLoader,
- securityViolation, includeCode
- && (aInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
-
- if (mSystemThread && "android".equals(aInfo.packageName)) {
- packageInfo.installSystemApplicationInfo(aInfo,
- getSystemContext().mPackageInfo.getClassLoader());
- }
-
- if (differentUser) {
- // Caching not supported across users
- } else if (includeCode) {
- mPackages.put(aInfo.packageName,
- new WeakReference<LoadedApk>(packageInfo));
- } else {
- mResourcePackages.put(aInfo.packageName,
- new WeakReference<LoadedApk>(packageInfo));
- }
-
+ boolean registerPackage, boolean isAppInfoFromAMS) {
+ LoadedApk packageInfo = retrieveCachedApk(aInfo, includeCode,
+ isAppInfoFromAMS);
+ if (packageInfo != null) {
return packageInfo;
}
- }
-
- private static boolean isLoadedApkResourceDirsUpToDate(LoadedApk loadedApk,
- ApplicationInfo appInfo) {
- Resources packageResources = loadedApk.mResources;
- boolean resourceDirsUpToDate = Arrays.equals(
- ArrayUtils.defeatNullable(appInfo.resourceDirs),
- ArrayUtils.defeatNullable(loadedApk.getOverlayDirs()));
- boolean overlayPathsUpToDate = Arrays.equals(
- ArrayUtils.defeatNullable(appInfo.overlayPaths),
- ArrayUtils.defeatNullable(loadedApk.getOverlayPaths()));
-
- return (packageResources == null || packageResources.getAssets().isUpToDate())
- && resourceDirsUpToDate && overlayPathsUpToDate;
+ if (localLOGV) {
+ Slog.v(TAG, (includeCode ? "Loading code package "
+ : "Loading resource-only package ") + aInfo.packageName
+ + " (in " + (mBoundApplication != null
+ ? mBoundApplication.processName : null)
+ + ")");
+ }
+ return createLoadedPackage(aInfo, compatInfo, baseLoader,
+ securityViolation, includeCode, registerPackage, isAppInfoFromAMS);
}
@UnsupportedAppUsage
@@ -3508,7 +3671,7 @@
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
- Context.CONTEXT_INCLUDE_CODE);
+ Context.CONTEXT_INCLUDE_CODE, true);
}
ComponentName component = r.intent.getComponent();
@@ -5991,40 +6154,10 @@
@VisibleForTesting(visibility = PACKAGE)
public void handleApplicationInfoChanged(@NonNull final ApplicationInfo ai) {
- // Updates triggered by package installation go through a package update
- // receiver. Here we try to capture ApplicationInfo changes that are
- // caused by other sources, such as overlays. That means we want to be as conservative
- // about code changes as possible. Take the diff of the old ApplicationInfo and the new
- // to see if anything needs to change.
- LoadedApk apk;
- LoadedApk resApk;
- // Update all affected loaded packages with new package information
- synchronized (mResourcesManager) {
- WeakReference<LoadedApk> ref = mPackages.get(ai.packageName);
- apk = ref != null ? ref.get() : null;
- ref = mResourcePackages.get(ai.packageName);
- resApk = ref != null ? ref.get() : null;
- }
-
- final String[] oldResDirs = new String[2];
-
- if (apk != null) {
- oldResDirs[0] = apk.getResDir();
- final ArrayList<String> oldPaths = new ArrayList<>();
- LoadedApk.makePaths(this, apk.getApplicationInfo(), oldPaths);
- apk.updateApplicationInfo(ai, oldPaths);
- }
- if (resApk != null) {
- oldResDirs[1] = resApk.getResDir();
- final ArrayList<String> oldPaths = new ArrayList<>();
- LoadedApk.makePaths(this, resApk.getApplicationInfo(), oldPaths);
- resApk.updateApplicationInfo(ai, oldPaths);
- }
-
- synchronized (mResourcesManager) {
- // Update all affected Resources objects to use new ResourcesImpl
- mResourcesManager.applyNewResourceDirs(ai, oldResDirs);
- }
+ // Updates triggered by package installation go through a package update receiver. Here we
+ // try to capture ApplicationInfo changes that are caused by other sources, such as
+ // overlays. That means we want to be as conservative about code changes as possible.
+ updateLatestCachedApkFromAMS(ai.packageName, ai, false);
}
/**
@@ -6201,29 +6334,7 @@
case ApplicationThreadConstants.PACKAGE_REMOVED:
case ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL:
{
- final boolean killApp = cmd == ApplicationThreadConstants.PACKAGE_REMOVED;
- if (packages == null) {
- break;
- }
- synchronized (mResourcesManager) {
- for (int i = packages.length - 1; i >= 0; i--) {
- if (!hasPkgInfo) {
- WeakReference<LoadedApk> ref = mPackages.get(packages[i]);
- if (ref != null && ref.get() != null) {
- hasPkgInfo = true;
- } else {
- ref = mResourcePackages.get(packages[i]);
- if (ref != null && ref.get() != null) {
- hasPkgInfo = true;
- }
- }
- }
- if (killApp) {
- mPackages.remove(packages[i]);
- mResourcePackages.remove(packages[i]);
- }
- }
- }
+ hasPkgInfo = clearCachedApks();
break;
}
case ApplicationThreadConstants.PACKAGE_REPLACED:
@@ -6231,68 +6342,19 @@
if (packages == null) {
break;
}
-
- List<String> packagesHandled = new ArrayList<>();
-
- synchronized (mResourcesManager) {
- for (int i = packages.length - 1; i >= 0; i--) {
- String packageName = packages[i];
- WeakReference<LoadedApk> ref = mPackages.get(packageName);
- LoadedApk pkgInfo = ref != null ? ref.get() : null;
- if (pkgInfo != null) {
- hasPkgInfo = true;
- } else {
- ref = mResourcePackages.get(packageName);
- pkgInfo = ref != null ? ref.get() : null;
- if (pkgInfo != null) {
- hasPkgInfo = true;
- }
- }
- // If the package is being replaced, yet it still has a valid
- // LoadedApk object, the package was updated with _DONT_KILL.
- // Adjust it's internal references to the application info and
- // resources.
- if (pkgInfo != null) {
- packagesHandled.add(packageName);
- try {
- final ApplicationInfo aInfo =
- sPackageManager.getApplicationInfo(
- packageName,
- PackageManager.GET_SHARED_LIBRARY_FILES,
- UserHandle.myUserId());
-
- if (mActivities.size() > 0) {
- for (ActivityClientRecord ar : mActivities.values()) {
- if (ar.activityInfo.applicationInfo.packageName
- .equals(packageName)) {
- ar.activityInfo.applicationInfo = aInfo;
- ar.packageInfo = pkgInfo;
- }
- }
- }
-
- final String[] oldResDirs = { pkgInfo.getResDir() };
-
- final ArrayList<String> oldPaths = new ArrayList<>();
- LoadedApk.makePaths(this, pkgInfo.getApplicationInfo(), oldPaths);
- pkgInfo.updateApplicationInfo(aInfo, oldPaths);
-
- synchronized (mResourcesManager) {
- // Update affected Resources objects to use new ResourcesImpl
- mResourcesManager.applyNewResourceDirs(aInfo, oldResDirs);
- }
- } catch (RemoteException e) {
- }
- }
+ final List<String> packagesHandled = new ArrayList<>();
+ for (int i = packages.length - 1; i >= 0; i--) {
+ final String packageName = packages[i];
+ if (updateLatestCachedApkFromAMS(packageName, null, true)) {
+ hasPkgInfo = true;
+ packagesHandled.add(packageName);
}
}
-
try {
getPackageManager().notifyPackagesReplacedReceived(
packagesHandled.toArray(new String[0]));
} catch (RemoteException ignored) {
}
-
break;
}
}
@@ -6851,7 +6913,7 @@
ii.copyTo(instrApp);
instrApp.initForUser(UserHandle.myUserId());
final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
- appContext.getClassLoader(), false, true, false);
+ appContext.getClassLoader(), false, true, false, true);
// The test context's op package name == the target app's op package name, because
// the app ops manager checks the op package name against the real calling UID,
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 96d59b8..9bd6c750f 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -4804,6 +4804,16 @@
public static final int HISTORY_FLAG_DISCRETE = 1 << 1;
/**
+ * Flag for querying app op history: assemble attribution chains, and attach the last visible
+ * node in the chain to the start as a proxy info. This only applies to discrete accesses.
+ *
+ * TODO 191512294: Add to @SystemApi
+ *
+ * @hide
+ */
+ public static final int HISTORY_FLAG_GET_ATTRIBUTION_CHAINS = 1 << 2;
+
+ /**
* Flag for querying app op history: get all types of historical access information.
*
* @see #getHistoricalOps(HistoricalOpsRequest, Executor, Consumer)
@@ -4819,7 +4829,8 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "HISTORY_FLAG_" }, value = {
HISTORY_FLAG_AGGREGATE,
- HISTORY_FLAG_DISCRETE
+ HISTORY_FLAG_DISCRETE,
+ HISTORY_FLAG_GET_ATTRIBUTION_CHAINS
})
public @interface OpHistoryFlags {}
@@ -5037,7 +5048,8 @@
* @return This builder.
*/
public @NonNull Builder setHistoryFlags(@OpHistoryFlags int flags) {
- Preconditions.checkFlagsArgument(flags, HISTORY_FLAGS_ALL);
+ Preconditions.checkFlagsArgument(flags,
+ HISTORY_FLAGS_ALL | HISTORY_FLAG_GET_ATTRIBUTION_CHAINS);
mHistoryFlags = flags;
return this;
}
@@ -5290,8 +5302,17 @@
@Nullable String attributionTag, @UidState int uidState, @OpFlags int opFlag,
long discreteAccessTime, long discreteAccessDuration) {
getOrCreateHistoricalUidOps(uid).addDiscreteAccess(opCode, packageName, attributionTag,
- uidState, opFlag, discreteAccessTime, discreteAccessDuration);
- };
+ uidState, opFlag, discreteAccessTime, discreteAccessDuration, null);
+ }
+
+ /** @hide */
+ public void addDiscreteAccess(int opCode, int uid, @NonNull String packageName,
+ @Nullable String attributionTag, @UidState int uidState, @OpFlags int opFlag,
+ long discreteAccessTime, long discreteAccessDuration,
+ @Nullable OpEventProxyInfo proxy) {
+ getOrCreateHistoricalUidOps(uid).addDiscreteAccess(opCode, packageName, attributionTag,
+ uidState, opFlag, discreteAccessTime, discreteAccessDuration, proxy);
+ }
/** @hide */
@@ -5623,9 +5644,10 @@
private void addDiscreteAccess(int opCode, @NonNull String packageName,
@Nullable String attributionTag, @UidState int uidState,
- @OpFlags int flag, long discreteAccessTime, long discreteAccessDuration) {
+ @OpFlags int flag, long discreteAccessTime, long discreteAccessDuration,
+ @Nullable OpEventProxyInfo proxy) {
getOrCreateHistoricalPackageOps(packageName).addDiscreteAccess(opCode, attributionTag,
- uidState, flag, discreteAccessTime, discreteAccessDuration);
+ uidState, flag, discreteAccessTime, discreteAccessDuration, proxy);
};
/**
@@ -5889,9 +5911,9 @@
private void addDiscreteAccess(int opCode, @Nullable String attributionTag,
@UidState int uidState, @OpFlags int flag, long discreteAccessTime,
- long discreteAccessDuration) {
+ long discreteAccessDuration, @Nullable OpEventProxyInfo proxy) {
getOrCreateAttributedHistoricalOps(attributionTag).addDiscreteAccess(opCode, uidState,
- flag, discreteAccessTime, discreteAccessDuration);
+ flag, discreteAccessTime, discreteAccessDuration, proxy);
}
/**
@@ -6212,9 +6234,10 @@
}
private void addDiscreteAccess(int opCode, @UidState int uidState, @OpFlags int flag,
- long discreteAccessTime, long discreteAccessDuration) {
+ long discreteAccessTime, long discreteAccessDuration,
+ @Nullable OpEventProxyInfo proxy) {
getOrCreateHistoricalOp(opCode).addDiscreteAccess(uidState,flag, discreteAccessTime,
- discreteAccessDuration);
+ discreteAccessDuration, proxy);
}
/**
@@ -6583,11 +6606,12 @@
}
private void addDiscreteAccess(@UidState int uidState, @OpFlags int flag,
- long discreteAccessTime, long discreteAccessDuration) {
+ long discreteAccessTime, long discreteAccessDuration,
+ @Nullable OpEventProxyInfo proxy) {
List<AttributedOpEntry> discreteAccesses = getOrCreateDiscreteAccesses();
LongSparseArray<NoteOpEvent> accessEvents = new LongSparseArray<>();
long key = makeKey(uidState, flag);
- NoteOpEvent note = new NoteOpEvent(discreteAccessTime, discreteAccessDuration, null);
+ NoteOpEvent note = new NoteOpEvent(discreteAccessTime, discreteAccessDuration, proxy);
accessEvents.append(key, note);
AttributedOpEntry access = new AttributedOpEntry(mOp, false, accessEvents, null);
int insertionPoint = discreteAccesses.size() - 1;
@@ -10022,6 +10046,8 @@
NoteOpEvent existingAccess = accessEvents.get(key);
if (existingAccess == null || existingAccess.getDuration() == -1) {
accessEvents.append(key, access);
+ } else if (existingAccess.mProxy == null && access.mProxy != null ) {
+ existingAccess.mProxy = access.mProxy;
}
}
if (reject != null) {
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index a757e32..7c85df8 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -16,9 +16,9 @@
package android.app;
-import android.app.AppOpsManager.AttributionFlags;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AppOpsManager.AttributionFlags;
import android.content.AttributionSource;
import android.os.IBinder;
import android.util.SparseArray;
@@ -29,6 +29,7 @@
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexFunction;
import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.QuintConsumer;
import com.android.internal.util.function.QuintFunction;
import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.UndecFunction;
@@ -155,6 +156,21 @@
SyncNotedAppOp> superImpl);
/**
+ * Allows overriding finish op.
+ *
+ * @param clientId The client state.
+ * @param code The op code to finish.
+ * @param uid The UID for which the op was noted.
+ * @param packageName The package for which it was noted. {@code null} for system package.
+ * @param attributionTag the attribution tag.
+ */
+ default void finishOperation(IBinder clientId, int code, int uid, String packageName,
+ String attributionTag,
+ @NonNull QuintConsumer<IBinder, Integer, Integer, String, String> superImpl) {
+ superImpl.accept(clientId, code, uid, packageName, attributionTag);
+ }
+
+ /**
* Allows overriding finish proxy op.
*
* @param code The op code to finish.
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index c3272c1..7a806bd 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -45,6 +45,7 @@
private long creationTime;
private ZenPolicy mZenPolicy;
private boolean mModified = false;
+ private String mPkg;
/**
* Creates an automatic zen rule.
@@ -123,6 +124,7 @@
creationTime = source.readLong();
mZenPolicy = source.readParcelable(null);
mModified = source.readInt() == ENABLED;
+ mPkg = source.readString();
}
/**
@@ -244,6 +246,20 @@
this.configurationActivity = componentName;
}
+ /**
+ * @hide
+ */
+ public void setPackageName(String pkgName) {
+ mPkg = pkgName;
+ }
+
+ /**
+ * @hide
+ */
+ public String getPackageName() {
+ return mPkg;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -265,6 +281,7 @@
dest.writeLong(creationTime);
dest.writeParcelable(mZenPolicy, 0);
dest.writeInt(mModified ? ENABLED : DISABLED);
+ dest.writeString(mPkg);
}
@Override
@@ -273,6 +290,7 @@
.append("enabled=").append(enabled)
.append(",name=").append(name)
.append(",interruptionFilter=").append(interruptionFilter)
+ .append(",pkg=").append(mPkg)
.append(",conditionId=").append(conditionId)
.append(",owner=").append(owner)
.append(",configActivity=").append(configurationActivity)
@@ -294,13 +312,14 @@
&& Objects.equals(other.owner, owner)
&& Objects.equals(other.mZenPolicy, mZenPolicy)
&& Objects.equals(other.configurationActivity, configurationActivity)
+ && Objects.equals(other.mPkg, mPkg)
&& other.creationTime == creationTime;
}
@Override
public int hashCode() {
return Objects.hash(enabled, name, interruptionFilter, conditionId, owner,
- configurationActivity, mZenPolicy, mModified, creationTime);
+ configurationActivity, mZenPolicy, mModified, creationTime, mPkg);
}
public static final @android.annotation.NonNull Parcelable.Creator<AutomaticZenRule> CREATOR
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 16b6ea5..d241968 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -48,7 +48,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.ApplicationInfoFlags;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.AssetManager;
import android.content.res.CompatResources;
@@ -2462,7 +2461,7 @@
public Context createApplicationContext(ApplicationInfo application, int flags)
throws NameNotFoundException {
LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
- flags | CONTEXT_REGISTER_PACKAGE);
+ flags | CONTEXT_REGISTER_PACKAGE, false);
if (pi != null) {
ContextImpl c = new ContextImpl(this, mMainThread, pi, ContextParams.EMPTY,
mAttributionSource.getAttributionTag(),
@@ -2494,13 +2493,6 @@
@Override
public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
throws NameNotFoundException {
- return createPackageContextAsUser(packageName, flags, user, 0 /* packageFlags */);
- }
-
- @Override
- public Context createPackageContextAsUser(
- @NonNull String packageName, @CreatePackageOptions int flags, @NonNull UserHandle user,
- @ApplicationInfoFlags int packageFlags) throws PackageManager.NameNotFoundException {
if (packageName.equals("system") || packageName.equals("android")) {
// The system resources are loaded in every application, so we can safely copy
// the context without reloading Resources.
@@ -2511,7 +2503,7 @@
}
LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
- flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier(), packageFlags);
+ flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
if (pi != null) {
ContextImpl c = new ContextImpl(this, mMainThread, pi, mParams,
mAttributionSource.getAttributionTag(),
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 4555c172..d6ff6d3 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -153,7 +153,7 @@
void performDirectAction(IBinder activityToken, String actionId,
in Bundle arguments, in RemoteCallback cancellationCallback,
in RemoteCallback resultCallback);
- void notifyContentProviderPublishStatus(in ContentProviderHolder holder, String auth,
+ void notifyContentProviderPublishStatus(in ContentProviderHolder holder, String authorities,
int userId, boolean published);
void instrumentWithoutRestart(in ComponentName instrumentationName,
in Bundle instrumentationArgs,
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index f33adb3..098492c 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -207,7 +207,7 @@
void setNotificationPolicyAccessGrantedForUser(String pkg, int userId, boolean granted);
AutomaticZenRule getAutomaticZenRule(String id);
List<ZenModeConfig.ZenRule> getZenRules();
- String addAutomaticZenRule(in AutomaticZenRule automaticZenRule);
+ String addAutomaticZenRule(in AutomaticZenRule automaticZenRule, String pkg);
boolean updateAutomaticZenRule(String id, in AutomaticZenRule automaticZenRule);
boolean removeAutomaticZenRule(String id);
boolean removeAutomaticZenRules(String packageName);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 9ed76c1..a2c9795 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -546,6 +546,10 @@
if (aInfo.sharedLibraryFiles != null) {
int index = 0;
for (String lib : aInfo.sharedLibraryFiles) {
+ // sharedLibraryFiles might contain native shared libraries that are not APK paths.
+ if (!lib.endsWith(".apk")) {
+ continue;
+ }
if (!outSeenPaths.contains(lib) && !outZipPaths.contains(lib)) {
outZipPaths.add(index, lib);
index++;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index ba0bc55..6454d20 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5606,14 +5606,24 @@
final boolean snoozeEnabled = !hideSnoozeButton
&& mContext.getContentResolver() != null
- && (Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1);
+ && isSnoozeSettingEnabled();
if (snoozeEnabled) {
big.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
RemoteViews.MARGIN_BOTTOM, 0);
}
}
+ private boolean isSnoozeSettingEnabled() {
+ try {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1;
+ } catch (SecurityException ex) {
+ // Most 3p apps can't access this snooze setting, so their NotificationListeners
+ // would be unable to create notification views if we propagated this exception.
+ return false;
+ }
+ }
+
/**
* Returns the actions that are not contextual.
*/
@@ -6334,11 +6344,10 @@
ApplicationInfo applicationInfo = n.extras.getParcelable(
EXTRA_BUILDER_APPLICATION_INFO);
Context builderContext;
- if (applicationInfo != null && applicationInfo.packageName != null) {
+ if (applicationInfo != null) {
try {
- builderContext = context.createPackageContextAsUser(applicationInfo.packageName,
- Context.CONTEXT_RESTRICTED,
- UserHandle.getUserHandleForUid(applicationInfo.uid));
+ builderContext = context.createApplicationContext(applicationInfo,
+ Context.CONTEXT_RESTRICTED);
} catch (NameNotFoundException e) {
Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
builderContext = context; // try with our context
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index da03a3d..ccf1edb 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1182,10 +1182,12 @@
List<ZenModeConfig.ZenRule> rules = service.getZenRules();
Map<String, AutomaticZenRule> ruleMap = new HashMap<>();
for (ZenModeConfig.ZenRule rule : rules) {
- ruleMap.put(rule.id, new AutomaticZenRule(rule.name, rule.component,
+ AutomaticZenRule azr = new AutomaticZenRule(rule.name, rule.component,
rule.configurationActivity, rule.conditionId, rule.zenPolicy,
zenModeToInterruptionFilter(rule.zenMode), rule.enabled,
- rule.creationTime));
+ rule.creationTime);
+ azr.setPackageName(rule.pkg);
+ ruleMap.put(rule.id, azr);
}
return ruleMap;
} catch (RemoteException e) {
@@ -1226,7 +1228,7 @@
public String addAutomaticZenRule(AutomaticZenRule automaticZenRule) {
INotificationManager service = getService();
try {
- return service.addAutomaticZenRule(automaticZenRule);
+ return service.addAutomaticZenRule(automaticZenRule, mContext.getPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index e68eb74..0136a35 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -365,7 +365,6 @@
}
if (Compatibility.isChangeEnabled(PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED)
- && !"com.google.android.apps.gcs".equals(packageName)
&& !flagImmutableSet && !flagMutableSet) {
String msg = packageName + ": Targeting S+ (version " + Build.VERSION_CODES.S
+ " and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE"
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 04a12af..6ca7dfb 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -34,6 +34,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
@@ -260,12 +261,19 @@
private static final HashMap<String, Integer> sCorks = new HashMap<>();
/**
+ * A map of cache keys that have been disabled in the local process. When a key is
+ * disabled locally, existing caches are disabled and the key is saved in this map.
+ * Future cache instances that use the same key will be disabled in their constructor.
+ */
+ @GuardedBy("sCorkLock")
+ private static final HashSet<String> sDisabledKeys = new HashSet<>();
+
+ /**
* Weakly references all cache objects in the current process, allowing us to iterate over
* them all for purposes like issuing debug dumps and reacting to memory pressure.
*/
@GuardedBy("sCorkLock")
- private static final WeakHashMap<PropertyInvalidatedCache, Void> sCaches =
- new WeakHashMap<>();
+ private static final WeakHashMap<PropertyInvalidatedCache, Void> sCaches = new WeakHashMap<>();
private final Object mLock = new Object();
@@ -348,6 +356,9 @@
};
synchronized (sCorkLock) {
sCaches.put(this, null);
+ if (sDisabledKeys.contains(mCacheName)) {
+ disableInstance();
+ }
}
}
@@ -372,6 +383,14 @@
protected abstract Result recompute(Query query);
/**
+ * Return true if the query should bypass the cache. The default behavior is to
+ * always use the cache but the method can be overridden for a specific class.
+ */
+ protected boolean bypass(Query query) {
+ return false;
+ }
+
+ /**
* Determines if a pair of responses are considered equal. Used to determine whether
* a cache is inadvertently returning stale results when VERIFY is set to true.
*/
@@ -414,7 +433,7 @@
/**
* Disable the use of this cache in this process.
*/
- public final void disableLocal() {
+ public final void disableInstance() {
synchronized (mLock) {
mDisabled = true;
clear();
@@ -422,6 +441,30 @@
}
/**
+ * Disable the local use of all caches with the same name. All currently registered caches
+ * using the key will be disabled now, and all future cache instances that use the key will be
+ * disabled in their constructor.
+ */
+ public static final void disableLocal(@NonNull String name) {
+ synchronized (sCorkLock) {
+ sDisabledKeys.add(name);
+ for (PropertyInvalidatedCache cache : sCaches.keySet()) {
+ if (name.equals(cache.mCacheName)) {
+ cache.disableInstance();
+ }
+ }
+ }
+ }
+
+ /**
+ * Disable this cache in the current process, and all other caches that use the same
+ * property.
+ */
+ public final void disableLocal() {
+ disableLocal(mCacheName);
+ }
+
+ /**
* Return whether the cache is disabled in this process.
*/
public final boolean isDisabledLocal() {
@@ -435,8 +478,8 @@
// Let access to mDisabled race: it's atomic anyway.
long currentNonce = (!isDisabledLocal()) ? getCurrentNonce() : NONCE_DISABLED;
for (;;) {
- if (currentNonce == NONCE_DISABLED || currentNonce == NONCE_UNSET ||
- currentNonce == NONCE_CORKED) {
+ if (currentNonce == NONCE_DISABLED || currentNonce == NONCE_UNSET
+ || currentNonce == NONCE_CORKED || bypass(query)) {
if (!mDisabled) {
// Do not bother collecting statistics if the cache is
// locally disabled.
@@ -875,6 +918,15 @@
}
/**
+ * Report the disabled status of this cache instance. The return value does not
+ * reflect status of the property key.
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public boolean getDisabledState() {
+ return isDisabledLocal();
+ }
+
+ /**
* Returns a list of caches alive at the current time.
*/
public static ArrayList<PropertyInvalidatedCache> getActiveCaches() {
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index f28c760..dfd1e2b 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -42,10 +42,10 @@
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.Pair;
import android.util.Slog;
import android.view.Display;
import android.view.DisplayAdjustments;
+import android.view.DisplayInfo;
import android.window.WindowContext;
import com.android.internal.annotations.VisibleForTesting;
@@ -56,7 +56,6 @@
import java.io.PrintWriter;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
-import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
@@ -99,6 +98,12 @@
private int mResDisplayId = DEFAULT_DISPLAY;
/**
+ * ApplicationInfo changes that need to be applied to Resources when the next configuration
+ * change occurs.
+ */
+ private ArrayList<ApplicationInfo> mPendingAppInfoUpdates;
+
+ /**
* A mapping of ResourceImpls and their configurations. These are heavy weight objects
* which should be reused as much as possible.
*/
@@ -251,12 +256,6 @@
new WeakHashMap<>();
/**
- * A cache of DisplayId, DisplayAdjustments to Display.
- */
- private final ArrayMap<Pair<Integer, DisplayAdjustments>, SoftReference<Display>>
- mAdjustedDisplays = new ArrayMap<>();
-
- /**
* Callback implementation for handling updates to Resources objects.
*/
private final UpdateHandler mUpdateCallbacks = new UpdateHandler();
@@ -331,10 +330,12 @@
*/
@VisibleForTesting
protected @NonNull DisplayMetrics getDisplayMetrics(int displayId, DisplayAdjustments da) {
- DisplayMetrics dm = new DisplayMetrics();
- final Display display = getAdjustedDisplay(displayId, da);
- if (display != null) {
- display.getMetrics(dm);
+ final DisplayManagerGlobal displayManagerGlobal = DisplayManagerGlobal.getInstance();
+ final DisplayMetrics dm = new DisplayMetrics();
+ final DisplayInfo displayInfo = displayManagerGlobal != null
+ ? displayManagerGlobal.getDisplayInfo(displayId) : null;
+ if (displayInfo != null) {
+ displayInfo.getAppMetrics(dm, da);
} else {
dm.setToDefaults();
}
@@ -376,45 +377,6 @@
/**
* Returns an adjusted {@link Display} object based on the inputs or null if display isn't
- * available. This method is only used within {@link ResourcesManager} to calculate display
- * metrics based on a set {@link DisplayAdjustments}. All other usages should instead call
- * {@link ResourcesManager#getAdjustedDisplay(int, Resources)}.
- *
- * @param displayId display Id.
- * @param displayAdjustments display adjustments.
- */
- private Display getAdjustedDisplay(final int displayId,
- @Nullable DisplayAdjustments displayAdjustments) {
- final DisplayAdjustments displayAdjustmentsCopy = (displayAdjustments != null)
- ? new DisplayAdjustments(displayAdjustments) : new DisplayAdjustments();
- final Pair<Integer, DisplayAdjustments> key =
- Pair.create(displayId, displayAdjustmentsCopy);
- SoftReference<Display> sd;
- synchronized (mLock) {
- sd = mAdjustedDisplays.get(key);
- }
- if (sd != null) {
- final Display display = sd.get();
- if (display != null) {
- return display;
- }
- }
- final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
- if (dm == null) {
- // may be null early in system startup
- return null;
- }
- final Display display = dm.getCompatibleDisplay(displayId, key.second);
- if (display != null) {
- synchronized (mLock) {
- mAdjustedDisplays.put(key, new SoftReference<>(display));
- }
- }
- return display;
- }
-
- /**
- * Returns an adjusted {@link Display} object based on the inputs or null if display isn't
* available.
*
* @param displayId display Id.
@@ -1032,7 +994,7 @@
* @param classLoader The classloader to use for the Resources object.
* If null, {@link ClassLoader#getSystemClassLoader()} is used.
* @return A Resources object that gets updated when
- * {@link #applyConfigurationToResourcesLocked(Configuration, CompatibilityInfo)}
+ * {@link #applyConfigurationToResources(Configuration, CompatibilityInfo)}
* is called.
*/
@Nullable
@@ -1159,8 +1121,8 @@
/**
* Updates an Activity's Resources object with overrideConfig. The Resources object
* that was previously returned by {@link #getResources(IBinder, String, String[], String[],
- * String[], Integer, Configuration, CompatibilityInfo, ClassLoader, List)} is still valid and
- * will have the updated configuration.
+ * String[], String[], Integer, Configuration, CompatibilityInfo, ClassLoader, List)} is still
+ * valid and will have the updated configuration.
*
* @param activityToken The Activity token.
* @param overrideConfig The configuration override to update.
@@ -1311,6 +1273,22 @@
return newKey;
}
+ public void updatePendingAppInfoUpdates(@NonNull ApplicationInfo appInfo) {
+ synchronized (mLock) {
+ if (mPendingAppInfoUpdates == null) {
+ mPendingAppInfoUpdates = new ArrayList<>();
+ }
+ // Clear previous app info changes for the package to prevent multiple ResourcesImpl
+ // recreations when only the last recreation will be used.
+ for (int i = mPendingAppInfoUpdates.size() - 1; i >= 0; i--) {
+ if (appInfo.sourceDir.equals(mPendingAppInfoUpdates.get(i).sourceDir)) {
+ mPendingAppInfoUpdates.remove(i);
+ }
+ }
+ mPendingAppInfoUpdates.add(appInfo);
+ }
+ }
+
public final boolean applyConfigurationToResources(@NonNull Configuration config,
@Nullable CompatibilityInfo compat) {
return applyConfigurationToResources(config, compat, null /* adjustments */);
@@ -1324,7 +1302,18 @@
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
"ResourcesManager#applyConfigurationToResources");
- if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
+ final boolean assetsUpdated = mPendingAppInfoUpdates != null
+ && config.assetsSeq > mResConfiguration.assetsSeq;
+ if (assetsUpdated) {
+ for (int i = 0, n = mPendingAppInfoUpdates.size(); i < n; i++) {
+ final ApplicationInfo appInfo = mPendingAppInfoUpdates.get(i);
+ applyNewResourceDirs(appInfo, new String[]{appInfo.sourceDir});
+ }
+ mPendingAppInfoUpdates = null;
+ }
+
+ if (!assetsUpdated && !mResConfiguration.isOtherSeqNewer(config)
+ && compat == null) {
if (DEBUG || DEBUG_CONFIGURATION) {
Slog.v(TAG, "Skipping new config: curSeq="
+ mResConfiguration.seq + ", newSeq=" + config.seq);
@@ -1332,9 +1321,6 @@
return false;
}
- // Things might have changed in display manager, so clear the cached displays.
- mAdjustedDisplays.clear();
-
int changes = mResConfiguration.updateFrom(config);
if (compat != null && (mResCompatibilityInfo == null
|| !mResCompatibilityInfo.equals(compat))) {
@@ -1367,7 +1353,7 @@
}
}
- return changes != 0;
+ return assetsUpdated || changes != 0;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 0ab3f2f..7cb1d89 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -697,6 +697,18 @@
* service element of manifest file. The value of attribute
* {@link android.R.attr#foregroundServiceType} can be multiple flags ORed together.</p>
*
+ * <div class="caution">
+ * <p><strong>Note:</strong>
+ * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S},
+ * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S}
+ * or higher are not allowed to start foreground services from the background.
+ * See
+ * <a href="{@docRoot}/about/versions/12/behavior-changes-12">
+ * Behavior changes: Apps targeting Android 12
+ * </a>
+ * for more details.
+ * </div>
+ *
* @throws ForegroundServiceStartNotAllowedException
* If the app targeting API is
* {@link android.os.Build.VERSION_CODES#S} or later, and the service is restricted from
@@ -733,6 +745,18 @@
* {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST} to use all flags that
* is specified in manifest attribute foregroundServiceType.</p>
*
+ * <div class="caution">
+ * <p><strong>Note:</strong>
+ * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S},
+ * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S}
+ * or higher are not allowed to start foreground services from the background.
+ * See
+ * <a href="{@docRoot}/about/versions/12/behavior-changes-12">
+ * Behavior changes: Apps targeting Android 12
+ * </a>
+ * for more details.
+ * </div>
+ *
* @param id The identifier for this notification as per
* {@link NotificationManager#notify(int, Notification)
* NotificationManager.notify(int, Notification)}; must not be 0.
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index cd82deb..7ef0a19 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -111,12 +111,15 @@
public WallpaperColors(Parcel parcel) {
mMainColors = new ArrayList<>();
mAllColors = new HashMap<>();
- final int count = parcel.readInt();
+ int count = parcel.readInt();
for (int i = 0; i < count; i++) {
final int colorInt = parcel.readInt();
Color color = Color.valueOf(colorInt);
mMainColors.add(color);
-
+ }
+ count = parcel.readInt();
+ for (int i = 0; i < count; i++) {
+ final int colorInt = parcel.readInt();
final int population = parcel.readInt();
mAllColors.put(colorInt, population);
}
@@ -411,9 +414,16 @@
for (int i = 0; i < count; i++) {
Color color = mainColors.get(i);
dest.writeInt(color.toArgb());
- Integer population = mAllColors.get(color.toArgb());
- int populationInt = (population != null) ? population : 0;
- dest.writeInt(populationInt);
+ }
+ count = mAllColors.size();
+ dest.writeInt(count);
+ for (Map.Entry<Integer, Integer> colorEntry : mAllColors.entrySet()) {
+ if (colorEntry.getKey() != null) {
+ dest.writeInt(colorEntry.getKey());
+ Integer population = colorEntry.getValue();
+ int populationInt = (population != null) ? population : 0;
+ dest.writeInt(populationInt);
+ }
}
dest.writeInt(mColorHints);
}
@@ -476,12 +486,13 @@
WallpaperColors other = (WallpaperColors) o;
return mMainColors.equals(other.mMainColors)
+ && mAllColors.equals(other.mAllColors)
&& mColorHints == other.mColorHints;
}
@Override
public int hashCode() {
- return 31 * mMainColors.hashCode() + mColorHints;
+ return (31 * mMainColors.hashCode() * mAllColors.hashCode()) + mColorHints;
}
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 52c58e1..8284cdd 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -11871,7 +11871,19 @@
public boolean isAffiliatedUser() {
throwIfParentInstance("isAffiliatedUser");
try {
- return mService.isAffiliatedUser();
+ return mService.isCallingUserAffiliated();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Returns whether target user is affiliated with the device.
+ */
+ public boolean isAffiliatedUser(@UserIdInt int userId) {
+ try {
+ return mService.isAffiliatedUser(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -13884,7 +13896,11 @@
}
/**
- * Returns whether USB data signaling is currently enabled by the admin. Callable by any app.
+ * Returns whether USB data signaling is currently enabled.
+ *
+ * <p> When called by a device owner or profile owner of an organization-owned managed profile,
+ * this API returns whether USB data signaling is currently enabled by that admin. When called
+ * by any other app, returns whether USB data signaling is currently enabled on the device.
*
* @return {@code true} if USB data signaling is enabled, {@code false} otherwise.
*/
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index a9bec98..a0d2977 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -18,7 +18,6 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.admin.DevicePolicyManager.OperationSafetyReason;
import android.content.ComponentName;
import android.content.Intent;
import android.os.UserHandle;
@@ -256,13 +255,4 @@
* {@link #supportsResetOp(int)} is true.
*/
public abstract void resetOp(int op, String packageName, @UserIdInt int userId);
-
- /**
- * Notifies the system that an unsafe operation reason has changed.
- *
- * @throws IllegalArgumentException if {@code checker} is not the same as set on
- * {@code DevicePolicyManagerService}.
- */
- public abstract void notifyUnsafeOperationStateChanged(DevicePolicySafetyChecker checker,
- @OperationSafetyReason int reason, boolean isSafe);
}
diff --git a/core/java/android/app/admin/DevicePolicyManagerLiteInternal.java b/core/java/android/app/admin/DevicePolicyManagerLiteInternal.java
new file mode 100644
index 0000000..ccb9947
--- /dev/null
+++ b/core/java/android/app/admin/DevicePolicyManagerLiteInternal.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import android.app.admin.DevicePolicyManager.OperationSafetyReason;
+
+/**
+ * Device policy manager local system service interface for methods that don't require the
+ * {@code device_admin} feature.
+ *
+ * Maintenance note: if you need to expose information from DPMS to lower level services such as
+ * PM/UM/AM/etc, then exposing it from DevicePolicyManagerInternal is not safe because it may cause
+ * lock order inversion. Consider using {@link DevicePolicyCache} instead.
+ *
+ * @hide Only for use within the system server.
+ */
+public interface DevicePolicyManagerLiteInternal {
+
+ /**
+ * Notifies the system that an unsafe operation reason has changed.
+ *
+ * @throws IllegalArgumentException if {@code checker} is not the same as set on
+ * {@code DevicePolicyManagerService.setDevicePolicySafetyChecker()}.
+ */
+ void notifyUnsafeOperationStateChanged(DevicePolicySafetyChecker checker,
+ @OperationSafetyReason int reason, boolean isSafe);
+}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index db2fc0d..b6c48a1 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -390,7 +390,8 @@
void setAffiliationIds(in ComponentName admin, in List<String> ids);
List<String> getAffiliationIds(in ComponentName admin);
- boolean isAffiliatedUser();
+ boolean isCallingUserAffiliated();
+ boolean isAffiliatedUser(int userId);
void setSecurityLoggingEnabled(in ComponentName admin, String packageName, boolean enabled);
boolean isSecurityLoggingEnabled(in ComponentName admin, String packageName);
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index eb4c624..585eb61 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -68,5 +68,5 @@
void reportUserInteraction(String packageName, int userId);
int getUsageSource();
void forceUsageSourceSettingRead();
- long getLastTimeAnyComponentUsed(String packageName);
+ long getLastTimeAnyComponentUsed(String packageName, String callingPackage);
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index e8175c7..ac7a318 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -1287,7 +1287,7 @@
android.Manifest.permission.PACKAGE_USAGE_STATS})
public long getLastTimeAnyComponentUsed(@NonNull String packageName) {
try {
- return mService.getLastTimeAnyComponentUsed(packageName);
+ return mService.getLastTimeAnyComponentUsed(packageName, mContext.getOpPackageName());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 3b11a19..ba3fc1e 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -37,7 +37,6 @@
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Parcelable;
-import android.os.UserHandle;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
@@ -719,10 +718,9 @@
protected Context getRemoteContext() {
try {
// Return if cloned successfully, otherwise default
- final ApplicationInfo info = mInfo.providerInfo.applicationInfo;
- Context newContext = mContext.createPackageContextAsUser(info.packageName,
- Context.CONTEXT_RESTRICTED,
- UserHandle.getUserHandleForUid(info.uid));
+ Context newContext = mContext.createApplicationContext(
+ mInfo.providerInfo.applicationInfo,
+ Context.CONTEXT_RESTRICTED);
if (mColorResources != null) {
mColorResources.apply(newContext);
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index ded5e6e..5b72b76 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -3054,6 +3054,9 @@
return true;
}
return false;
+ } else if (profile == BluetoothProfile.LE_AUDIO) {
+ BluetoothLeAudio leAudio = new BluetoothLeAudio(context, listener, this);
+ return true;
} else {
return false;
}
@@ -3142,6 +3145,10 @@
case BluetoothProfile.HEARING_AID:
BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy;
hearingAid.close();
+ break;
+ case BluetoothProfile.LE_AUDIO:
+ BluetoothLeAudio leAudio = (BluetoothLeAudio) proxy;
+ leAudio.close();
}
}
diff --git a/core/java/android/bluetooth/OWNERS b/core/java/android/bluetooth/OWNERS
index 3523ee0..2239100 100644
--- a/core/java/android/bluetooth/OWNERS
+++ b/core/java/android/bluetooth/OWNERS
@@ -2,3 +2,4 @@
zachoverflow@google.com
siyuanh@google.com
+rahulsabnis@google.com
diff --git a/core/java/android/companion/OWNERS b/core/java/android/companion/OWNERS
index da723b3..54b35fc 100644
--- a/core/java/android/companion/OWNERS
+++ b/core/java/android/companion/OWNERS
@@ -1 +1,4 @@
-eugenesusla@google.com
\ No newline at end of file
+ewol@google.com
+evanxinchen@google.com
+guojing@google.com
+svetoslavganov@google.com
\ No newline at end of file
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ea0e321..c02dcfd 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -46,7 +46,6 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.ApplicationInfoFlags;
import android.content.res.AssetManager;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
@@ -3182,7 +3181,8 @@
* <p>This function will throw {@link SecurityException} if you do not
* have permission to start the given service.
*
- * <p class="note"><strong>Note:</strong> Each call to startService()
+ * <div class="caution">
+ * <p><strong>Note:</strong> Each call to startService()
* results in significant work done by the system to manage service
* lifecycle surrounding the processing of the intent, which can take
* multiple milliseconds of CPU time. Due to this cost, startService()
@@ -3191,6 +3191,25 @@
* for high frequency calls.
* </p>
*
+ * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#O},
+ * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#O}
+ * or higher are not allowed to start background services from the background.
+ * See
+ * <a href="{@docRoot}/about/versions/oreo/background">
+ * Background Execution Limits</a>
+ * for more details.
+ *
+ * <p><strong>Note:</strong>
+ * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S},
+ * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S}
+ * or higher are not allowed to start foreground services from the background.
+ * See
+ * <a href="{@docRoot}/about/versions/12/behavior-changes-12">
+ * Behavior changes: Apps targeting Android 12
+ * </a>
+ * for more details.
+ * </div>
+ *
* @param service Identifies the service to be started. The Intent must be
* fully explicit (supplying a component name). Additional values
* may be included in the Intent extras to supply arguments along with
@@ -3215,6 +3234,7 @@
* This excemption extends {@link IllegalStateException}, so apps can
* use {@code catch (IllegalStateException)} to catch both.
*
+ * @see #startForegroundService(Intent)
* @see #stopService
* @see #bindService
*/
@@ -3232,6 +3252,18 @@
* at any time, regardless of whether the app hosting the service is in a foreground
* state.
*
+ * <div class="caution">
+ * <p><strong>Note:</strong>
+ * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S},
+ * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S}
+ * or higher are not allowed to start foreground services from the background.
+ * See
+ * <a href="{@docRoot}/about/versions/12/behavior-changes-12">
+ * Behavior changes: Apps targeting Android 12
+ * </a>
+ * for more details.
+ * </div>
+ *
* @param service Identifies the service to be started. The Intent must be
* fully explicit (supplying a component name). Additional values
* may be included in the Intent extras to supply arguments along with
@@ -6236,23 +6268,6 @@
}
/**
- * Similar to {@link #createPackageContextAsUser(String, int, UserHandle)}, but also allows
- * specifying the flags used to retrieve the {@link ApplicationInfo} of the package.
- *
- * @hide
- */
- @NonNull
- public Context createPackageContextAsUser(
- @NonNull String packageName, @CreatePackageOptions int flags, @NonNull UserHandle user,
- @ApplicationInfoFlags int packageFlags)
- throws PackageManager.NameNotFoundException {
- if (Build.IS_ENG) {
- throw new IllegalStateException("createPackageContextAsUser not overridden!");
- }
- return this;
- }
-
- /**
* Similar to {@link #createPackageContext(String, int)}, but for the own package with a
* different {@link UserHandle}. For example, {@link #getContentResolver()}
* will open any {@link Uri} as the given user.
@@ -6271,18 +6286,10 @@
/**
* Creates a context given an {@link android.content.pm.ApplicationInfo}.
*
- * @deprecated use {@link #createPackageContextAsUser(String, int, UserHandle, int)}
- * If an application caches an ApplicationInfo and uses it to call this method,
- * the app will not get the most recent version of Runtime Resource Overlays for
- * that application. To make things worse, the LoadedApk stored in
- * {@code ActivityThread#mResourcePackages} is updated using the old ApplicationInfo
- * causing further uses of the cached LoadedApk to return outdated information.
- *
* @hide
*/
@SuppressWarnings("HiddenAbstractMethod")
@UnsupportedAppUsage
- @Deprecated
public abstract Context createApplicationContext(ApplicationInfo application,
@CreatePackageOptions int flags) throws PackageManager.NameNotFoundException;
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index cf0dc8c..6324d0e 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1009,14 +1009,6 @@
/** @hide */
@Override
- public Context createPackageContextAsUser(String packageName, int flags, UserHandle user,
- int packageFlags)
- throws PackageManager.NameNotFoundException {
- return mBase.createPackageContextAsUser(packageName, flags, user, packageFlags);
- }
-
- /** @hide */
- @Override
public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) {
return mBase.createContextAsUser(user, flags);
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index af5f9ce..60ab83a 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -524,8 +524,12 @@
* owned ActivityContainer such as that within an ActivityView. If not set and
* this activity is launched into such a container a SecurityException will be
* thrown. Set from the {@link android.R.attr#allowEmbedded} attribute.
+ *
+ * @deprecated this flag is no longer needed since ActivityView is now fully removed
+ * TODO(b/191165536): delete this flag since is no longer used
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Deprecated
public static final int FLAG_ALLOW_EMBEDDED = 0x80000000;
/**
diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java
index 63f93bf..806091e 100644
--- a/core/java/android/content/pm/AppSearchShortcutInfo.java
+++ b/core/java/android/content/pm/AppSearchShortcutInfo.java
@@ -423,7 +423,7 @@
shortLabelResName, longLabel, longLabelResId, longLabelResName, disabledMessage,
disabledMessageResId, disabledMessageResName, categoriesSet, intents, rank, extras,
getCreationTimestampMillis(), flags, iconResId, iconResName, bitmapPath, iconUri,
- disabledReason, persons, locusId, 0);
+ disabledReason, persons, locusId, null);
si.setImplicitRank(implicitRank);
if ((implicitRank & ShortcutInfo.RANK_CHANGED_BIT) != 0) {
si.setRankChanged();
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index b0ce6a5..12911d6 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -62,6 +62,8 @@
void bypassNextStagedInstallerCheck(boolean value);
+ void bypassNextAllowedApexUpdateCheck(boolean value);
+
void setAllowUnlimitedSilentUpdates(String installerPackageName);
void setSilentUpdatesThrottleTime(long throttleTimeInSeconds);
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index c2ac80e..d3ed006 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -586,11 +586,6 @@
String targetCompilerFilter, boolean force);
/**
- * Ask the package manager to compile layouts in the given package.
- */
- boolean compileLayouts(String packageName);
-
- /**
* Ask the package manager to dump profiles associated with a package.
*/
void dumpProfiles(String packageName);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 33a34be..2ed00b5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1278,6 +1278,13 @@
*/
public static final int INSTALL_STAGED = 0x00200000;
+ /**
+ * Flag parameter for {@link #installPackage} to indicate that check whether given APEX can be
+ * updated should be disabled for this install.
+ * @hide
+ */
+ public static final int INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK = 0x00400000;
+
/** @hide */
@IntDef(flag = true, value = {
DONT_KILL_APP,
diff --git a/core/java/android/content/pm/PackagePartitions.java b/core/java/android/content/pm/PackagePartitions.java
index 52ee4de..d157768 100644
--- a/core/java/android/content/pm/PackagePartitions.java
+++ b/core/java/android/content/pm/PackagePartitions.java
@@ -119,6 +119,9 @@
@Nullable
private final DeferredCanonicalFile mOverlayFolder;
+ @NonNull
+ private final File mNonConicalFolder;
+
private SystemPartition(@NonNull File folder, @PartitionType int type,
boolean containsPrivApp, boolean containsOverlay) {
this.type = type;
@@ -128,6 +131,7 @@
: null;
this.mOverlayFolder = containsOverlay ? new DeferredCanonicalFile(folder, "overlay")
: null;
+ this.mNonConicalFolder = folder;
}
public SystemPartition(@NonNull SystemPartition original) {
@@ -136,6 +140,7 @@
this.mAppFolder = original.mAppFolder;
this.mPrivAppFolder = original.mPrivAppFolder;
this.mOverlayFolder = original.mOverlayFolder;
+ this.mNonConicalFolder = original.mNonConicalFolder;
}
/**
@@ -153,6 +158,12 @@
return mFolder.getFile();
}
+ /** Returns the non-canonical folder of the partition. */
+ @NonNull
+ public File getNonConicalFolder() {
+ return mNonConicalFolder;
+ }
+
/** Returns the canonical app folder of the partition. */
@Nullable
public File getAppFolder() {
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 76712b5..a264beb 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -449,7 +449,7 @@
private int mDisabledReason;
- private int mStartingThemeResId;
+ @Nullable private String mStartingThemeResName;
private ShortcutInfo(Builder b) {
mUserId = b.mContext.getUserId();
@@ -478,8 +478,9 @@
mExtras = b.mExtras;
mLocusId = b.mLocusId;
+ mStartingThemeResName = b.mStartingThemeResId != 0
+ ? b.mContext.getResources().getResourceName(b.mStartingThemeResId) : null;
updateTimestamp();
- mStartingThemeResId = b.mStartingThemeResId;
}
/**
@@ -626,7 +627,7 @@
// Set this bit.
mFlags |= FLAG_KEY_FIELDS_ONLY;
}
- mStartingThemeResId = source.mStartingThemeResId;
+ mStartingThemeResName = source.mStartingThemeResName;
}
/**
@@ -950,8 +951,8 @@
if (source.mLocusId != null) {
mLocusId = source.mLocusId;
}
- if (source.mStartingThemeResId != 0) {
- mStartingThemeResId = source.mStartingThemeResId;
+ if (source.mStartingThemeResName != null && !source.mStartingThemeResName.isEmpty()) {
+ mStartingThemeResName = source.mStartingThemeResName;
}
}
@@ -1454,11 +1455,12 @@
}
/**
- * Returns the theme resource id used for the splash screen.
+ * Returns the theme resource name used for the splash screen.
* @hide
*/
- public int getStartingThemeResId() {
- return mStartingThemeResId;
+ @Nullable
+ public String getStartingThemeResName() {
+ return mStartingThemeResName;
}
/** @hide -- old signature, the internal code still uses it. */
@@ -2182,7 +2184,7 @@
mPersons = source.readParcelableArray(cl, Person.class);
mLocusId = source.readParcelable(cl);
mIconUri = source.readString8();
- mStartingThemeResId = source.readInt();
+ mStartingThemeResName = source.readString8();
}
@Override
@@ -2234,7 +2236,7 @@
dest.writeParcelableArray(mPersons, flags);
dest.writeParcelable(mLocusId, flags);
dest.writeString8(mIconUri);
- dest.writeInt(mStartingThemeResId);
+ dest.writeString8(mStartingThemeResName);
}
public static final @NonNull Creator<ShortcutInfo> CREATOR =
@@ -2391,10 +2393,10 @@
sb.append("disabledReason=");
sb.append(getDisabledReasonDebugString(mDisabledReason));
- if (mStartingThemeResId != 0) {
+ if (mStartingThemeResName != null && !mStartingThemeResName.isEmpty()) {
addIndentOrComma(sb, indent);
- sb.append("SplashScreenThemeResId=");
- sb.append(Integer.toHexString(mStartingThemeResId));
+ sb.append("SplashScreenThemeResName=");
+ sb.append(mStartingThemeResName);
}
addIndentOrComma(sb, indent);
@@ -2482,7 +2484,8 @@
Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras,
long lastChangedTimestamp,
int flags, int iconResId, String iconResName, String bitmapPath, String iconUri,
- int disabledReason, Person[] persons, LocusId locusId, int startingThemeResId) {
+ int disabledReason, Person[] persons, LocusId locusId,
+ @Nullable String startingThemeResName) {
mUserId = userId;
mId = id;
mPackageName = packageName;
@@ -2511,6 +2514,6 @@
mDisabledReason = disabledReason;
mPersons = persons;
mLocusId = locusId;
- mStartingThemeResId = startingThemeResId;
+ mStartingThemeResName = startingThemeResName;
}
}
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index 233abf3..3ed5c64 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -74,7 +74,7 @@
/**
* Get the theme res ID of the starting window, it can be 0 if not specified.
*/
- public abstract int getShortcutStartingThemeResId(int launcherUserId,
+ public abstract @Nullable String getShortcutStartingThemeResName(int launcherUserId,
@NonNull String callingPackage, @NonNull String packageName, @NonNull String shortcutId,
int userId);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 4c44ba1..5a7f210 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -1164,7 +1164,7 @@
dest.writeTypedList(this.usesPermissions);
sForInternedStringList.parcel(this.implicitPermissions, dest, flags);
sForStringSet.parcel(this.upgradeKeySets, dest, flags);
- dest.writeMap(this.keySetMapping);
+ ParsingPackageUtils.writeKeySetMapping(dest, this.keySetMapping);
sForInternedStringList.parcel(this.protectedBroadcasts, dest, flags);
dest.writeTypedList(this.activities);
dest.writeTypedList(this.receivers);
@@ -1180,7 +1180,7 @@
sForInternedString.parcel(this.volumeUuid, dest, flags);
dest.writeParcelable(this.signingDetails, flags);
dest.writeString(this.mPath);
- dest.writeParcelableList(this.queriesIntents, flags);
+ dest.writeTypedList(this.queriesIntents, flags);
sForInternedStringList.parcel(this.queriesPackages, dest, flags);
sForInternedStringSet.parcel(this.queriesProviders, dest, flags);
dest.writeString(this.appComponentFactory);
@@ -1287,7 +1287,7 @@
this.usesPermissions = in.createTypedArrayList(ParsedUsesPermission.CREATOR);
this.implicitPermissions = sForInternedStringList.unparcel(in);
this.upgradeKeySets = sForStringSet.unparcel(in);
- this.keySetMapping = in.readHashMap(boot);
+ this.keySetMapping = ParsingPackageUtils.readKeySetMapping(in);
this.protectedBroadcasts = sForInternedStringList.unparcel(in);
this.activities = in.createTypedArrayList(ParsedActivity.CREATOR);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 6fd5333..dce242c 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -87,6 +87,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.FileUtils;
+import android.os.Parcel;
import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
@@ -3160,6 +3161,68 @@
}
/**
+ * Writes the keyset mapping to the provided package. {@code null} mappings are permitted.
+ */
+ public static void writeKeySetMapping(@NonNull Parcel dest,
+ @NonNull Map<String, ArraySet<PublicKey>> keySetMapping) {
+ if (keySetMapping == null) {
+ dest.writeInt(-1);
+ return;
+ }
+
+ final int N = keySetMapping.size();
+ dest.writeInt(N);
+
+ for (String key : keySetMapping.keySet()) {
+ dest.writeString(key);
+ ArraySet<PublicKey> keys = keySetMapping.get(key);
+ if (keys == null) {
+ dest.writeInt(-1);
+ continue;
+ }
+
+ final int M = keys.size();
+ dest.writeInt(M);
+ for (int j = 0; j < M; j++) {
+ dest.writeSerializable(keys.valueAt(j));
+ }
+ }
+ }
+
+ /**
+ * Reads a keyset mapping from the given parcel at the given data position. May return
+ * {@code null} if the serialized mapping was {@code null}.
+ */
+ @NonNull
+ public static ArrayMap<String, ArraySet<PublicKey>> readKeySetMapping(@NonNull Parcel in) {
+ final int N = in.readInt();
+ if (N == -1) {
+ return null;
+ }
+
+ ArrayMap<String, ArraySet<PublicKey>> keySetMapping = new ArrayMap<>();
+ for (int i = 0; i < N; ++i) {
+ String key = in.readString();
+ final int M = in.readInt();
+ if (M == -1) {
+ keySetMapping.put(key, null);
+ continue;
+ }
+
+ ArraySet<PublicKey> keys = new ArraySet<>(M);
+ for (int j = 0; j < M; ++j) {
+ PublicKey pk = (PublicKey) in.readSerializable();
+ keys.add(pk);
+ }
+
+ keySetMapping.put(key, keys);
+ }
+
+ return keySetMapping;
+ }
+
+
+ /**
* Callback interface for retrieving information that may be needed while parsing
* a package.
*/
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponent.java b/core/java/android/content/pm/parsing/component/ParsedComponent.java
index 4aed77a..9d830ec 100644
--- a/core/java/android/content/pm/parsing/component/ParsedComponent.java
+++ b/core/java/android/content/pm/parsing/component/ParsedComponent.java
@@ -172,7 +172,7 @@
this.packageName = sForInternedString.unparcel(in);
this.intents = sForIntentInfos.unparcel(in);
this.metaData = in.readBundle(boot);
- this.mProperties = in.createTypedArrayMap(Property.CREATOR);
+ this.mProperties = in.readHashMap(boot);
}
@NonNull
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
index 6b797bc..463a181 100644
--- a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
+++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java
@@ -19,7 +19,6 @@
import android.annotation.Nullable;
import android.content.IntentFilter;
import android.os.Parcel;
-import android.os.Parcelable;
import android.util.Pair;
import com.android.internal.util.Parcelling;
@@ -167,19 +166,6 @@
+ '}';
}
- public static final Parcelable.Creator<ParsedIntentInfo> CREATOR =
- new Parcelable.Creator<ParsedIntentInfo>() {
- @Override
- public ParsedIntentInfo createFromParcel(Parcel source) {
- return new ParsedIntentInfo(source);
- }
-
- @Override
- public ParsedIntentInfo[] newArray(int size) {
- return new ParsedIntentInfo[size];
- }
- };
-
public boolean isHasDefault() {
return hasDefault;
}
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index ec6c233..08f5a8a 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -848,8 +848,10 @@
/**
* Get the highest supported direct report mode rate level of the sensor.
*
- * @return Highest direct report rate level of this sensor. If the sensor does not support
- * direct report mode, this returns {@link SensorDirectChannel#RATE_STOP}.
+ * @return Highest direct report rate level of this sensor. Note that if the app does not have
+ * the {@link android.Manifest.permission#HIGH_SAMPLING_RATE_SENSORS} permission, the highest
+ * direct report rate level is {@link SensorDirectChannel#RATE_NORMAL}. If the sensor
+ * does not support direct report mode, this returns {@link SensorDirectChannel#RATE_STOP}.
* @see SensorDirectChannel#RATE_STOP
* @see SensorDirectChannel#RATE_NORMAL
* @see SensorDirectChannel#RATE_FAST
@@ -952,7 +954,8 @@
}
/**
- * @return name string of the sensor.
+ * @return name string of the sensor. The name is guaranteed to be unique
+ * for a particular sensor type.
*/
public String getName() {
return mName;
@@ -1001,9 +1004,11 @@
}
/**
- * @return the minimum delay allowed between two events in microsecond
+ * @return the minimum delay allowed between two events in microseconds
* or zero if this sensor only returns a value when the data it's measuring
- * changes.
+ * changes. Note that if the app does not have the
+ * {@link android.Manifest.permission#HIGH_SAMPLING_RATE_SENSORS} permission, the
+ * minimum delay is capped at 5000 microseconds (200 Hz).
*/
public int getMinDelay() {
return mMinDelay;
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 0a76a9c..572a8a8 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -46,6 +46,13 @@
* at {@link TriggerEventListener}. {@link Sensor#TYPE_SIGNIFICANT_MOTION}
* is an example of a trigger sensor.
* </p>
+ * <p>
+ * In order to access sensor data at high sampling rates (i.e. greater than 200 Hz
+ * for {@link SensorEventListener} and greater than {@link SensorDirectChannel#RATE_NORMAL}
+ * for {@link SensorDirectChannel}), apps must declare
+ * the {@link android.Manifest.permission#HIGH_SAMPLING_RATE_SENSORS} permission
+ * in their AndroidManifest.xml file.
+ * </p>
* <pre class="prettyprint">
* public class SensorActivity extends Activity implements SensorEventListener {
* private final SensorManager mSensorManager;
@@ -399,7 +406,9 @@
* Use this method to get the list of available sensors of a certain type.
* Make multiple calls to get sensors of different types or use
* {@link android.hardware.Sensor#TYPE_ALL Sensor.TYPE_ALL} to get all the
- * sensors.
+ * sensors. Note that the {@link android.hardware.Sensor#getName()} is
+ * expected to yield a value that is unique across any sensors that return
+ * the same value for {@link android.hardware.Sensor#getType()}.
*
* <p class="note">
* NOTE: Both wake-up and non wake-up sensors matching the given type are
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index a002707..60a36583 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -63,6 +63,11 @@
*/
int TYPE_FACE = 1 << 3;
+ /**
+ * @hide
+ */
+ int TYPE_ANY_BIOMETRIC = TYPE_FINGERPRINT | TYPE_IRIS | TYPE_FACE;
+
@IntDef(flag = true, value = {
TYPE_NONE,
TYPE_CREDENTIAL,
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index 79f716c..e665d0f 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -60,7 +60,8 @@
BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
BIOMETRIC_ERROR_RE_ENROLL,
BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
- FINGERPRINT_ERROR_UNKNOWN})
+ FINGERPRINT_ERROR_UNKNOWN,
+ FINGERPRINT_ERROR_BAD_CALIBARTION})
@Retention(RetentionPolicy.SOURCE)
@interface FingerprintError {}
@@ -181,6 +182,12 @@
int FINGERPRINT_ERROR_UNKNOWN = 17;
/**
+ * Error indicating that the fingerprint sensor has bad calibration.
+ * @hide
+ */
+ int FINGERPRINT_ERROR_BAD_CALIBARTION = 18;
+
+ /**
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
@@ -201,7 +208,8 @@
FINGERPRINT_ACQUIRED_TOO_FAST,
FINGERPRINT_ACQUIRED_VENDOR,
FINGERPRINT_ACQUIRED_START,
- FINGERPRINT_ACQUIRED_UNKNOWN})
+ FINGERPRINT_ACQUIRED_UNKNOWN,
+ FINGERPRINT_ACQUIRED_IMMOBILE})
@Retention(RetentionPolicy.SOURCE)
@interface FingerprintAcquired {}
@@ -271,6 +279,14 @@
int FINGERPRINT_ACQUIRED_UNKNOWN = 8;
/**
+ * This message may be sent during enrollment if the same area of the finger has already
+ * been captured during this enrollment session. In general, enrolling multiple areas of the
+ * same finger can help against false rejections.
+ * @hide
+ */
+ int FINGERPRINT_ACQUIRED_IMMOBILE = 9;
+
+ /**
* @hide
*/
int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 0a12470..e0138c5 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -2511,9 +2511,9 @@
* <p>Not all output formats may be supported in a configuration with
* an input stream of a particular format. For more details, see
* android.scaler.availableInputOutputFormatsMap.</p>
- * <p>The following table describes the minimum required output stream
- * configurations based on the hardware level
- * ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel}), prior to Android 12:</p>
+ * <p>For applications targeting SDK version older than 31, the following table
+ * describes the minimum required output stream configurations based on the hardware level
+ * ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel}):</p>
* <table>
* <thead>
* <tr>
@@ -2574,10 +2574,13 @@
* </tr>
* </tbody>
* </table>
- * <p>Starting from Android 12, the camera device may not support JPEG sizes smaller than the
- * minimum of 1080p and the camera sensor active array size. The requirements for
- * IMPLEMENTATION_DEFINED and YUV_420_888 stay the same. This new minimum required output
- * stream configurations are illustrated by the table below:</p>
+ * <p>For applications targeting SDK version 31 or newer, if the mobile device declares to be
+ * {@link android.os.Build.VERSION_CDOES.MEDIA_PERFORMANCE_CLASS media performance class} S,
+ * the primary camera devices (first rear/front camera in the camera ID list) will not
+ * support JPEG sizes smaller than 1080p. If the application configures a JPEG stream
+ * smaller than 1080p, the camera device will round up the JPEG image size to at least
+ * 1080p. The requirements for IMPLEMENTATION_DEFINED and YUV_420_888 stay the same.
+ * This new minimum required output stream configurations are illustrated by the table below:</p>
* <table>
* <thead>
* <tr>
@@ -2644,6 +2647,10 @@
* </tr>
* </tbody>
* </table>
+ * <p>For applications targeting SDK version 31 or newer, if the mobile device doesn't declare
+ * to be media performance class S, or if the camera device isn't a primary rear/front
+ * camera, the minimum required output stream configurations are the same as for applications
+ * targeting SDK version older than 31.</p>
* <p>Refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} for additional
* mandatory stream configurations on a per-capability basis.</p>
* <p>Exception on 176x144 (QCIF) resolution: camera devices usually have a fixed capability for
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 4127243..5b259f7 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.hardware.camera2.extension.IAdvancedExtenderImpl;
import android.hardware.camera2.extension.ICameraExtensionsProxyService;
@@ -32,6 +33,7 @@
import android.os.ConditionVariable;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.util.Log;
@@ -210,9 +212,9 @@
*/
private static final class CameraExtensionManagerGlobal {
private static final String TAG = "CameraExtensionManagerGlobal";
- private static final String PROXY_PACKAGE_NAME = "com.android.camera";
+ private static final String PROXY_PACKAGE_NAME = "com.android.cameraextensions";
private static final String PROXY_SERVICE_NAME =
- "com.android.camera.CameraExtensionsProxyService";
+ "com.android.cameraextensions.CameraExtensionsProxyService";
// Singleton instance
private static final CameraExtensionManagerGlobal GLOBAL_CAMERA_MANAGER =
@@ -235,6 +237,19 @@
if (mConnection == null) {
Intent intent = new Intent();
intent.setClassName(PROXY_PACKAGE_NAME, PROXY_SERVICE_NAME);
+ String vendorProxyPackage = SystemProperties.get(
+ "ro.vendor.camera.extensions.package");
+ String vendorProxyService = SystemProperties.get(
+ "ro.vendor.camera.extensions.service");
+ if (!vendorProxyPackage.isEmpty() && !vendorProxyService.isEmpty()) {
+ Log.v(TAG,
+ "Choosing the vendor camera extensions proxy package: "
+ + vendorProxyPackage);
+ Log.v(TAG,
+ "Choosing the vendor camera extensions proxy service: "
+ + vendorProxyService);
+ intent.setClassName(vendorProxyPackage, vendorProxyService);
+ }
mInitFuture = new InitializerFuture();
mConnection = new ServiceConnection() {
@Override
@@ -255,9 +270,9 @@
}
}
};
- ctx.bindService(intent, mConnection, Context.BIND_AUTO_CREATE |
- Context.BIND_IMPORTANT | Context.BIND_ABOVE_CLIENT |
- Context.BIND_NOT_VISIBLE);
+ ctx.bindService(intent, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT |
+ Context.BIND_ABOVE_CLIENT | Context.BIND_NOT_VISIBLE,
+ android.os.AsyncTask.THREAD_POOL_EXECUTOR, mConnection);
try {
mInitFuture.get(PROXY_SERVICE_DELAY_MS, TimeUnit.MILLISECONDS);
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index c1af99a..a1c8d29 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -217,7 +217,7 @@
@NonNull Map<String, SessionConfiguration> cameraIdAndSessionConfig)
throws CameraAccessException {
return CameraManagerGlobal.get().isConcurrentSessionConfigurationSupported(
- cameraIdAndSessionConfig);
+ cameraIdAndSessionConfig, mContext.getApplicationInfo().targetSdkVersion);
}
/**
@@ -413,7 +413,8 @@
try {
for (String physicalCameraId : physicalCameraIds) {
CameraMetadataNative physicalCameraInfo =
- cameraService.getCameraCharacteristics(physicalCameraId);
+ cameraService.getCameraCharacteristics(physicalCameraId,
+ mContext.getApplicationInfo().targetSdkVersion);
StreamConfiguration[] configs = physicalCameraInfo.get(
CameraCharacteristics.
SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS);
@@ -472,7 +473,8 @@
try {
Size displaySize = getDisplaySize();
- CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId);
+ CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId,
+ mContext.getApplicationInfo().targetSdkVersion);
try {
info.setCameraId(Integer.parseInt(cameraId));
} catch (NumberFormatException e) {
@@ -590,7 +592,7 @@
}
cameraUser = cameraService.connectDevice(callbacks, cameraId,
mContext.getOpPackageName(), mContext.getAttributionTag(), uid,
- oomScoreOffset);
+ oomScoreOffset, mContext.getApplicationInfo().targetSdkVersion);
} catch (ServiceSpecificException e) {
if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) {
throw new AssertionError("Should've gone down the shim path");
@@ -1613,8 +1615,8 @@
}
public boolean isConcurrentSessionConfigurationSupported(
- @NonNull Map<String, SessionConfiguration> cameraIdsAndSessionConfigurations)
- throws CameraAccessException {
+ @NonNull Map<String, SessionConfiguration> cameraIdsAndSessionConfigurations,
+ int targetSdkVersion) throws CameraAccessException {
if (cameraIdsAndSessionConfigurations == null) {
throw new IllegalArgumentException("cameraIdsAndSessionConfigurations was null");
@@ -1650,7 +1652,7 @@
}
try {
return mCameraService.isConcurrentSessionConfigurationSupported(
- cameraIdsAndConfigs);
+ cameraIdsAndConfigs, targetSdkVersion);
} catch (ServiceSpecificException e) {
throwAsPublicException(e);
} catch (RemoteException e) {
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index c739c6a..1c0ae28 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -16,24 +16,41 @@
package android.hardware.display;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Point;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.PowerManager;
import android.util.IntArray;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Objects;
+
/**
* Display manager local system service interface.
*
* @hide Only for use within the system server.
*/
public abstract class DisplayManagerInternal {
+
+ @IntDef(prefix = {"REFRESH_RATE_LIMIT_"}, value = {
+ REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RefreshRateLimitType {}
+
+ /** Refresh rate should be limited when High Brightness Mode is active. */
+ public static final int REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE = 1;
+
/**
* Called by the power manager to initialize power management facilities.
*/
@@ -293,6 +310,33 @@
public abstract int getRefreshRateSwitchingType();
/**
+ * TODO: b/191384041 - Replace this with getRefreshRateLimitations()
+ * Return the refresh rate restriction for the specified display and sensor pairing. If the
+ * specified sensor is identified as an associated sensor in the specified display's
+ * display-device-config file, then return any refresh rate restrictions that it might define.
+ * If no restriction is specified, or the sensor is not associated with the display, then null
+ * will be returned.
+ *
+ * @param displayId The display to check against.
+ * @param name The name of the sensor.
+ * @param type The type of sensor.
+ *
+ * @return The min/max refresh-rate restriction as a {@link Pair} of floats, or null if not
+ * restricted.
+ */
+ public abstract RefreshRateRange getRefreshRateForDisplayAndSensor(
+ int displayId, String name, String type);
+
+ /**
+ * Returns a list of various refresh rate limitations for the specified display.
+ *
+ * @param displayId The display to get limitations for.
+ *
+ * @return a list of {@link RefreshRateLimitation}s describing the various limits.
+ */
+ public abstract List<RefreshRateLimitation> getRefreshRateLimitations(int displayId);
+
+ /**
* Describes the requested power state of the display.
*
* This object is intended to describe the general characteristics of the
@@ -527,4 +571,91 @@
*/
void onDisplayGroupChanged(int groupId);
}
+
+ /**
+ * Information about the min and max refresh rate DM would like to set the display to.
+ */
+ public static final class RefreshRateRange {
+ public static final String TAG = "RefreshRateRange";
+
+ // The tolerance within which we consider something approximately equals.
+ public static final float FLOAT_TOLERANCE = 0.01f;
+
+ /**
+ * The lowest desired refresh rate.
+ */
+ public float min;
+
+ /**
+ * The highest desired refresh rate.
+ */
+ public float max;
+
+ public RefreshRateRange() {}
+
+ public RefreshRateRange(float min, float max) {
+ if (min < 0 || max < 0 || min > max + FLOAT_TOLERANCE) {
+ Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : "
+ + min + " " + max);
+ this.min = this.max = 0;
+ return;
+ }
+ if (min > max) {
+ // Min and max are within epsilon of each other, but in the wrong order.
+ float t = min;
+ min = max;
+ max = t;
+ }
+ this.min = min;
+ this.max = max;
+ }
+
+ /**
+ * Checks whether the two objects have the same values.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof RefreshRateRange)) {
+ return false;
+ }
+
+ RefreshRateRange refreshRateRange = (RefreshRateRange) other;
+ return (min == refreshRateRange.min && max == refreshRateRange.max);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(min, max);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + min + " " + max + ")";
+ }
+ }
+
+ /**
+ * Describes a limitation on a display's refresh rate. Includes the allowed refresh rate
+ * range as well as information about when it applies, such as high-brightness-mode.
+ */
+ public static final class RefreshRateLimitation {
+ @RefreshRateLimitType public int type;
+
+ /** The range the that refresh rate should be limited to. */
+ public RefreshRateRange range;
+
+ public RefreshRateLimitation(@RefreshRateLimitType int type, float min, float max) {
+ this.type = type;
+ range = new RefreshRateRange(min, max);
+ }
+
+ @Override
+ public String toString() {
+ return "RefreshRateLimitation(" + type + ": " + range + ")";
+ }
+ }
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index e594666..68dd6234 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -1375,6 +1375,9 @@
case BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED:
return context.getString(
com.android.internal.R.string.fingerprint_error_security_update_required);
+ case FINGERPRINT_ERROR_BAD_CALIBARTION:
+ return context.getString(
+ com.android.internal.R.string.fingerprint_error_bad_calibration);
case FINGERPRINT_ERROR_VENDOR: {
String[] msgArray = context.getResources().getStringArray(
com.android.internal.R.array.fingerprint_error_vendor);
@@ -1414,6 +1417,9 @@
case FINGERPRINT_ACQUIRED_TOO_FAST:
return context.getString(
com.android.internal.R.string.fingerprint_acquired_too_fast);
+ case FINGERPRINT_ACQUIRED_IMMOBILE:
+ return context.getString(
+ com.android.internal.R.string.fingerprint_acquired_immobile);
case FINGERPRINT_ACQUIRED_VENDOR: {
String[] msgArray = context.getResources().getStringArray(
com.android.internal.R.array.fingerprint_acquired_vendor);
diff --git a/core/java/android/hardware/input/InputDeviceVibrator.java b/core/java/android/hardware/input/InputDeviceVibrator.java
index d8150e4..653c622 100644
--- a/core/java/android/hardware/input/InputDeviceVibrator.java
+++ b/core/java/android/hardware/input/InputDeviceVibrator.java
@@ -56,10 +56,10 @@
mDeviceId = deviceId;
mVibratorInfo = new VibratorInfo.Builder(vibratorId)
.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
- // Set predefined support to empty as we know input devices do not support them.
- .setSupportedEffects()
- .setSupportedPrimitives()
- .setSupportedBraking()
+ // The supported effect and braking lists are known to be empty for input devices,
+ // which is different from not being set (that means the device support is unknown).
+ .setSupportedEffects(new int[0])
+ .setSupportedBraking(new int[0])
.build();
mToken = new Binder();
}
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index 662ebb3..5c28553 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -389,6 +389,10 @@
/**
* Starts a legacy VPN.
+ *
+ * Legacy VPN is deprecated starting from Android S. So this API shouldn't be called if the
+ * initial SDK version of device is Android S+. Otherwise, UnsupportedOperationException will be
+ * thrown.
* @hide
*/
public void startLegacyVpn(VpnProfile profile) {
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 86cd23d..752ef3e 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -352,6 +352,7 @@
public int hashCode() {
return Objects.hash(
mGatewayConnectionName,
+ mTunnelConnectionParams,
mExposedCapabilities,
Arrays.hashCode(mRetryIntervalsMs),
mMaxMtu);
@@ -365,6 +366,7 @@
final VcnGatewayConnectionConfig rhs = (VcnGatewayConnectionConfig) other;
return mGatewayConnectionName.equals(rhs.mGatewayConnectionName)
+ && mTunnelConnectionParams.equals(rhs.mTunnelConnectionParams)
&& mExposedCapabilities.equals(rhs.mExposedCapabilities)
&& Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
&& mMaxMtu == rhs.mMaxMtu;
diff --git a/core/java/android/os/AggregateBatteryConsumer.java b/core/java/android/os/AggregateBatteryConsumer.java
index ee86265..802387c 100644
--- a/core/java/android/os/AggregateBatteryConsumer.java
+++ b/core/java/android/os/AggregateBatteryConsumer.java
@@ -17,7 +17,13 @@
package android.os;
import android.annotation.NonNull;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.io.PrintWriter;
/**
@@ -72,6 +78,43 @@
return mConsumedPowerMah;
}
+ /** Serializes this object to XML */
+ void writeToXml(TypedXmlSerializer serializer,
+ @BatteryUsageStats.AggregateBatteryConsumerScope int scope) throws IOException {
+ serializer.startTag(null, BatteryUsageStats.XML_TAG_AGGREGATE);
+ serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_SCOPE, scope);
+ serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, mConsumedPowerMah);
+ mPowerComponents.writeToXml(serializer);
+ serializer.endTag(null, BatteryUsageStats.XML_TAG_AGGREGATE);
+ }
+
+ /** Parses an XML representation and populates the BatteryUsageStats builder */
+ static void parseXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder)
+ throws XmlPullParserException, IOException {
+ final int scope = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_SCOPE);
+ final Builder consumerBuilder = builder.getAggregateBatteryConsumerBuilder(scope);
+
+ int eventType = parser.getEventType();
+ if (eventType != XmlPullParser.START_TAG || !parser.getName().equals(
+ BatteryUsageStats.XML_TAG_AGGREGATE)) {
+ throw new XmlPullParserException("Invalid XML parser state");
+ }
+
+ consumerBuilder.setConsumedPower(
+ parser.getAttributeDouble(null, BatteryUsageStats.XML_ATTR_POWER));
+
+ while (!(eventType == XmlPullParser.END_TAG && parser.getName().equals(
+ BatteryUsageStats.XML_TAG_AGGREGATE))
+ && eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) {
+ PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder);
+ }
+ }
+ eventType = parser.next();
+ }
+ }
+
/**
* Builder for DeviceBatteryConsumer.
*/
@@ -91,6 +134,14 @@
}
/**
+ * Adds power and usage duration from the supplied AggregateBatteryConsumer.
+ */
+ public void add(AggregateBatteryConsumer aggregateBatteryConsumer) {
+ mConsumedPowerMah += aggregateBatteryConsumer.mConsumedPowerMah;
+ mPowerComponentsBuilder.addPowerAndDuration(aggregateBatteryConsumer.mPowerComponents);
+ }
+
+ /**
* Creates a read-only object out of the Builder values.
*/
@NonNull
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index 6c9f0f67..f483752 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -20,16 +20,23 @@
import android.annotation.NonNull;
import android.util.Range;
import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.proto.ProtoOutputStream;
import com.android.internal.os.BatteryStatsHistory;
import com.android.internal.os.BatteryStatsHistoryIterator;
import com.android.internal.os.PowerCalculator;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
@@ -77,6 +84,34 @@
private static final int STATSD_PULL_ATOM_MAX_BYTES = 45000;
+ // XML tags and attributes for BatteryUsageStats persistence
+ static final String XML_TAG_BATTERY_USAGE_STATS = "battery_usage_stats";
+ static final String XML_TAG_AGGREGATE = "aggregate";
+ static final String XML_TAG_UID = "uid";
+ static final String XML_TAG_USER = "user";
+ static final String XML_TAG_POWER_COMPONENTS = "power_components";
+ static final String XML_TAG_COMPONENT = "component";
+ static final String XML_TAG_CUSTOM_COMPONENT = "custom_component";
+ static final String XML_ATTR_ID = "id";
+ static final String XML_ATTR_UID = "uid";
+ static final String XML_ATTR_USER_ID = "user_id";
+ static final String XML_ATTR_SCOPE = "scope";
+ static final String XML_ATTR_PREFIX_CUSTOM_COMPONENT = "custom_component_";
+ static final String XML_ATTR_START_TIMESTAMP = "start_timestamp";
+ static final String XML_ATTR_END_TIMESTAMP = "end_timestamp";
+ static final String XML_ATTR_POWER = "power";
+ static final String XML_ATTR_DURATION = "duration";
+ static final String XML_ATTR_MODEL = "model";
+ static final String XML_ATTR_BATTERY_CAPACITY = "battery_capacity";
+ static final String XML_ATTR_DISCHARGE_PERCENT = "discharge_pct";
+ static final String XML_ATTR_DISCHARGE_LOWER = "discharge_lower";
+ static final String XML_ATTR_DISCHARGE_UPPER = "discharge_upper";
+ static final String XML_ATTR_BATTERY_REMAINING = "battery_remaining";
+ static final String XML_ATTR_CHARGE_REMAINING = "charge_remaining";
+ static final String XML_ATTR_HIGHEST_DRAIN_PACKAGE = "highest_drain_package";
+ static final String XML_ATTR_TIME_IN_FOREGROUND = "time_in_foreground";
+ static final String XML_ATTR_TIME_IN_BACKGROUND = "time_in_background";
+
private final int mDischargePercentage;
private final double mBatteryCapacityMah;
private final long mStatsStartTimestampMs;
@@ -96,11 +131,7 @@
private BatteryUsageStats(@NonNull Builder builder) {
mStatsStartTimestampMs = builder.mStatsStartTimestampMs;
mStatsEndTimestampMs = builder.mStatsEndTimestampMs;
- if (builder.mStatsDurationMs != -1) {
- mStatsDurationMs = builder.mStatsDurationMs;
- } else {
- mStatsDurationMs = mStatsEndTimestampMs - mStatsStartTimestampMs;
- }
+ mStatsDurationMs = builder.getStatsDuration();
mBatteryCapacityMah = builder.mBatteryCapacityMah;
mDischargePercentage = builder.mDischargePercentage;
mDischargedPowerLowerBound = builder.mDischargedPowerLowerBoundMah;
@@ -275,14 +306,23 @@
AggregateBatteryConsumer.CREATOR.createFromParcel(source);
mAggregateBatteryConsumers[i].setCustomPowerComponentNames(mCustomPowerComponentNames);
}
- int uidCount = source.readInt();
+
+ // UidBatteryConsumers are included as a blob to avoid a TransactionTooLargeException
+ final Parcel blob = Parcel.obtain();
+ final byte[] bytes = source.readBlob();
+ blob.unmarshall(bytes, 0, bytes.length);
+ blob.setDataPosition(0);
+
+ final int uidCount = blob.readInt();
mUidBatteryConsumers = new ArrayList<>(uidCount);
for (int i = 0; i < uidCount; i++) {
final UidBatteryConsumer consumer =
- UidBatteryConsumer.CREATOR.createFromParcel(source);
+ UidBatteryConsumer.CREATOR.createFromParcel(blob);
consumer.setCustomPowerComponentNames(mCustomPowerComponentNames);
mUidBatteryConsumers.add(consumer);
}
+ blob.recycle();
+
int userCount = source.readInt();
mUserBatteryConsumers = new ArrayList<>(userCount);
for (int i = 0; i < userCount; i++) {
@@ -292,14 +332,10 @@
mUserBatteryConsumers.add(consumer);
}
if (source.readBoolean()) {
- mHistoryBuffer = Parcel.obtain();
- mHistoryBuffer.setDataSize(0);
- mHistoryBuffer.setDataPosition(0);
+ final byte[] historyBlob = source.readBlob();
- int historyBufferSize = source.readInt();
- int curPos = source.dataPosition();
- mHistoryBuffer.appendFrom(source, curPos, historyBufferSize);
- source.setDataPosition(curPos + historyBufferSize);
+ mHistoryBuffer = Parcel.obtain();
+ mHistoryBuffer.unmarshall(historyBlob, 0, historyBlob.length);
int historyTagCount = source.readInt();
mHistoryTagPool = new ArrayList<>(historyTagCount);
@@ -331,21 +367,26 @@
for (int i = 0; i < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; i++) {
mAggregateBatteryConsumers[i].writeToParcel(dest, flags);
}
- dest.writeInt(mUidBatteryConsumers.size());
+
+ // UidBatteryConsumers are included as a blob, because each UidBatteryConsumer
+ // takes > 300 bytes, so a typical number of UIDs in the system, 300 would result
+ // in a 90 kB Parcel, which is not safe to pass in a binder transaction because
+ // of the possibility of TransactionTooLargeException
+ final Parcel blob = Parcel.obtain();
+ blob.writeInt(mUidBatteryConsumers.size());
for (int i = mUidBatteryConsumers.size() - 1; i >= 0; i--) {
- mUidBatteryConsumers.get(i).writeToParcel(dest, flags);
+ mUidBatteryConsumers.get(i).writeToParcel(blob, flags);
}
+ dest.writeBlob(blob.marshall());
+ blob.recycle();
+
dest.writeInt(mUserBatteryConsumers.size());
for (int i = mUserBatteryConsumers.size() - 1; i >= 0; i--) {
mUserBatteryConsumers.get(i).writeToParcel(dest, flags);
}
if (mHistoryBuffer != null) {
dest.writeBoolean(true);
-
- final int historyBufferSize = mHistoryBuffer.dataSize();
- dest.writeInt(historyBufferSize);
- dest.appendFrom(mHistoryBuffer, 0, historyBufferSize);
-
+ dest.writeBlob(mHistoryBuffer.marshall());
dest.writeInt(mHistoryTagPool.size());
for (int i = mHistoryTagPool.size() - 1; i >= 0; i--) {
final BatteryStats.HistoryTag tag = mHistoryTagPool.get(i);
@@ -579,6 +620,109 @@
}
}
+ /** Serializes this object to XML */
+ public void writeXml(TypedXmlSerializer serializer) throws IOException {
+ serializer.startTag(null, XML_TAG_BATTERY_USAGE_STATS);
+
+ for (int i = 0; i < mCustomPowerComponentNames.length; i++) {
+ serializer.attribute(null, XML_ATTR_PREFIX_CUSTOM_COMPONENT + i,
+ mCustomPowerComponentNames[i]);
+ }
+
+ serializer.attributeLong(null, XML_ATTR_START_TIMESTAMP, mStatsStartTimestampMs);
+ serializer.attributeLong(null, XML_ATTR_END_TIMESTAMP, mStatsEndTimestampMs);
+ serializer.attributeLong(null, XML_ATTR_DURATION, mStatsDurationMs);
+ serializer.attributeDouble(null, XML_ATTR_BATTERY_CAPACITY, mBatteryCapacityMah);
+ serializer.attributeInt(null, XML_ATTR_DISCHARGE_PERCENT, mDischargePercentage);
+ serializer.attributeDouble(null, XML_ATTR_DISCHARGE_LOWER, mDischargedPowerLowerBound);
+ serializer.attributeDouble(null, XML_ATTR_DISCHARGE_UPPER, mDischargedPowerUpperBound);
+ serializer.attributeLong(null, XML_ATTR_BATTERY_REMAINING, mBatteryTimeRemainingMs);
+ serializer.attributeLong(null, XML_ATTR_CHARGE_REMAINING, mChargeTimeRemainingMs);
+
+ for (int scope = 0; scope < BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT;
+ scope++) {
+ mAggregateBatteryConsumers[scope].writeToXml(serializer, scope);
+ }
+ for (UidBatteryConsumer consumer : mUidBatteryConsumers) {
+ consumer.writeToXml(serializer);
+ }
+ for (UserBatteryConsumer consumer : mUserBatteryConsumers) {
+ consumer.writeToXml(serializer);
+ }
+ serializer.endTag(null, XML_TAG_BATTERY_USAGE_STATS);
+ }
+
+ /** Parses an XML representation of BatteryUsageStats */
+ public static BatteryUsageStats createFromXml(TypedXmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ Builder builder = null;
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG
+ && parser.getName().equals(XML_TAG_BATTERY_USAGE_STATS)) {
+ List<String> customComponentNames = new ArrayList<>();
+ int i = 0;
+ while (true) {
+ int index = parser.getAttributeIndex(null,
+ XML_ATTR_PREFIX_CUSTOM_COMPONENT + i);
+ if (index == -1) {
+ break;
+ }
+ customComponentNames.add(parser.getAttributeValue(index));
+ i++;
+ }
+
+ builder = new Builder(
+ customComponentNames.toArray(new String[0]), true);
+
+ builder.setStatsStartTimestamp(
+ parser.getAttributeLong(null, XML_ATTR_START_TIMESTAMP));
+ builder.setStatsEndTimestamp(
+ parser.getAttributeLong(null, XML_ATTR_END_TIMESTAMP));
+ builder.setStatsDuration(
+ parser.getAttributeLong(null, XML_ATTR_DURATION));
+ builder.setBatteryCapacity(
+ parser.getAttributeDouble(null, XML_ATTR_BATTERY_CAPACITY));
+ builder.setDischargePercentage(
+ parser.getAttributeInt(null, XML_ATTR_DISCHARGE_PERCENT));
+ builder.setDischargedPowerRange(
+ parser.getAttributeDouble(null, XML_ATTR_DISCHARGE_LOWER),
+ parser.getAttributeDouble(null, XML_ATTR_DISCHARGE_UPPER));
+ builder.setBatteryTimeRemainingMs(
+ parser.getAttributeLong(null, XML_ATTR_BATTERY_REMAINING));
+ builder.setChargeTimeRemainingMs(
+ parser.getAttributeLong(null, XML_ATTR_CHARGE_REMAINING));
+
+ eventType = parser.next();
+ break;
+ }
+ eventType = parser.next();
+ }
+
+ if (builder == null) {
+ throw new XmlPullParserException("No root element");
+ }
+
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ switch (parser.getName()) {
+ case XML_TAG_AGGREGATE:
+ AggregateBatteryConsumer.parseXml(parser, builder);
+ break;
+ case XML_TAG_UID:
+ UidBatteryConsumer.createFromXml(parser, builder);
+ break;
+ case XML_TAG_USER:
+ UserBatteryConsumer.createFromXml(parser, builder);
+ break;
+ }
+ }
+ eventType = parser.next();
+ }
+
+ return builder.build();
+ }
+
/**
* Builder for BatteryUsageStats.
*/
@@ -658,6 +802,14 @@
return this;
}
+ private long getStatsDuration() {
+ if (mStatsDurationMs != -1) {
+ return mStatsDurationMs;
+ } else {
+ return mStatsEndTimestampMs - mStatsStartTimestampMs;
+ }
+ }
+
/**
* Sets the battery discharge amount since BatteryStats reset as percentage of the full
* charge.
@@ -738,6 +890,22 @@
}
/**
+ * Creates or returns a UidBatteryConsumer, which represents battery attribution
+ * data for an individual UID. This version of the method is not suitable for use
+ * with PowerCalculators.
+ */
+ @NonNull
+ public UidBatteryConsumer.Builder getOrCreateUidBatteryConsumerBuilder(int uid) {
+ UidBatteryConsumer.Builder builder = mUidBatteryConsumerBuilders.get(uid);
+ if (builder == null) {
+ builder = new UidBatteryConsumer.Builder(mCustomPowerComponentNames,
+ mIncludePowerModels, uid);
+ mUidBatteryConsumerBuilders.put(uid, builder);
+ }
+ return builder;
+ }
+
+ /**
* Creates or returns a UserBatteryConsumer, which represents battery attribution
* data for an individual {@link UserHandle}.
*/
@@ -756,5 +924,59 @@
public SparseArray<UidBatteryConsumer.Builder> getUidBatteryConsumerBuilders() {
return mUidBatteryConsumerBuilders;
}
+
+ /**
+ * Adds battery usage stats from another snapshots. The two snapshots are assumed to be
+ * non-overlapping, meaning that the power consumption estimates and session durations
+ * can be simply summed across the two snapshots. This remains true even if the timestamps
+ * seem to indicate that the sessions are in fact overlapping: timestamps may be off as a
+ * result of realtime clock adjustments by the user or the system.
+ */
+ @NonNull
+ public Builder add(BatteryUsageStats stats) {
+ if (!Arrays.equals(mCustomPowerComponentNames, stats.mCustomPowerComponentNames)) {
+ throw new IllegalArgumentException(
+ "BatteryUsageStats have different custom power components");
+ }
+
+ if (mUserBatteryConsumerBuilders.size() != 0
+ || !stats.getUserBatteryConsumers().isEmpty()) {
+ throw new UnsupportedOperationException(
+ "Combining UserBatteryConsumers is not supported");
+ }
+
+ mDischargedPowerLowerBoundMah += stats.mDischargedPowerLowerBound;
+ mDischargedPowerUpperBoundMah += stats.mDischargedPowerUpperBound;
+ mDischargePercentage += stats.mDischargePercentage;
+
+ mStatsDurationMs = getStatsDuration() + stats.getStatsDuration();
+
+ if (mStatsStartTimestampMs == 0
+ || stats.mStatsStartTimestampMs < mStatsStartTimestampMs) {
+ mStatsStartTimestampMs = stats.mStatsStartTimestampMs;
+ }
+
+ final boolean addingLaterSnapshot = stats.mStatsEndTimestampMs > mStatsEndTimestampMs;
+ if (addingLaterSnapshot) {
+ mStatsEndTimestampMs = stats.mStatsEndTimestampMs;
+ }
+
+ for (int scope = 0; scope < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; scope++) {
+ getAggregateBatteryConsumerBuilder(scope)
+ .add(stats.mAggregateBatteryConsumers[scope]);
+ }
+
+ for (UidBatteryConsumer consumer : stats.getUidBatteryConsumers()) {
+ getOrCreateUidBatteryConsumerBuilder(consumer.getUid()).add(consumer);
+ }
+
+ if (addingLaterSnapshot) {
+ mBatteryCapacityMah = stats.mBatteryCapacityMah;
+ mBatteryTimeRemainingMs = stats.mBatteryTimeRemainingMs;
+ mChargeTimeRemainingMs = stats.mChargeTimeRemainingMs;
+ }
+
+ return this;
+ }
}
}
diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java
index 5080442..97f24cc 100644
--- a/core/java/android/os/BatteryUsageStatsQuery.java
+++ b/core/java/android/os/BatteryUsageStatsQuery.java
@@ -72,12 +72,16 @@
@NonNull
private final int[] mUserIds;
private final long mMaxStatsAgeMs;
+ private long mFromTimestamp;
+ private long mToTimestamp;
private BatteryUsageStatsQuery(@NonNull Builder builder) {
mFlags = builder.mFlags;
mUserIds = builder.mUserIds != null ? builder.mUserIds.toArray()
: new int[]{UserHandle.USER_ALL};
mMaxStatsAgeMs = builder.mMaxStatsAgeMs;
+ mFromTimestamp = builder.mFromTimestamp;
+ mToTimestamp = builder.mToTimestamp;
}
@BatteryUsageStatsFlags
@@ -112,11 +116,30 @@
return mMaxStatsAgeMs;
}
+ /**
+ * Returns the exclusive lower bound of the stored snapshot timestamps that should be included
+ * in the aggregation. Ignored if {@link #getToTimestamp()} is zero.
+ */
+ public long getFromTimestamp() {
+ return mFromTimestamp;
+ }
+
+ /**
+ * Returns the inclusive upper bound of the stored snapshot timestamps that should
+ * be included in the aggregation. The default is to include only the current stats
+ * accumulated since the latest battery reset.
+ */
+ public long getToTimestamp() {
+ return mToTimestamp;
+ }
+
private BatteryUsageStatsQuery(Parcel in) {
mFlags = in.readInt();
mUserIds = new int[in.readInt()];
in.readIntArray(mUserIds);
mMaxStatsAgeMs = in.readLong();
+ mFromTimestamp = in.readLong();
+ mToTimestamp = in.readLong();
}
@Override
@@ -125,6 +148,8 @@
dest.writeInt(mUserIds.length);
dest.writeIntArray(mUserIds);
dest.writeLong(mMaxStatsAgeMs);
+ dest.writeLong(mFromTimestamp);
+ dest.writeLong(mToTimestamp);
}
@Override
@@ -153,6 +178,8 @@
private int mFlags;
private IntArray mUserIds;
private long mMaxStatsAgeMs = DEFAULT_MAX_STATS_AGE_MS;
+ private long mFromTimestamp;
+ private long mToTimestamp;
/**
* Builds a read-only BatteryUsageStatsQuery object.
@@ -204,6 +231,17 @@
}
/**
+ * Requests to aggregate stored snapshots between the two supplied timestamps
+ * @param fromTimestamp Exclusive starting timestamp, as per System.currentTimeMillis()
+ * @param toTimestamp Inclusive ending timestamp, as per System.currentTimeMillis()
+ */
+ public Builder aggregateSnapshots(long fromTimestamp, long toTimestamp) {
+ mFromTimestamp = fromTimestamp;
+ mToTimestamp = toTimestamp;
+ return this;
+ }
+
+ /**
* Set the client's tolerance for stale battery stats. The data may be up to
* this many milliseconds out-of-date.
*/
diff --git a/core/java/android/os/FileBridge.java b/core/java/android/os/FileBridge.java
index 7b84575..9dcdbf9 100644
--- a/core/java/android/os/FileBridge.java
+++ b/core/java/android/os/FileBridge.java
@@ -32,6 +32,7 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
@@ -95,9 +96,11 @@
@Override
public void run() {
- final byte[] temp = new byte[8192];
+ final ByteBuffer tempBuffer = ByteBuffer.allocateDirect(8192);
+ final byte[] temp = tempBuffer.hasArray() ? tempBuffer.array() : new byte[8192];
try {
- while (IoBridge.read(mServer.getFileDescriptor(), temp, 0, MSG_LENGTH) == MSG_LENGTH) {
+ while (IoBridge.read(mServer.getFileDescriptor(), temp,
+ 0, MSG_LENGTH) == MSG_LENGTH) {
final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
if (cmd == CMD_WRITE) {
// Shuttle data into local file
@@ -138,7 +141,10 @@
public static class FileBridgeOutputStream extends OutputStream {
private final ParcelFileDescriptor mClientPfd;
private final FileDescriptor mClient;
- private final byte[] mTemp = new byte[MSG_LENGTH];
+ private final ByteBuffer mTempBuffer = ByteBuffer.allocateDirect(MSG_LENGTH);
+ private final byte[] mTemp = mTempBuffer.hasArray()
+ ? mTempBuffer.array()
+ : new byte[MSG_LENGTH];
public FileBridgeOutputStream(ParcelFileDescriptor clientPfd) {
mClientPfd = clientPfd;
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 9a81942..3bee4b7 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -46,6 +46,7 @@
UserInfo createProfileForUserWithThrow(in String name, in String userType, int flags, int userId,
in String[] disallowedPackages);
UserInfo createRestrictedProfileWithThrow(String name, int parentUserHandle);
+ String[] getPreInstallableSystemPackages(in String userType);
void setUserEnabled(int userId);
void setUserAdmin(int userId);
void evictCredentialEncryptionKey(int userId);
diff --git a/core/java/android/os/PackageTagsList.java b/core/java/android/os/PackageTagsList.java
index c94d3de..4a81dc6 100644
--- a/core/java/android/os/PackageTagsList.java
+++ b/core/java/android/os/PackageTagsList.java
@@ -23,20 +23,26 @@
import android.util.ArrayMap;
import android.util.ArraySet;
+import com.android.internal.annotations.Immutable;
+
import java.io.PrintWriter;
+import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
- * A list of packages and associated attribution tags that supports easy membership checks.
+ * A list of packages and associated attribution tags that supports easy membership checks. Supports
+ * "wildcard" attribution tags (ie, matching any attribution tag under a package) in additional to
+ * standard checks.
*
* @hide
*/
@TestApi
+@Immutable
public final class PackageTagsList implements Parcelable {
- // an empty set value matches any attribution tag
+ // an empty set value matches any attribution tag (ie, wildcard)
private final ArrayMap<String, ArraySet<String>> mPackageTags;
private PackageTagsList(@NonNull ArrayMap<String, ArraySet<String>> packageTags) {
@@ -51,15 +57,34 @@
}
/**
- * Returns true if the given package is represented within this instance. If this returns true
- * this does not imply anything about whether any given attribution tag under the given package
- * name is present.
+ * Returns true if the given package is found within this instance. If this returns true this
+ * does not imply anything about whether any given attribution tag under the given package name
+ * is present.
*/
public boolean includes(@NonNull String packageName) {
return mPackageTags.containsKey(packageName);
}
/**
+ * Returns true if the given attribution tag is found within this instance under any package.
+ * Only returns true if the attribution tag literal is found, not if any package contains the
+ * set of all attribution tags.
+ *
+ * @hide
+ */
+ public boolean includesTag(@NonNull String attributionTag) {
+ final int size = mPackageTags.size();
+ for (int i = 0; i < size; i++) {
+ ArraySet<String> tags = mPackageTags.valueAt(i);
+ if (tags.contains(attributionTag)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
* Returns true if all attribution tags under the given package are contained within this
* instance.
*/
@@ -76,6 +101,7 @@
if (tags == null) {
return false;
} else if (tags.isEmpty()) {
+ // our tags are the full set, so we contain any attribution tag
return true;
} else {
return tags.contains(attributionTag);
@@ -98,10 +124,12 @@
return false;
}
if (tags.isEmpty()) {
+ // our tags are the full set, so we contain whatever the other tags are
continue;
}
ArraySet<String> otherTags = packageTagsList.mPackageTags.valueAt(i);
if (otherTags.isEmpty()) {
+ // other tags are the full set, so we can't contain them
return false;
}
if (!tags.containsAll(otherTags)) {
@@ -248,6 +276,31 @@
}
/**
+ * Adds the specified package and set of attribution tags to the builder.
+ *
+ * @hide
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder add(@NonNull String packageName,
+ @NonNull Collection<String> attributionTags) {
+ if (attributionTags.isEmpty()) {
+ // the input is not allowed to specify a full set by passing in an empty collection
+ return this;
+ }
+
+ ArraySet<String> tags = mPackageTags.get(packageName);
+ if (tags == null) {
+ tags = new ArraySet<>(attributionTags);
+ mPackageTags.put(packageName, tags);
+ } else if (!tags.isEmpty()) {
+ // if we contain the full set, already done, otherwise add all the tags
+ tags.addAll(attributionTags);
+ }
+
+ return this;
+ }
+
+ /**
* Adds the specified {@link PackageTagsList} to the builder.
*/
@SuppressLint("MissingGetterMatchingBuilder")
@@ -267,13 +320,92 @@
if (newTags.isEmpty()) {
add(entry.getKey());
} else {
- ArraySet<String> tags = mPackageTags.get(entry.getKey());
- if (tags == null) {
- tags = new ArraySet<>(newTags);
- mPackageTags.put(entry.getKey(), tags);
- } else if (!tags.isEmpty()) {
- tags.addAll(newTags);
- }
+ add(entry.getKey(), newTags);
+ }
+ }
+
+ return this;
+ }
+
+ /**
+ * Removes all attribution tags under the specified package from the builder.
+ *
+ * @hide
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder remove(@NonNull String packageName) {
+ mPackageTags.remove(packageName);
+ return this;
+ }
+
+ /**
+ * Removes the specified package and attribution tag from the builder if and only if the
+ * specified attribution tag is listed explicitly under the package. If the package contains
+ * all possible attribution tags, then nothing will be removed.
+ *
+ * @hide
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder remove(@NonNull String packageName,
+ @Nullable String attributionTag) {
+ ArraySet<String> tags = mPackageTags.get(packageName);
+ if (tags != null && tags.remove(attributionTag) && tags.isEmpty()) {
+ mPackageTags.remove(packageName);
+ }
+ return this;
+ }
+
+ /**
+ * Removes the specified package and set of attribution tags from the builder if and only if
+ * the specified set of attribution tags are listed explicitly under the package. If the
+ * package contains all possible attribution tags, then nothing will be removed.
+ *
+ * @hide
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder remove(@NonNull String packageName,
+ @NonNull Collection<String> attributionTags) {
+ if (attributionTags.isEmpty()) {
+ // the input is not allowed to specify a full set by passing in an empty collection
+ return this;
+ }
+
+ ArraySet<String> tags = mPackageTags.get(packageName);
+ if (tags != null && tags.removeAll(attributionTags) && tags.isEmpty()) {
+ mPackageTags.remove(packageName);
+ }
+ return this;
+ }
+
+ /**
+ * Removes the specified {@link PackageTagsList} from the builder. If a package contains all
+ * possible attribution tags, it will only be removed if the package in the removed
+ * {@link PackageTagsList} also contains all possible attribution tags.
+ *
+ * @hide
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder remove(@NonNull PackageTagsList packageTagsList) {
+ return remove(packageTagsList.mPackageTags);
+ }
+
+ /**
+ * Removes the given map of package to attribution tags to the builder. An empty set of
+ * attribution tags is interpreted to imply all attribution tags under that package. If a
+ * package contains all possible attribution tags, it will only be removed if the package in
+ * the removed map also contains all possible attribution tags.
+ *
+ * @hide
+ */
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public @NonNull Builder remove(@NonNull Map<String, ? extends Set<String>> packageTagsMap) {
+ for (Map.Entry<String, ? extends Set<String>> entry : packageTagsMap.entrySet()) {
+ Set<String> removedTags = entry.getValue();
+ if (removedTags.isEmpty()) {
+ // if removing the full set, drop the package completely
+ remove(entry.getKey());
+ } else {
+ remove(entry.getKey(), removedTags);
}
}
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index a90ed20..db3d13b 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -19,11 +19,18 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import android.util.proto.ProtoOutputStream;
import com.android.internal.os.PowerCalculator;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.io.PrintWriter;
+import java.util.Arrays;
/**
* Contains details of battery attribution data broken down to individual power drain types
@@ -36,9 +43,12 @@
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
private final double mConsumedPowerMah;
+ @NonNull
private final double[] mPowerComponentsMah;
+ @NonNull
private final long[] mUsageDurationsMs;
private final int mCustomPowerComponentCount;
+ @Nullable
private final byte[] mPowerModels;
// Not written to Parcel and must be explicitly restored during the parent object's unparceling
private String[] mCustomPowerComponentNames;
@@ -49,7 +59,7 @@
mPowerComponentsMah = builder.mPowerComponentsMah;
mUsageDurationsMs = builder.mUsageDurationsMs;
mConsumedPowerMah = builder.getTotalPower();
- mPowerModels = builder.mPowerModels;
+ mPowerModels = builder.getPowerModels();
}
PowerComponents(@NonNull Parcel source) {
@@ -146,9 +156,13 @@
}
}
+ public boolean hasPowerModels() {
+ return mPowerModels != null;
+ }
+
@BatteryConsumer.PowerModel
int getPowerModel(@BatteryConsumer.PowerComponent int component) {
- if (mPowerModels == null) {
+ if (!hasPowerModels()) {
throw new IllegalStateException(
"Power model IDs were not requested in the BatteryUsageStatsQuery");
}
@@ -294,10 +308,128 @@
return interestingData;
}
+ void writeToXml(TypedXmlSerializer serializer) throws IOException {
+ serializer.startTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS);
+ for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
+ componentId++) {
+ final double powerMah = getConsumedPower(componentId);
+ final long durationMs = getUsageDurationMillis(componentId);
+ if (powerMah == 0 && durationMs == 0) {
+ continue;
+ }
+
+ serializer.startTag(null, BatteryUsageStats.XML_TAG_COMPONENT);
+ serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, componentId);
+ if (powerMah != 0) {
+ serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, powerMah);
+ }
+ if (durationMs != 0) {
+ serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs);
+ }
+ if (mPowerModels != null) {
+ serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_MODEL,
+ mPowerModels[componentId]);
+ }
+ serializer.endTag(null, BatteryUsageStats.XML_TAG_COMPONENT);
+ }
+
+ final int customComponentEnd =
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + mCustomPowerComponentCount;
+ for (int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
+ componentId < customComponentEnd;
+ componentId++) {
+ final double powerMah = getConsumedPowerForCustomComponent(componentId);
+ final long durationMs = getUsageDurationForCustomComponentMillis(componentId);
+ if (powerMah == 0 && durationMs == 0) {
+ continue;
+ }
+
+ serializer.startTag(null, BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT);
+ serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_ID, componentId);
+ if (powerMah != 0) {
+ serializer.attributeDouble(null, BatteryUsageStats.XML_ATTR_POWER, powerMah);
+ }
+ if (durationMs != 0) {
+ serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_DURATION, durationMs);
+ }
+ serializer.endTag(null, BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT);
+ }
+
+ serializer.endTag(null, BatteryUsageStats.XML_TAG_POWER_COMPONENTS);
+ }
+
+
+ static void parseXml(TypedXmlPullParser parser, PowerComponents.Builder builder)
+ throws XmlPullParserException, IOException {
+ int eventType = parser.getEventType();
+ if (eventType != XmlPullParser.START_TAG || !parser.getName().equals(
+ BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) {
+ throw new XmlPullParserException("Invalid XML parser state");
+ }
+
+ while (!(eventType == XmlPullParser.END_TAG && parser.getName().equals(
+ BatteryUsageStats.XML_TAG_POWER_COMPONENTS))
+ && eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ switch (parser.getName()) {
+ case BatteryUsageStats.XML_TAG_COMPONENT: {
+ int componentId = -1;
+ double powerMah = 0;
+ long durationMs = 0;
+ int model = BatteryConsumer.POWER_MODEL_UNDEFINED;
+ for (int i = 0; i < parser.getAttributeCount(); i++) {
+ switch (parser.getAttributeName(i)) {
+ case BatteryUsageStats.XML_ATTR_ID:
+ componentId = parser.getAttributeInt(i);
+ break;
+ case BatteryUsageStats.XML_ATTR_POWER:
+ powerMah = parser.getAttributeDouble(i);
+ break;
+ case BatteryUsageStats.XML_ATTR_DURATION:
+ durationMs = parser.getAttributeLong(i);
+ break;
+ case BatteryUsageStats.XML_ATTR_MODEL:
+ model = parser.getAttributeInt(i);
+ break;
+ }
+ }
+ builder.setConsumedPower(componentId, powerMah, model);
+ builder.setUsageDurationMillis(componentId, durationMs);
+ break;
+ }
+ case BatteryUsageStats.XML_TAG_CUSTOM_COMPONENT: {
+ int componentId = -1;
+ double powerMah = 0;
+ long durationMs = 0;
+ for (int i = 0; i < parser.getAttributeCount(); i++) {
+ switch (parser.getAttributeName(i)) {
+ case BatteryUsageStats.XML_ATTR_ID:
+ componentId = parser.getAttributeInt(i);
+ break;
+ case BatteryUsageStats.XML_ATTR_POWER:
+ powerMah = parser.getAttributeDouble(i);
+ break;
+ case BatteryUsageStats.XML_ATTR_DURATION:
+ durationMs = parser.getAttributeLong(i);
+ break;
+ }
+ }
+ builder.setConsumedPowerForCustomComponent(componentId, powerMah);
+ builder.setUsageDurationForCustomComponentMillis(componentId, durationMs);
+ break;
+ }
+ }
+ }
+ eventType = parser.next();
+ }
+ }
+
/**
* Builder for PowerComponents.
*/
static final class Builder {
+ private static final byte POWER_MODEL_UNINITIALIZED = -1;
+
private final double[] mPowerComponentsMah;
private final String[] mCustomPowerComponentNames;
private final long[] mUsageDurationsMs;
@@ -311,6 +443,7 @@
mUsageDurationsMs = new long[powerComponentCount];
if (includePowerModels) {
mPowerModels = new byte[BatteryConsumer.POWER_COMPONENT_COUNT];
+ Arrays.fill(mPowerModels, POWER_MODEL_UNINITIALIZED);
} else {
mPowerModels = null;
}
@@ -412,12 +545,39 @@
return this;
}
- public void addPowerAndDuration(Builder other) {
+ public void addPowerAndDuration(PowerComponents.Builder other) {
+ addPowerAndDuration(other.mPowerComponentsMah, other.mUsageDurationsMs,
+ other.mPowerModels);
+ }
+
+ public void addPowerAndDuration(PowerComponents other) {
+ addPowerAndDuration(other.mPowerComponentsMah, other.mUsageDurationsMs,
+ other.mPowerModels);
+ }
+
+ private void addPowerAndDuration(double[] powerComponentsMah,
+ long[] usageDurationsMs, byte[] powerModels) {
+ if (mPowerComponentsMah.length != powerComponentsMah.length) {
+ throw new IllegalArgumentException(
+ "Number of power components does not match: " + powerComponentsMah.length
+ + ", expected: " + mPowerComponentsMah.length);
+ }
+
for (int i = mPowerComponentsMah.length - 1; i >= 0; i--) {
- mPowerComponentsMah[i] += other.mPowerComponentsMah[i];
+ mPowerComponentsMah[i] += powerComponentsMah[i];
}
for (int i = mUsageDurationsMs.length - 1; i >= 0; i--) {
- mUsageDurationsMs[i] += other.mUsageDurationsMs[i];
+ mUsageDurationsMs[i] += usageDurationsMs[i];
+ }
+ if (mPowerModels != null && powerModels != null) {
+ for (int i = mPowerModels.length - 1; i >= 0; i--) {
+ if (mPowerModels[i] == POWER_MODEL_UNINITIALIZED) {
+ mPowerModels[i] = powerModels[i];
+ } else if (mPowerModels[i] != powerModels[i]
+ && powerModels[i] != POWER_MODEL_UNINITIALIZED) {
+ mPowerModels[i] = BatteryConsumer.POWER_MODEL_UNDEFINED;
+ }
+ }
}
}
@@ -433,6 +593,19 @@
return totalPowerMah;
}
+ private byte[] getPowerModels() {
+ if (mPowerModels == null) {
+ return null;
+ }
+
+ byte[] powerModels = new byte[mPowerModels.length];
+ for (int i = mPowerModels.length - 1; i >= 0; i--) {
+ powerModels[i] = mPowerModels[i] != POWER_MODEL_UNINITIALIZED ? mPowerModels[i]
+ : BatteryConsumer.POWER_MODEL_UNDEFINED;
+ }
+ return powerModels;
+ }
+
/**
* Creates a read-only object out of the Builder values.
*/
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index fd8948c..0c3debb1 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -26,6 +26,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Objects;
@@ -254,11 +255,14 @@
* <p>This uses the first vibrator on the list as the default one for all hardware spec, but
* uses an intersection of all vibrators to decide the capabilities and effect/primitive
* support.
+ *
+ * @hide
*/
- private static class AllVibratorsInfo extends VibratorInfo {
+ @VisibleForTesting
+ public static class AllVibratorsInfo extends VibratorInfo {
private final VibratorInfo[] mVibratorInfos;
- AllVibratorsInfo(VibratorInfo[] vibrators) {
+ public AllVibratorsInfo(VibratorInfo[] vibrators) {
super(/* id= */ -1, capabilitiesIntersection(vibrators),
vibrators.length > 0 ? vibrators[0] : VibratorInfo.EMPTY_VIBRATOR_INFO);
mVibratorInfos = vibrators;
@@ -266,6 +270,9 @@
@Override
public int isEffectSupported(int effectId) {
+ if (mVibratorInfos.length == 0) {
+ return Vibrator.VIBRATION_EFFECT_SUPPORT_NO;
+ }
int supported = Vibrator.VIBRATION_EFFECT_SUPPORT_YES;
for (VibratorInfo info : mVibratorInfos) {
int effectSupported = info.isEffectSupported(effectId);
@@ -280,6 +287,9 @@
@Override
public boolean isPrimitiveSupported(int primitiveId) {
+ if (mVibratorInfos.length == 0) {
+ return false;
+ }
for (VibratorInfo info : mVibratorInfos) {
if (!info.isPrimitiveSupported(primitiveId)) {
return false;
@@ -288,6 +298,19 @@
return true;
}
+ @Override
+ public int getPrimitiveDuration(int primitiveId) {
+ int maxDuration = 0;
+ for (VibratorInfo info : mVibratorInfos) {
+ int duration = info.getPrimitiveDuration(primitiveId);
+ if (duration == 0) {
+ return 0;
+ }
+ maxDuration = Math.max(maxDuration, duration);
+ }
+ return maxDuration;
+ }
+
private static int capabilitiesIntersection(VibratorInfo[] infos) {
if (infos.length == 0) {
return 0;
diff --git a/core/java/android/os/SystemVibratorManager.java b/core/java/android/os/SystemVibratorManager.java
index c33603d..0416556 100644
--- a/core/java/android/os/SystemVibratorManager.java
+++ b/core/java/android/os/SystemVibratorManager.java
@@ -146,7 +146,7 @@
@Override
public void cancel() {
- cancelVibration(/* usageFilter= */ -1);
+ cancelVibration(VibrationAttributes.USAGE_FILTER_MATCH_ALL);
}
@Override
diff --git a/core/java/android/os/UidBatteryConsumer.java b/core/java/android/os/UidBatteryConsumer.java
index 16a6c76..bfc4f73 100644
--- a/core/java/android/os/UidBatteryConsumer.java
+++ b/core/java/android/os/UidBatteryConsumer.java
@@ -19,9 +19,16 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.internal.os.PowerCalculator;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -143,13 +150,65 @@
return 0;
}
+ /** Serializes this object to XML */
+ void writeToXml(TypedXmlSerializer serializer) throws IOException {
+ if (getConsumedPower() == 0) {
+ return;
+ }
+
+ serializer.startTag(null, BatteryUsageStats.XML_TAG_UID);
+ serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_UID, getUid());
+ if (!TextUtils.isEmpty(mPackageWithHighestDrain)) {
+ serializer.attribute(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE,
+ mPackageWithHighestDrain);
+ }
+ serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND,
+ mTimeInForegroundMs);
+ serializer.attributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND,
+ mTimeInBackgroundMs);
+ mPowerComponents.writeToXml(serializer);
+ serializer.endTag(null, BatteryUsageStats.XML_TAG_UID);
+ }
+
+ /** Parses an XML representation and populates the BatteryUsageStats builder */
+ static void createFromXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder)
+ throws XmlPullParserException, IOException {
+ final int uid = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_UID);
+ final UidBatteryConsumer.Builder consumerBuilder =
+ builder.getOrCreateUidBatteryConsumerBuilder(uid);
+
+ int eventType = parser.getEventType();
+ if (eventType != XmlPullParser.START_TAG
+ || !parser.getName().equals(BatteryUsageStats.XML_TAG_UID)) {
+ throw new XmlPullParserException("Invalid XML parser state");
+ }
+
+ consumerBuilder.setPackageWithHighestDrain(
+ parser.getAttributeValue(null, BatteryUsageStats.XML_ATTR_HIGHEST_DRAIN_PACKAGE));
+ consumerBuilder.setTimeInStateMs(STATE_FOREGROUND,
+ parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_FOREGROUND));
+ consumerBuilder.setTimeInStateMs(STATE_BACKGROUND,
+ parser.getAttributeLong(null, BatteryUsageStats.XML_ATTR_TIME_IN_BACKGROUND));
+ while (!(eventType == XmlPullParser.END_TAG
+ && parser.getName().equals(BatteryUsageStats.XML_TAG_UID))
+ && eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) {
+ PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder);
+ }
+ }
+ eventType = parser.next();
+ }
+ }
+
/**
* Builder for UidBatteryConsumer.
*/
public static final class Builder extends BaseBuilder<Builder> {
+ private static final String PACKAGE_NAME_UNINITIALIZED = "";
private final BatteryStats.Uid mBatteryStatsUid;
private final int mUid;
- private String mPackageWithHighestDrain;
+ private String mPackageWithHighestDrain = PACKAGE_NAME_UNINITIALIZED;
public long mTimeInForegroundMs;
public long mTimeInBackgroundMs;
private boolean mExcludeFromBatteryUsageStats;
@@ -161,8 +220,19 @@
mUid = batteryStatsUid.getUid();
}
+ public Builder(@NonNull String[] customPowerComponentNames, boolean includePowerModels,
+ int uid) {
+ super(customPowerComponentNames, includePowerModels);
+ mBatteryStatsUid = null;
+ mUid = uid;
+ }
+
@NonNull
public BatteryStats.Uid getBatteryStatsUid() {
+ if (mBatteryStatsUid == null) {
+ throw new IllegalStateException(
+ "UidBatteryConsumer.Builder was initialized without a BatteryStats.Uid");
+ }
return mBatteryStatsUid;
}
@@ -176,7 +246,7 @@
*/
@NonNull
public Builder setPackageWithHighestDrain(@Nullable String packageName) {
- mPackageWithHighestDrain = packageName;
+ mPackageWithHighestDrain = TextUtils.nullIfEmpty(packageName);
return this;
}
@@ -208,6 +278,30 @@
}
/**
+ * Adds power and usage duration from the supplied UidBatteryConsumer.
+ */
+ public Builder add(UidBatteryConsumer consumer) {
+ mPowerComponentsBuilder.addPowerAndDuration(consumer.mPowerComponents);
+ mTimeInBackgroundMs += consumer.mTimeInBackgroundMs;
+ mTimeInForegroundMs += consumer.mTimeInForegroundMs;
+
+ if (mPackageWithHighestDrain == PACKAGE_NAME_UNINITIALIZED) {
+ mPackageWithHighestDrain = consumer.mPackageWithHighestDrain;
+ } else if (!TextUtils.equals(mPackageWithHighestDrain,
+ consumer.mPackageWithHighestDrain)) {
+ // Consider combining two UidBatteryConsumers with this distribution
+ // of power drain between packages:
+ // (package1=100, package2=10) and (package1=100, package2=101).
+ // Since we don't know the actual power distribution between packages at this
+ // point, we have no way to correctly declare package1 as the winner.
+ // The naive logic of picking the consumer with the higher total consumed
+ // power would produce an incorrect result.
+ mPackageWithHighestDrain = null;
+ }
+ return this;
+ }
+
+ /**
* Returns true if this UidBatteryConsumer must be excluded from the
* BatteryUsageStats.
*/
@@ -220,6 +314,9 @@
*/
@NonNull
public UidBatteryConsumer build() {
+ if (mPackageWithHighestDrain == PACKAGE_NAME_UNINITIALIZED) {
+ mPackageWithHighestDrain = null;
+ }
return new UidBatteryConsumer(this);
}
}
diff --git a/core/java/android/os/UserBatteryConsumer.java b/core/java/android/os/UserBatteryConsumer.java
index 429d2c5..b508a8c 100644
--- a/core/java/android/os/UserBatteryConsumer.java
+++ b/core/java/android/os/UserBatteryConsumer.java
@@ -17,9 +17,15 @@
package android.os;
import android.annotation.NonNull;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
import com.android.internal.os.PowerCalculator;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -85,6 +91,42 @@
return 0;
}
+ /** Serializes this object to XML */
+ void writeToXml(TypedXmlSerializer serializer) throws IOException {
+ if (getConsumedPower() == 0) {
+ return;
+ }
+
+ serializer.startTag(null, BatteryUsageStats.XML_TAG_USER);
+ serializer.attributeInt(null, BatteryUsageStats.XML_ATTR_USER_ID, getUserId());
+ mPowerComponents.writeToXml(serializer);
+ serializer.endTag(null, BatteryUsageStats.XML_TAG_USER);
+ }
+
+ /** Parses an XML representation and populates the BatteryUsageStats builder */
+ static void createFromXml(TypedXmlPullParser parser, BatteryUsageStats.Builder builder)
+ throws XmlPullParserException, IOException {
+ final int userId = parser.getAttributeInt(null, BatteryUsageStats.XML_ATTR_USER_ID);
+ final UserBatteryConsumer.Builder consumerBuilder =
+ builder.getOrCreateUserBatteryConsumerBuilder(userId);
+
+ int eventType = parser.getEventType();
+ if (eventType != XmlPullParser.START_TAG
+ || !parser.getName().equals(BatteryUsageStats.XML_TAG_USER)) {
+ throw new XmlPullParserException("Invalid XML parser state");
+ }
+ while (!(eventType == XmlPullParser.END_TAG
+ && parser.getName().equals(BatteryUsageStats.XML_TAG_USER))
+ && eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ if (parser.getName().equals(BatteryUsageStats.XML_TAG_POWER_COMPONENTS)) {
+ PowerComponents.parseXml(parser, consumerBuilder.mPowerComponentsBuilder);
+ }
+ }
+ eventType = parser.next();
+ }
+ }
+
/**
* Builder for UserBatteryConsumer.
*/
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index c22224d..8709f07 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -26,6 +26,7 @@
import android.annotation.RequiresPermission;
import android.annotation.StringDef;
import android.annotation.SuppressAutoDoc;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -53,6 +54,7 @@
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.AndroidException;
+import android.util.ArraySet;
import android.view.WindowManager.LayoutParams;
import com.android.internal.R;
@@ -1322,6 +1324,24 @@
"disallow_camera_toggle";
/**
+ * This is really not a user restriction in the normal sense. This can't be set to a user,
+ * via UserManager nor via DevicePolicyManager. This is not even set in UserSettingsUtils.
+ * This is defined here purely for convenience within the settings app.
+ *
+ * TODO(b/191306258): Refactor the Settings app to remove the need for this field, and delete it
+ *
+ * Specifies whether biometrics are available to the user. This is used internally only,
+ * as a means of communications between biometric settings and
+ * {@link com.android.settingslib.enterprise.ActionDisabledByAdminControllerFactory}.
+ *
+ * @see {@link android.hardware.biometrics.ParentalControlsUtilsInternal}
+ * @see {@link com.android.settings.biometrics.ParentalControlsUtils}
+ *
+ * @hide
+ */
+ public static final String DISALLOW_BIOMETRIC = "disallow_biometric";
+
+ /**
* Application restriction key that is used to indicate the pending arrival
* of real restrictions for the app.
*
@@ -1415,6 +1435,7 @@
DISALLOW_MICROPHONE_TOGGLE,
DISALLOW_CAMERA_TOGGLE,
KEY_RESTRICTIONS_PENDING,
+ DISALLOW_BIOMETRIC,
})
@Retention(RetentionPolicy.SOURCE)
public @interface UserRestrictionKey {}
@@ -3202,6 +3223,33 @@
}
/**
+ * Returns the list of the system packages that would be installed on this type of user upon
+ * its creation.
+ *
+ * Returns {@code null} if all system packages would be installed.
+ *
+ * @hide
+ */
+ @TestApi
+ @SuppressLint("NullableCollection")
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS
+ })
+ public @Nullable Set<String> getPreInstallableSystemPackages(@NonNull String userType) {
+ try {
+ final String[] installableSystemPackages
+ = mService.getPreInstallableSystemPackages(userType);
+ if (installableSystemPackages == null) {
+ return null;
+ }
+ return new ArraySet<>(installableSystemPackages);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @hide
*
* Returns the preferred account name for user creation.
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index cec323f..43ea2e7 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -63,6 +63,11 @@
public @interface Usage{}
/**
+ * Vibration usage filter value to match all usages.
+ * @hide
+ */
+ public static final int USAGE_FILTER_MATCH_ALL = -1;
+ /**
* Vibration usage class value to use when the vibration usage class is unknown.
*/
public static final int USAGE_CLASS_UNKNOWN = 0x0;
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 12538e6..d7893e4 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -126,6 +126,7 @@
// The default vibration intensity level for ringtones.
@VibrationIntensity
private int mDefaultRingVibrationIntensity;
+ private float mHapticChannelMaxVibrationAmplitude;
/**
* @hide to prevent subclassing from outside of the framework
@@ -134,7 +135,7 @@
public Vibrator() {
mPackageName = ActivityThread.currentPackageName();
final Context ctx = ActivityThread.currentActivityThread().getSystemContext();
- loadVibrationIntensities(ctx);
+ loadVibrationConfig(ctx);
}
/**
@@ -142,22 +143,28 @@
*/
protected Vibrator(Context context) {
mPackageName = context.getOpPackageName();
- loadVibrationIntensities(context);
+ loadVibrationConfig(context);
}
- private void loadVibrationIntensities(Context context) {
+ private void loadVibrationConfig(Context context) {
mDefaultHapticFeedbackIntensity = loadDefaultIntensity(context,
com.android.internal.R.integer.config_defaultHapticFeedbackIntensity);
mDefaultNotificationVibrationIntensity = loadDefaultIntensity(context,
com.android.internal.R.integer.config_defaultNotificationVibrationIntensity);
mDefaultRingVibrationIntensity = loadDefaultIntensity(context,
com.android.internal.R.integer.config_defaultRingVibrationIntensity);
+ mHapticChannelMaxVibrationAmplitude = loadFloat(context,
+ com.android.internal.R.dimen.config_hapticChannelMaxVibrationAmplitude, 0);
}
private int loadDefaultIntensity(Context ctx, int resId) {
return ctx != null ? ctx.getResources().getInteger(resId) : VIBRATION_INTENSITY_MEDIUM;
}
+ private float loadFloat(Context ctx, int resId, float defaultValue) {
+ return ctx != null ? ctx.getResources().getFloat(resId) : defaultValue;
+ }
+
/** @hide */
protected VibratorInfo getInfo() {
return VibratorInfo.EMPTY_VIBRATOR_INFO;
@@ -297,6 +304,24 @@
}
/**
+ * Return the maximum amplitude the vibrator can play using the audio haptic channels.
+ *
+ * <p>This is a positive value, or {@link Float#NaN NaN} if it's unknown. If this returns a
+ * positive value <code>maxAmplitude</code>, then the signals from the haptic channels of audio
+ * tracks should be in the range <code>[-maxAmplitude, maxAmplitude]</code>.
+ *
+ * @return a positive value representing the maximum absolute value the device can play signals
+ * from audio haptic channels, or {@link Float#NaN NaN} if it's unknown.
+ * @hide
+ */
+ public float getHapticChannelMaximumAmplitude() {
+ if (mHapticChannelMaxVibrationAmplitude <= 0) {
+ return Float.NaN;
+ }
+ return mHapticChannelMaxVibrationAmplitude;
+ }
+
+ /**
* Configure an always-on haptics effect.
*
* @param alwaysOnId The board-specific always-on ID to configure.
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 597df08..486f9f1 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -51,8 +51,11 @@
private final SparseBooleanArray mSupportedEffects;
@Nullable
private final SparseBooleanArray mSupportedBraking;
- @Nullable
private final SparseIntArray mSupportedPrimitives;
+ private final int mPrimitiveDelayMax;
+ private final int mCompositionSizeMax;
+ private final int mPwlePrimitiveDurationMax;
+ private final int mPwleSizeMax;
private final float mQFactor;
private final FrequencyMapping mFrequencyMapping;
@@ -62,6 +65,10 @@
mSupportedEffects = in.readSparseBooleanArray();
mSupportedBraking = in.readSparseBooleanArray();
mSupportedPrimitives = in.readSparseIntArray();
+ mPrimitiveDelayMax = in.readInt();
+ mCompositionSizeMax = in.readInt();
+ mPwlePrimitiveDurationMax = in.readInt();
+ mPwleSizeMax = in.readInt();
mQFactor = in.readFloat();
mFrequencyMapping = in.readParcelable(VibratorInfo.class.getClassLoader());
}
@@ -69,48 +76,50 @@
/**
* Default constructor.
*
- * @param id The vibrator id.
- * @param capabilities All capability flags of the vibrator, defined in IVibrator.CAP_*.
- * @param supportedEffects All supported predefined effects, enum values from {@link
- * android.hardware.vibrator.Effect}.
- * @param supportedBraking All supported braking types, enum values from {@link Braking}.
- * @param supportedPrimitives All supported primitive effects, enum values from {@link
- * android.hardware.vibrator.CompositePrimitive}.
- * @param primitiveDurations A mapping of primitive durations, where indexes are enum values
- * from {@link android.hardware.vibrator.CompositePrimitive} and the
- * values are estimated durations in milliseconds.
- * @param qFactor The vibrator quality factor.
- * @param frequencyMapping The description of the vibrator supported frequencies and max
- * amplitude mappings.
+ * @param id The vibrator id.
+ * @param capabilities All capability flags of the vibrator, defined in
+ * IVibrator.CAP_*.
+ * @param supportedEffects All supported predefined effects, enum values from
+ * {@link android.hardware.vibrator.Effect}.
+ * @param supportedBraking All supported braking types, enum values from {@link
+ * Braking}.
+ * @param supportedPrimitives All supported primitive effects, key are enum values from
+ * {@link android.hardware.vibrator.CompositePrimitive} and
+ * values are estimated durations in milliseconds.
+ * @param primitiveDelayMax The maximum delay that can be set to a composition primitive
+ * in milliseconds.
+ * @param compositionSizeMax The maximum number of primitives supported by a composition.
+ * @param pwlePrimitiveDurationMax The maximum duration of a PWLE primitive in milliseconds.
+ * @param pwleSizeMax The maximum number of primitives supported by a PWLE
+ * composition.
+ * @param qFactor The vibrator quality factor.
+ * @param frequencyMapping The description of the vibrator supported frequencies and max
+ * amplitude mappings.
* @hide
*/
- public VibratorInfo(int id, long capabilities, int[] supportedEffects, int[] supportedBraking,
- int[] supportedPrimitives, int[] primitiveDurations, float qFactor,
- @NonNull FrequencyMapping frequencyMapping) {
+ public VibratorInfo(int id, long capabilities, @Nullable SparseBooleanArray supportedEffects,
+ @Nullable SparseBooleanArray supportedBraking,
+ @NonNull SparseIntArray supportedPrimitives, int primitiveDelayMax,
+ int compositionSizeMax, int pwlePrimitiveDurationMax, int pwleSizeMax,
+ float qFactor, @NonNull FrequencyMapping frequencyMapping) {
mId = id;
mCapabilities = capabilities;
- mSupportedEffects = toSparseBooleanArray(supportedEffects);
- mSupportedBraking = toSparseBooleanArray(supportedBraking);
- mSupportedPrimitives = toSparseIntArray(supportedPrimitives, primitiveDurations);
+ mSupportedEffects = supportedEffects == null ? null : supportedEffects.clone();
+ mSupportedBraking = supportedBraking == null ? null : supportedBraking.clone();
+ mSupportedPrimitives = supportedPrimitives.clone();
+ mPrimitiveDelayMax = primitiveDelayMax;
+ mCompositionSizeMax = compositionSizeMax;
+ mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax;
+ mPwleSizeMax = pwleSizeMax;
mQFactor = qFactor;
mFrequencyMapping = frequencyMapping;
}
protected VibratorInfo(int id, int capabilities, VibratorInfo baseVibrator) {
- mId = id;
- mCapabilities = capabilities;
- mSupportedEffects = baseVibrator.mSupportedEffects == null ? null :
- baseVibrator.mSupportedEffects.clone();
- mSupportedBraking = baseVibrator.mSupportedBraking == null ? null :
- baseVibrator.mSupportedBraking.clone();
- mSupportedPrimitives = baseVibrator.mSupportedPrimitives == null ? null :
- baseVibrator.mSupportedPrimitives.clone();
- mQFactor = baseVibrator.mQFactor;
- mFrequencyMapping = new FrequencyMapping(baseVibrator.mFrequencyMapping.mMinFrequencyHz,
- baseVibrator.mFrequencyMapping.mResonantFrequencyHz,
- baseVibrator.mFrequencyMapping.mFrequencyResolutionHz,
- baseVibrator.mFrequencyMapping.mSuggestedSafeRangeHz,
- baseVibrator.mFrequencyMapping.mMaxAmplitudes);
+ this(id, capabilities, baseVibrator.mSupportedEffects, baseVibrator.mSupportedBraking,
+ baseVibrator.mSupportedPrimitives, baseVibrator.mPrimitiveDelayMax,
+ baseVibrator.mCompositionSizeMax, baseVibrator.mPwlePrimitiveDurationMax,
+ baseVibrator.mPwleSizeMax, baseVibrator.mQFactor, baseVibrator.mFrequencyMapping);
}
@Override
@@ -120,6 +129,10 @@
dest.writeSparseBooleanArray(mSupportedEffects);
dest.writeSparseBooleanArray(mSupportedBraking);
dest.writeSparseIntArray(mSupportedPrimitives);
+ dest.writeInt(mPrimitiveDelayMax);
+ dest.writeInt(mCompositionSizeMax);
+ dest.writeInt(mPwlePrimitiveDurationMax);
+ dest.writeInt(mPwleSizeMax);
dest.writeFloat(mQFactor);
dest.writeParcelable(mFrequencyMapping, flags);
}
@@ -138,24 +151,23 @@
return false;
}
VibratorInfo that = (VibratorInfo) o;
- if (mSupportedPrimitives == null || that.mSupportedPrimitives == null) {
- if (mSupportedPrimitives != that.mSupportedPrimitives) {
+ int supportedPrimitivesCount = mSupportedPrimitives.size();
+ if (supportedPrimitivesCount != that.mSupportedPrimitives.size()) {
+ return false;
+ }
+ for (int i = 0; i < supportedPrimitivesCount; i++) {
+ if (mSupportedPrimitives.keyAt(i) != that.mSupportedPrimitives.keyAt(i)) {
return false;
}
- } else {
- if (mSupportedPrimitives.size() != that.mSupportedPrimitives.size()) {
+ if (mSupportedPrimitives.valueAt(i) != that.mSupportedPrimitives.valueAt(i)) {
return false;
}
- for (int i = 0; i < mSupportedPrimitives.size(); i++) {
- if (mSupportedPrimitives.keyAt(i) != that.mSupportedPrimitives.keyAt(i)) {
- return false;
- }
- if (mSupportedPrimitives.valueAt(i) != that.mSupportedPrimitives.valueAt(i)) {
- return false;
- }
- }
}
return mId == that.mId && mCapabilities == that.mCapabilities
+ && mPrimitiveDelayMax == that.mPrimitiveDelayMax
+ && mCompositionSizeMax == that.mCompositionSizeMax
+ && mPwlePrimitiveDurationMax == that.mPwlePrimitiveDurationMax
+ && mPwleSizeMax == that.mPwleSizeMax
&& Objects.equals(mSupportedEffects, that.mSupportedEffects)
&& Objects.equals(mSupportedBraking, that.mSupportedBraking)
&& Objects.equals(mQFactor, that.mQFactor)
@@ -166,11 +178,9 @@
public int hashCode() {
int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
mQFactor, mFrequencyMapping);
- if (mSupportedPrimitives != null) {
- for (int i = 0; i < mSupportedPrimitives.size(); i++) {
- hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
- hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
- }
+ for (int i = 0; i < mSupportedPrimitives.size(); i++) {
+ hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
+ hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
}
return hashCode;
}
@@ -184,6 +194,10 @@
+ ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames())
+ ", mSupportedBraking=" + Arrays.toString(getSupportedBrakingNames())
+ ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames())
+ + ", mPrimitiveDelayMax=" + mPrimitiveDelayMax
+ + ", mCompositionSizeMax=" + mCompositionSizeMax
+ + ", mPwlePrimitiveDurationMax=" + mPwlePrimitiveDurationMax
+ + ", mPwleSizeMax=" + mPwleSizeMax
+ ", mQFactor=" + mQFactor
+ ", mFrequencyMapping=" + mFrequencyMapping
+ '}';
@@ -247,7 +261,7 @@
*/
public boolean isPrimitiveSupported(
@VibrationEffect.Composition.PrimitiveType int primitiveId) {
- return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS) && mSupportedPrimitives != null
+ return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)
&& (mSupportedPrimitives.indexOfKey(primitiveId) >= 0);
}
@@ -260,7 +274,43 @@
*/
public int getPrimitiveDuration(
@VibrationEffect.Composition.PrimitiveType int primitiveId) {
- return mSupportedPrimitives != null ? mSupportedPrimitives.get(primitiveId) : 0;
+ return mSupportedPrimitives.get(primitiveId);
+ }
+
+ /**
+ * Query the maximum delay supported for a primitive in a composed effect.
+ *
+ * @return The max delay in milliseconds, or zero if unlimited.
+ */
+ public int getPrimitiveDelayMax() {
+ return mPrimitiveDelayMax;
+ }
+
+ /**
+ * Query the maximum number of primitives supported in a composed effect.
+ *
+ * @return The max number of primitives supported, or zero if unlimited.
+ */
+ public int getCompositionSizeMax() {
+ return mCompositionSizeMax;
+ }
+
+ /**
+ * Query the maximum duration supported for a primitive in a PWLE composition.
+ *
+ * @return The max duration in milliseconds, or zero if unlimited.
+ */
+ public int getPwlePrimitiveDurationMax() {
+ return mPwlePrimitiveDurationMax;
+ }
+
+ /**
+ * Query the maximum number of primitives supported in a PWLE composition.
+ *
+ * @return The max number of primitives supported, or zero if unlimited.
+ */
+ public int getPwleSizeMax() {
+ return mPwleSizeMax;
}
/**
@@ -408,52 +458,15 @@
}
private String[] getSupportedPrimitivesNames() {
- if (mSupportedPrimitives == null) {
- return new String[0];
- }
- String[] names = new String[mSupportedPrimitives.size()];
- for (int i = 0; i < mSupportedPrimitives.size(); i++) {
+ int supportedPrimitivesCount = mSupportedPrimitives.size();
+ String[] names = new String[supportedPrimitivesCount];
+ for (int i = 0; i < supportedPrimitivesCount; i++) {
names[i] = VibrationEffect.Composition.primitiveToString(mSupportedPrimitives.keyAt(i));
}
return names;
}
/**
- * Create a {@link SparseBooleanArray} from given {@code supportedKeys} where each key is mapped
- * to {@code true}.
- */
- @Nullable
- private static SparseBooleanArray toSparseBooleanArray(int[] supportedKeys) {
- if (supportedKeys == null) {
- return null;
- }
- SparseBooleanArray array = new SparseBooleanArray();
- for (int key : supportedKeys) {
- array.put(key, true);
- }
- return array;
- }
-
- /**
- * Create a {@link SparseIntArray} from given {@code supportedKeys} where each key is mapped
- * to the value indexed by it.
- *
- * <p>If {@code values} is null or does not contain a given key as a index, then zero is stored
- * to the sparse array so it can still be used to query the supported keys.
- */
- @Nullable
- private static SparseIntArray toSparseIntArray(int[] supportedKeys, int[] values) {
- if (supportedKeys == null) {
- return null;
- }
- SparseIntArray array = new SparseIntArray();
- for (int key : supportedKeys) {
- array.put(key, (values == null || key >= values.length) ? 0 : values[key]);
- }
- return array;
- }
-
- /**
* Describes how frequency should be mapped to absolute values for a specific {@link Vibrator}.
*
* <p>This mapping is defined by the following parameters:
@@ -675,11 +688,14 @@
/** @hide */
public static final class Builder {
private final int mId;
- private int mCapabilities = 0;
- private int[] mSupportedEffects = null;
- private int[] mSupportedBraking = null;
- private int[] mSupportedPrimitives = null;
- private int[] mPrimitiveDurations = new int[0];
+ private long mCapabilities;
+ private SparseBooleanArray mSupportedEffects;
+ private SparseBooleanArray mSupportedBraking;
+ private SparseIntArray mSupportedPrimitives = new SparseIntArray();
+ private int mPrimitiveDelayMax;
+ private int mCompositionSizeMax;
+ private int mPwlePrimitiveDurationMax;
+ private int mPwleSizeMax;
private float mQFactor = Float.NaN;
private FrequencyMapping mFrequencyMapping =
new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
@@ -691,7 +707,7 @@
/** Configure the vibrator capabilities with a combination of IVibrator.CAP_* values. */
@NonNull
- public Builder setCapabilities(int capabilities) {
+ public Builder setCapabilities(long capabilities) {
mCapabilities = capabilities;
return this;
}
@@ -699,34 +715,49 @@
/** Configure the effects supported with {@link android.hardware.vibrator.Effect} values. */
@NonNull
public Builder setSupportedEffects(int... supportedEffects) {
- mSupportedEffects = supportedEffects;
+ mSupportedEffects = toSparseBooleanArray(supportedEffects);
return this;
}
/** Configure braking supported with {@link android.hardware.vibrator.Braking} values. */
@NonNull
public Builder setSupportedBraking(int... supportedBraking) {
- mSupportedBraking = supportedBraking;
+ mSupportedBraking = toSparseBooleanArray(supportedBraking);
return this;
}
- /**
- * Configure the primitives supported with
- * {@link android.hardware.vibrator.CompositePrimitive} values.
- */
+ /** Configure maximum duration, in milliseconds, of a PWLE primitive. */
@NonNull
- public Builder setSupportedPrimitives(int... supportedPrimitives) {
- mSupportedPrimitives = supportedPrimitives;
+ public Builder setPwlePrimitiveDurationMax(int pwlePrimitiveDurationMax) {
+ mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax;
+ return this;
+ }
+
+ /** Configure maximum number of primitives supported in a single PWLE composed effect. */
+ @NonNull
+ public Builder setPwleSizeMax(int pwleSizeMax) {
+ mPwleSizeMax = pwleSizeMax;
return this;
}
/** Configure the duration of a {@link android.hardware.vibrator.CompositePrimitive}. */
@NonNull
- public Builder setPrimitiveDuration(int primitiveId, int duration) {
- if (mPrimitiveDurations.length <= primitiveId) {
- mPrimitiveDurations = Arrays.copyOf(mPrimitiveDurations, primitiveId + 1);
- }
- mPrimitiveDurations[primitiveId] = duration;
+ public Builder setSupportedPrimitive(int primitiveId, int duration) {
+ mSupportedPrimitives.put(primitiveId, duration);
+ return this;
+ }
+
+ /** Configure maximum delay, in milliseconds, supported in a composed effect primitive. */
+ @NonNull
+ public Builder setPrimitiveDelayMax(int primitiveDelayMax) {
+ mPrimitiveDelayMax = primitiveDelayMax;
+ return this;
+ }
+
+ /** Configure maximum number of primitives supported in a single composed effect. */
+ @NonNull
+ public Builder setCompositionSizeMax(int compositionSizeMax) {
+ mCompositionSizeMax = compositionSizeMax;
return this;
}
@@ -748,7 +779,25 @@
@NonNull
public VibratorInfo build() {
return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
- mSupportedPrimitives, mPrimitiveDurations, mQFactor, mFrequencyMapping);
+ mSupportedPrimitives, mPrimitiveDelayMax, mCompositionSizeMax,
+ mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyMapping);
+ }
+
+ /**
+ * Create a {@link SparseBooleanArray} from given {@code supportedKeys} where each key is
+ * mapped
+ * to {@code true}.
+ */
+ @Nullable
+ private static SparseBooleanArray toSparseBooleanArray(int[] supportedKeys) {
+ if (supportedKeys == null) {
+ return null;
+ }
+ SparseBooleanArray array = new SparseBooleanArray();
+ for (int key : supportedKeys) {
+ array.put(key, true);
+ }
+ return array;
}
}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index c5d0cd4..4ef0e6e 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -908,9 +908,32 @@
*/
public static boolean shouldShowPackageForIndicatorCached(@NonNull Context context,
@NonNull String packageName) {
- if (SYSTEM_PKG.equals(packageName)) {
- return false;
+ return !getIndicatorExemptedPackages(context).contains(packageName);
+ }
+
+ /**
+ * Get the list of packages that are not shown by the indicators. Only a select few roles, and
+ * the system app itself, are hidden. These values are updated at most every 15 seconds.
+ * @hide
+ */
+ public static Set<String> getIndicatorExemptedPackages(@NonNull Context context) {
+ updateIndicatorExemptedPackages(context);
+ ArraySet<String> pkgNames = new ArraySet<>();
+ pkgNames.add(SYSTEM_PKG);
+ for (int i = 0; i < INDICATOR_EXEMPTED_PACKAGES.length; i++) {
+ String exemptedPackage = INDICATOR_EXEMPTED_PACKAGES[i];
+ if (exemptedPackage != null) {
+ pkgNames.add(exemptedPackage);
+ }
}
+ return pkgNames;
+ }
+
+ /**
+ * Update the cached indicator exempted packages
+ * @hide
+ */
+ public static void updateIndicatorExemptedPackages(@NonNull Context context) {
long now = SystemClock.elapsedRealtime();
if (sLastIndicatorUpdateTime == -1
|| (now - sLastIndicatorUpdateTime) > EXEMPTED_INDICATOR_ROLE_UPDATE_FREQUENCY_MS) {
@@ -919,14 +942,6 @@
INDICATOR_EXEMPTED_PACKAGES[i] = context.getString(EXEMPTED_ROLES[i]);
}
}
- for (int i = 0; i < EXEMPTED_ROLES.length; i++) {
- String exemptedPackage = INDICATOR_EXEMPTED_PACKAGES[i];
- if (exemptedPackage != null && exemptedPackage.equals(packageName)) {
- return false;
- }
- }
-
- return true;
}
/**
* Gets the list of packages that have permissions that specified
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index d4e548e..791764b 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -410,7 +410,9 @@
int usageAttr = usage.getPackageIdHash();
// If this usage has a proxy, but is not a proxy, it is the end of a chain.
- if (!proxies.containsKey(usageAttr) && usage.proxy != null) {
+ // TODO remove once camera converted
+ if (!proxies.containsKey(usageAttr) && usage.proxy != null
+ && !usage.op.equals(OPSTR_RECORD_AUDIO)) {
proxyLabels.put(usage, new ArrayList<>());
proxyPackages.add(usage.getPackageIdHash());
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 36bc3e7..cb87653 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6661,6 +6661,20 @@
public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category.";
/**
+ * Whether or not compress blocks should be released on install.
+ * <p>The setting only determines if the platform will attempt to release
+ * compress blocks; it does not guarantee that the files will have their
+ * compress blocks released. Compression is currently only supported on
+ * some f2fs filesystems.
+ * <p>
+ * Type: int (0 for false, 1 for true)
+ *
+ * @hide
+ */
+ public static final String RELEASE_COMPRESS_BLOCKS_ON_INSTALL =
+ "release_compress_blocks_on_install";
+
+ /**
* List of input methods that are currently enabled. This is a string
* containing the IDs of all enabled input methods, each ID separated
* by ':'.
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 0b11aeb..3b4f7e2 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -124,7 +124,6 @@
public static final int KM_TAG_DEVICE_UNIQUE_ATTESTATION =
Tag.DEVICE_UNIQUE_ATTESTATION; // KM_BOOL | 720;
- public static final int KM_TAG_ASSOCIATED_DATA = Tag.ASSOCIATED_DATA; // KM_BYTES | 1000;
public static final int KM_TAG_NONCE = Tag.NONCE; // KM_BYTES | 1001;
public static final int KM_TAG_MAC_LENGTH = Tag.MAC_LENGTH; // KM_UINT | 1003;
public static final int KM_TAG_RESET_SINCE_ID_ROTATION =
diff --git a/core/java/android/service/contentcapture/ActivityEvent.java b/core/java/android/service/contentcapture/ActivityEvent.java
index 1188a3f..74a7355 100644
--- a/core/java/android/service/contentcapture/ActivityEvent.java
+++ b/core/java/android/service/contentcapture/ActivityEvent.java
@@ -55,12 +55,25 @@
*/
public static final int TYPE_ACTIVITY_DESTROYED = Event.ACTIVITY_DESTROYED;
+ /**
+ * TODO: change to public field.
+ * The activity was started.
+ *
+ * <p>There are some reason, ACTIVITY_START cannot be added into UsageStats. We don't depend on
+ * UsageEvents for Activity start.
+ * </p>
+ *
+ * @hide
+ */
+ public static final int TYPE_ACTIVITY_STARTED = 10000;
+
/** @hide */
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_ACTIVITY_RESUMED,
TYPE_ACTIVITY_PAUSED,
TYPE_ACTIVITY_STOPPED,
- TYPE_ACTIVITY_DESTROYED
+ TYPE_ACTIVITY_DESTROYED,
+ TYPE_ACTIVITY_STARTED
})
@Retention(RetentionPolicy.SOURCE)
public @interface ActivityEventType{}
@@ -86,7 +99,8 @@
* Gets the event type.
*
* @return either {@link #TYPE_ACTIVITY_RESUMED}, {@value #TYPE_ACTIVITY_PAUSED},
- * {@value #TYPE_ACTIVITY_STOPPED}, or {@value #TYPE_ACTIVITY_DESTROYED}.
+ * {@value #TYPE_ACTIVITY_STOPPED}, {@value #TYPE_ACTIVITY_DESTROYED} or 10000 if the Activity
+ * was started.
*/
@ActivityEventType
public int getEventType() {
@@ -104,6 +118,8 @@
return "ACTIVITY_STOPPED";
case TYPE_ACTIVITY_DESTROYED:
return "ACTIVITY_DESTROYED";
+ case TYPE_ACTIVITY_STARTED:
+ return "ACTIVITY_STARTED";
default:
return "UKNOWN_TYPE: " + type;
}
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 863d71f..8e4a68e 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -434,8 +434,11 @@
public Context getPackageContext(Context context) {
if (mContext == null) {
try {
- mContext = context.createPackageContextAsUser(pkg, Context.CONTEXT_RESTRICTED, user,
- PackageManager.MATCH_UNINSTALLED_PACKAGES);
+ ApplicationInfo ai = context.getPackageManager()
+ .getApplicationInfoAsUser(pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES,
+ getUserId());
+ mContext = context.createApplicationContext(ai,
+ Context.CONTEXT_RESTRICTED);
} catch (PackageManager.NameNotFoundException e) {
mContext = null;
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 12d9055..ff69281 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -50,6 +50,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
+import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -162,6 +163,7 @@
private static final String RULE_ATT_ENABLED = "enabled";
private static final String RULE_ATT_SNOOZING = "snoozing";
private static final String RULE_ATT_NAME = "name";
+ private static final String RULE_ATT_PKG = "pkg";
private static final String RULE_ATT_COMPONENT = "component";
private static final String RULE_ATT_CONFIG_ACTIVITY = "configActivity";
private static final String RULE_ATT_ZEN = "zen";
@@ -671,11 +673,11 @@
rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
rt.configurationActivity = safeComponentName(parser, RULE_ATT_CONFIG_ACTIVITY);
- rt.pkg = (rt.component != null)
- ? rt.component.getPackageName()
- : (rt.configurationActivity != null)
- ? rt.configurationActivity.getPackageName()
- : null;
+ rt.pkg = XmlUtils.readStringAttribute(parser, RULE_ATT_PKG);
+ if (rt.pkg == null) {
+ // backfill from component, if present. configActivity is not safe to backfill from
+ rt.pkg = rt.component != null ? rt.component.getPackageName() : null;
+ }
rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0);
rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER);
rt.condition = readConditionXml(parser);
@@ -697,6 +699,9 @@
out.attribute(null, RULE_ATT_NAME, rule.name);
}
out.attributeInt(null, RULE_ATT_ZEN, rule.zenMode);
+ if (rule.pkg != null) {
+ out.attribute(null, RULE_ATT_PKG, rule.pkg);
+ }
if (rule.component != null) {
out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString());
}
diff --git a/core/java/android/service/translation/TranslationService.java b/core/java/android/service/translation/TranslationService.java
index e1d4a56..93c006a 100644
--- a/core/java/android/service/translation/TranslationService.java
+++ b/core/java/android/service/translation/TranslationService.java
@@ -361,6 +361,11 @@
new Consumer<Set<TranslationCapability>>() {
@Override
public void accept(Set<TranslationCapability> values) {
+ if (!isValidCapabilities(sourceFormat, targetFormat, values)) {
+ throw new IllegalStateException("Invalid capabilities and "
+ + "format compatibility");
+ }
+
final ArraySet<TranslationCapability> capabilities = new ArraySet<>(values);
final Bundle bundle = new Bundle();
bundle.putParcelableArray(TranslationManager.EXTRA_CAPABILITIES,
@@ -369,4 +374,23 @@
}
});
}
+
+ /**
+ * Helper method to validate capabilities and format compatibility.
+ */
+ private boolean isValidCapabilities(@TranslationSpec.DataFormat int sourceFormat,
+ @TranslationSpec.DataFormat int targetFormat, Set<TranslationCapability> capabilities) {
+ if (sourceFormat != TranslationSpec.DATA_FORMAT_TEXT
+ && targetFormat != TranslationSpec.DATA_FORMAT_TEXT) {
+ return true;
+ }
+
+ for (TranslationCapability capability : capabilities) {
+ if (capability.getState() == TranslationCapability.STATE_REMOVED_AND_AVAILABLE) {
+ return false;
+ }
+ }
+
+ return true;
+ }
}
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 67b97ce..4137416 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -263,6 +263,7 @@
private static final int MSG_DETECTION_RESUME = 5;
private static final int MSG_HOTWORD_REJECTED = 6;
private static final int MSG_HOTWORD_STATUS_REPORTED = 7;
+ private static final int MSG_PROCESS_RESTARTED = 8;
private final String mText;
private final Locale mLocale;
@@ -1212,6 +1213,12 @@
message.arg1 = status;
message.sendToTarget();
}
+
+ @Override
+ public void onProcessRestarted() {
+ Slog.i(TAG, "onProcessRestarted");
+ mHandler.sendEmptyMessage(MSG_PROCESS_RESTARTED);
+ }
}
class MyHandler extends Handler {
@@ -1246,6 +1253,9 @@
case MSG_HOTWORD_STATUS_REPORTED:
mExternalCallback.onHotwordDetectionServiceInitialized(msg.arg1);
break;
+ case MSG_PROCESS_RESTARTED:
+ mExternalCallback.onHotwordDetectionServiceRestarted();
+ break;
default:
super.handleMessage(msg);
}
diff --git a/core/java/android/service/voice/HotwordDetectedResult.java b/core/java/android/service/voice/HotwordDetectedResult.java
index 846f2f9..315392b 100644
--- a/core/java/android/service/voice/HotwordDetectedResult.java
+++ b/core/java/android/service/voice/HotwordDetectedResult.java
@@ -20,11 +20,18 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.content.res.Resources;
+import android.media.AudioRecord;
import android.media.MediaSyncEvent;
+import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
+import com.android.internal.R;
import com.android.internal.util.DataClass;
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
/**
* Represents a result supporting the hotword detection.
@@ -187,16 +194,20 @@
return new PersistableBundle();
}
+ private static int sMaxBundleSize = -1;
+
/**
* Returns the maximum byte size of the information contained in the bundle.
*
- * <p>The total size will be calculated as a sum of byte sizes over all bundle keys.
- *
- * <p>For example, for a bundle containing a single key: {@code "example_key" -> 42.0f}, the
- * bundle size will be {@code 11 + Float.BYTES = 15} bytes.
+ * <p>The total size will be calculated by how much bundle data should be written into the
+ * Parcel.
*/
public static int getMaxBundleSize() {
- return 50;
+ if (sMaxBundleSize < 0) {
+ sMaxBundleSize = Resources.getSystem().getInteger(
+ R.integer.config_hotwordDetectedResultMaxBundleSize);
+ }
+ return sMaxBundleSize;
}
/**
@@ -212,6 +223,34 @@
return mMediaSyncEvent;
}
+ /**
+ * Returns how many bytes should be written into the Parcel
+ *
+ * @hide
+ */
+ public static int getParcelableSize(@NonNull Parcelable parcelable) {
+ final Parcel p = Parcel.obtain();
+ parcelable.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ final int size = p.dataSize();
+ p.recycle();
+ return size;
+ }
+
+ private void onConstructed() {
+ Preconditions.checkArgumentInRange(mScore, 0, getMaxScore(), "score");
+ Preconditions.checkArgumentInRange(mPersonalizedScore, 0, getMaxScore(),
+ "personalizedScore");
+ Preconditions.checkArgumentInRange(mHotwordPhraseId, 0, getMaxHotwordPhraseId(),
+ "hotwordPhraseId");
+ Preconditions.checkArgumentInRange((long) mHotwordDurationMillis, 0,
+ AudioRecord.getMaxSharedAudioHistoryMillis(), "hotwordDurationMillis");
+ if (!mExtras.isEmpty()) {
+ Preconditions.checkArgumentInRange(getParcelableSize(mExtras), 0, getMaxBundleSize(),
+ "extras");
+ }
+ }
+
// Code below generated by codegen v1.0.23.
@@ -290,7 +329,7 @@
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mExtras);
- // onConstructed(); // You can define this method to get a callback
+ onConstructed();
}
/**
@@ -422,7 +461,7 @@
//noinspection PointlessBooleanExpression
return true
&& mConfidenceLevel == that.mConfidenceLevel
- && java.util.Objects.equals(mMediaSyncEvent, that.mMediaSyncEvent)
+ && Objects.equals(mMediaSyncEvent, that.mMediaSyncEvent)
&& mHotwordOffsetMillis == that.mHotwordOffsetMillis
&& mHotwordDurationMillis == that.mHotwordDurationMillis
&& mAudioChannel == that.mAudioChannel
@@ -430,7 +469,7 @@
&& mScore == that.mScore
&& mPersonalizedScore == that.mPersonalizedScore
&& mHotwordPhraseId == that.mHotwordPhraseId
- && java.util.Objects.equals(mExtras, that.mExtras);
+ && Objects.equals(mExtras, that.mExtras);
}
@Override
@@ -441,7 +480,7 @@
int _hash = 1;
_hash = 31 * _hash + mConfidenceLevel;
- _hash = 31 * _hash + java.util.Objects.hashCode(mMediaSyncEvent);
+ _hash = 31 * _hash + Objects.hashCode(mMediaSyncEvent);
_hash = 31 * _hash + mHotwordOffsetMillis;
_hash = 31 * _hash + mHotwordDurationMillis;
_hash = 31 * _hash + mAudioChannel;
@@ -449,13 +488,13 @@
_hash = 31 * _hash + mScore;
_hash = 31 * _hash + mPersonalizedScore;
_hash = 31 * _hash + mHotwordPhraseId;
- _hash = 31 * _hash + java.util.Objects.hashCode(mExtras);
+ _hash = 31 * _hash + Objects.hashCode(mExtras);
return _hash;
}
@Override
@DataClass.Generated.Member
- public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
@@ -481,7 +520,7 @@
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
- /* package-private */ HotwordDetectedResult(@NonNull android.os.Parcel in) {
+ /* package-private */ HotwordDetectedResult(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
@@ -512,7 +551,7 @@
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mExtras);
- // onConstructed(); // You can define this method to get a callback
+ onConstructed();
}
@DataClass.Generated.Member
@@ -524,7 +563,7 @@
}
@Override
- public HotwordDetectedResult createFromParcel(@NonNull android.os.Parcel in) {
+ public HotwordDetectedResult createFromParcel(@NonNull Parcel in) {
return new HotwordDetectedResult(in);
}
};
@@ -745,10 +784,10 @@
}
@DataClass.Generated(
- time = 1621943150502L,
+ time = 1624361647985L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/voice/HotwordDetectedResult.java",
- inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ inputSignatures = "public static final int CONFIDENCE_LEVEL_NONE\npublic static final int CONFIDENCE_LEVEL_LOW\npublic static final int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM\npublic static final int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final int CONFIDENCE_LEVEL_HIGH\npublic static final int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final int HOTWORD_OFFSET_UNSET\npublic static final int AUDIO_CHANNEL_UNSET\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate int mHotwordOffsetMillis\nprivate int mHotwordDurationMillis\nprivate int mAudioChannel\nprivate boolean mHotwordDetectionPersonalized\nprivate final int mScore\nprivate final int mPersonalizedScore\nprivate final int mHotwordPhraseId\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static int sMaxBundleSize\nprivate static int defaultConfidenceLevel()\nprivate static int defaultScore()\nprivate static int defaultPersonalizedScore()\npublic static int getMaxScore()\nprivate static int defaultHotwordPhraseId()\npublic static int getMaxHotwordPhraseId()\nprivate static android.os.PersistableBundle defaultExtras()\npublic static int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static int getParcelableSize(android.os.Parcelable)\nprivate void onConstructed()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index b66d93d..567ee2f 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -76,6 +76,7 @@
private static final boolean DBG = true;
private static final long UPDATE_TIMEOUT_MILLIS = 5000;
+
/** @hide */
public static final String KEY_INITIALIZATION_STATUS = "initialization_status";
@@ -291,9 +292,7 @@
@Nullable PersistableBundle options,
@Nullable SharedMemory sharedMemory,
@DurationMillisLong long callbackTimeoutMillis,
- @Nullable IntConsumer statusCallback) {
- // TODO: Handle the unimplemented case by throwing?
- }
+ @Nullable IntConsumer statusCallback) {}
/**
* Called when the {@link VoiceInteractionService} requests that this service
@@ -390,6 +389,14 @@
*/
public void onDetected(@NonNull HotwordDetectedResult result) {
requireNonNull(result);
+ final PersistableBundle persistableBundle = result.getExtras();
+ if (!persistableBundle.isEmpty() && HotwordDetectedResult.getParcelableSize(
+ persistableBundle) > HotwordDetectedResult.getMaxBundleSize()) {
+ throw new IllegalArgumentException(
+ "The bundle size of result is larger than max bundle size ("
+ + HotwordDetectedResult.getMaxBundleSize()
+ + ") of HotwordDetectedResult");
+ }
try {
mRemoteCallback.onDetected(result);
} catch (RemoteException e) {
diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java
index 204e7df..fb540b1 100644
--- a/core/java/android/service/voice/SoftwareHotwordDetector.java
+++ b/core/java/android/service/voice/SoftwareHotwordDetector.java
@@ -122,7 +122,7 @@
this.mCallback = callback;
}
- /** TODO: onDetected */
+ /** Called when the detected result is valid. */
@Override
public void onDetected(
@Nullable HotwordDetectedResult hotwordDetectedResult,
@@ -150,33 +150,45 @@
public void onKeyphraseDetected(
SoundTrigger.KeyphraseRecognitionEvent recognitionEvent,
HotwordDetectedResult result) {
-
+ if (DEBUG) {
+ Slog.i(TAG, "Ignored #onKeyphraseDetected event");
+ }
}
@Override
public void onGenericSoundTriggerDetected(
SoundTrigger.GenericRecognitionEvent recognitionEvent) throws RemoteException {
-
+ if (DEBUG) {
+ Slog.i(TAG, "Ignored #onGenericSoundTriggerDetected event");
+ }
}
@Override
public void onRejected(HotwordRejectedResult result) throws RemoteException {
-
+ if (DEBUG) {
+ Slog.i(TAG, "Ignored #onRejected event");
+ }
}
@Override
public void onError(int status) throws RemoteException {
-
+ if (DEBUG) {
+ Slog.i(TAG, "Ignored #onError (" + status + ") event");
+ }
}
@Override
public void onRecognitionPaused() throws RemoteException {
-
+ if (DEBUG) {
+ Slog.i(TAG, "Ignored #onRecognitionPaused event");
+ }
}
@Override
public void onRecognitionResumed() throws RemoteException {
-
+ if (DEBUG) {
+ Slog.i(TAG, "Ignored #onRecognitionResumed event");
+ }
}
@Override
@@ -187,6 +199,14 @@
mCallback,
status));
}
+
+ @Override
+ public void onProcessRestarted() throws RemoteException {
+ Slog.v(TAG, "onProcessRestarted()");
+ mHandler.sendMessage(obtainMessage(
+ HotwordDetector.Callback::onHotwordDetectionServiceRestarted,
+ mCallback));
+ }
}
/** @hide */
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 05ed75a..a88d5b9 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1570,6 +1570,7 @@
+ page.getBitmap().getWidth() + " x " + page.getBitmap().getHeight());
}
for (RectF area: page.getAreas()) {
+ if (area == null) continue;
RectF subArea = generateSubRect(area, pageIndx, numPages);
Bitmap b = page.getBitmap();
int x = Math.round(b.getWidth() * subArea.left);
@@ -1933,6 +1934,7 @@
}
private boolean isValid(RectF area) {
+ if (area == null) return false;
boolean valid = area.bottom > area.top && area.left < area.right
&& LOCAL_COLOR_BOUNDS.contains(area);
return valid;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index f0f0867..505f400 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -2351,7 +2351,10 @@
final int ellipsisStringLen = ellipsisString.length();
// Use the ellipsis string only if there are that at least as many characters to replace.
final boolean useEllipsisString = ellipsisCount >= ellipsisStringLen;
- for (int i = 0; i < ellipsisCount; i++) {
+ final int min = Math.max(0, start - ellipsisStart - lineStart);
+ final int max = Math.min(ellipsisCount, end - ellipsisStart - lineStart);
+
+ for (int i = min; i < max; i++) {
final char c;
if (useEllipsisString && i < ellipsisStringLen) {
c = ellipsisString.charAt(i);
@@ -2360,9 +2363,7 @@
}
final int a = i + ellipsisStart + lineStart;
- if (start <= a && a < end) {
- dest[destoff + a - start] = c;
- }
+ dest[destoff + a - start] = c;
}
}
diff --git a/core/java/android/view/CrossWindowBlurListeners.java b/core/java/android/view/CrossWindowBlurListeners.java
index e307b96..761a2b8 100644
--- a/core/java/android/view/CrossWindowBlurListeners.java
+++ b/core/java/android/view/CrossWindowBlurListeners.java
@@ -73,14 +73,14 @@
return instance;
}
- boolean isCrossWindowBlurEnabled() {
+ public boolean isCrossWindowBlurEnabled() {
synchronized (sLock) {
attachInternalListenerIfNeededLocked();
return mCrossWindowBlurEnabled;
}
}
- void addListener(@NonNull @CallbackExecutor Executor executor,
+ public void addListener(@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<Boolean> listener) {
Preconditions.checkNotNull(listener, "listener cannot be null");
Preconditions.checkNotNull(executor, "executor cannot be null");
@@ -94,7 +94,7 @@
}
- void removeListener(Consumer<Boolean> listener) {
+ public void removeListener(Consumer<Boolean> listener) {
Preconditions.checkNotNull(listener, "listener cannot be null");
synchronized (sLock) {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 8080883..145607a 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1265,7 +1265,7 @@
}
stateChanged |= getSourceConsumer(types.valueAt(j)).notifyAnimationFinished();
}
- if (invokeCallback && runningAnimation.startDispatched) {
+ if (invokeCallback) {
dispatchAnimationEnd(runningAnimation.runner.getAnimation());
}
break;
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 85ff93b..1506ee4 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -48,6 +48,7 @@
private Insets mInsetsHint;
private boolean mSkipAnimationOnce;
+ private int mParcelableFlags;
public InsetsSourceControl(@InternalInsetsType int type, @Nullable SurfaceControl leash,
Point surfacePosition, Insets insetsHint) {
@@ -132,6 +133,10 @@
return result;
}
+ public void setParcelableFlags(int parcelableFlags) {
+ mParcelableFlags = parcelableFlags;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -140,9 +145,9 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mType);
- dest.writeTypedObject(mLeash, 0 /* parcelableFlags */);
- dest.writeTypedObject(mSurfacePosition, 0 /* parcelableFlags */);
- dest.writeTypedObject(mInsetsHint, 0 /* parcelableFlags */);
+ dest.writeTypedObject(mLeash, mParcelableFlags);
+ dest.writeTypedObject(mSurfacePosition, mParcelableFlags);
+ dest.writeTypedObject(mInsetsHint, mParcelableFlags);
dest.writeBoolean(mSkipAnimationOnce);
}
diff --git a/core/java/android/view/ScrollCaptureTarget.java b/core/java/android/view/ScrollCaptureTarget.java
index 44017ed..a8bb037 100644
--- a/core/java/android/view/ScrollCaptureTarget.java
+++ b/core/java/android/view/ScrollCaptureTarget.java
@@ -21,13 +21,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiThread;
-import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.CancellationSignal;
-import com.android.internal.util.FastMath;
-
import java.io.PrintWriter;
import java.util.function.Consumer;
@@ -43,8 +40,7 @@
private final int mHint;
private Rect mScrollBounds;
- private final float[] mTmpFloatArr = new float[2];
- private final Matrix mMatrixViewLocalToWindow = new Matrix();
+ private final int[] mTmpIntArr = new int[2];
public ScrollCaptureTarget(@NonNull View scrollTarget, @NonNull Rect localVisibleRect,
@NonNull Point positionInWindow, @NonNull ScrollCaptureCallback callback) {
@@ -117,28 +113,15 @@
}
}
- private static void zero(float[] pointArray) {
- pointArray[0] = 0;
- pointArray[1] = 0;
- }
-
- private static void roundIntoPoint(Point pointObj, float[] pointArray) {
- pointObj.x = FastMath.round(pointArray[0]);
- pointObj.y = FastMath.round(pointArray[1]);
- }
-
/**
- * Refresh the local visible bounds and it's offset within the window, based on the current
+ * Refresh the local visible bounds and its offset within the window, based on the current
* state of the {@code containing view}.
*/
@UiThread
public void updatePositionInWindow() {
- mMatrixViewLocalToWindow.reset();
- mContainingView.transformMatrixToGlobal(mMatrixViewLocalToWindow);
-
- zero(mTmpFloatArr);
- mMatrixViewLocalToWindow.mapPoints(mTmpFloatArr);
- roundIntoPoint(mPositionInWindow, mTmpFloatArr);
+ mContainingView.getLocationInWindow(mTmpIntArr);
+ mPositionInWindow.x = mTmpIntArr[0];
+ mPositionInWindow.y = mTmpIntArr[1];
}
public String toString() {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index f34cd8f..c03db6d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -233,6 +233,7 @@
private static native void nativeRemoveJankDataListener(long nativeListener);
private static native long nativeCreateJankDataListenerWrapper(OnJankDataListener listener);
private static native int nativeGetGPUContextPriority();
+ private static native void nativeSetTransformHint(long nativeObject, int transformHint);
@Nullable
@GuardedBy("mLock")
@@ -348,6 +349,8 @@
@GuardedBy("mLock")
private int mHeight;
+ private int mTransformHint;
+
private WeakReference<View> mLocalOwnerView;
static GlobalTransactionWrapper sGlobalTransaction;
@@ -605,6 +608,7 @@
mName = other.mName;
mWidth = other.mWidth;
mHeight = other.mHeight;
+ mTransformHint = other.mTransformHint;
mLocalOwnerView = other.mLocalOwnerView;
assignNativeObject(nativeCopyFromSurfaceControl(other.mNativeObject), callsite);
}
@@ -1467,6 +1471,7 @@
mName = in.readString8();
mWidth = in.readInt();
mHeight = in.readInt();
+ mTransformHint = in.readInt();
long object = 0;
if (in.readInt() != 0) {
@@ -1485,6 +1490,7 @@
dest.writeString8(mName);
dest.writeInt(mWidth);
dest.writeInt(mHeight);
+ dest.writeInt(mTransformHint);
if (mNativeObject == 0) {
dest.writeInt(0);
} else {
@@ -2604,16 +2610,6 @@
= sRegistry.registerNativeAllocation(this, mNativeObject);
}
- /**
- * Create a transaction object that wraps a native peer.
- * @hide
- */
- Transaction(long nativeObject) {
- mNativeObject = nativeObject;
- mFreeNativeResources =
- sRegistry.registerNativeAllocation(this, mNativeObject);
- }
-
private Transaction(Parcel in) {
readFromParcel(in);
}
@@ -3602,4 +3598,27 @@
mHeight = h;
nativeUpdateDefaultBufferSize(mNativeObject, w, h);
}
+
+ /**
+ * @hide
+ */
+ public int getTransformHint() {
+ return mTransformHint;
+ }
+
+ /**
+ * Update the transform hint of current SurfaceControl. Only affect if type is
+ * {@link #FX_SURFACE_BLAST}
+ *
+ * The transform hint is used to prevent allocating a buffer of different size when a
+ * layer is rotated. The producer can choose to consume the hint and allocate the buffer
+ * with the same size.
+ * @hide
+ */
+ public void setTransformHint(@Surface.Rotation int transformHint) {
+ if (mTransformHint != transformHint) {
+ mTransformHint = transformHint;
+ nativeSetTransformHint(mNativeObject, transformHint);
+ }
+ }
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 6b0bb9d..4f2cf6d 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -30,7 +30,6 @@
import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.HardwareRenderer;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
@@ -214,6 +213,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
final Rect mSurfaceFrame = new Rect();
int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
+ int mTransformHint = 0;
private boolean mGlobalListenersAdded;
private boolean mAttachedToWindow;
@@ -944,7 +944,7 @@
}
private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator,
- boolean creating, boolean sizeChanged) {
+ boolean creating, boolean sizeChanged, boolean hintChanged) {
boolean realSizeChanged = false;
mSurfaceLock.lock();
@@ -1009,7 +1009,7 @@
}
}
mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
- if (sizeChanged && !creating) {
+ if ((sizeChanged || hintChanged) && !creating) {
setBufferSize(mTmpTransaction);
}
@@ -1081,17 +1081,18 @@
|| mWindowSpaceTop != mLocation[1];
final boolean layoutSizeChanged = getWidth() != mScreenRect.width()
|| getHeight() != mScreenRect.height();
-
+ final boolean hintChanged = viewRoot.getSurfaceTransformHint() != mTransformHint;
if (creating || formatChanged || sizeChanged || visibleChanged ||
(mUseAlpha && alphaChanged) || windowVisibleChanged ||
- positionChanged || layoutSizeChanged) {
+ positionChanged || layoutSizeChanged || hintChanged) {
getLocationInWindow(mLocation);
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "Changes: creating=" + creating
+ " format=" + formatChanged + " size=" + sizeChanged
+ " visible=" + visibleChanged + " alpha=" + alphaChanged
+ + " hint=" + hintChanged
+ " mUseAlpha=" + mUseAlpha
+ " visible=" + visibleChanged
+ " left=" + (mWindowSpaceLeft != mLocation[0])
@@ -1105,6 +1106,7 @@
mSurfaceHeight = myHeight;
mFormat = mRequestedFormat;
mLastWindowVisibility = mWindowVisibility;
+ mTransformHint = viewRoot.getSurfaceTransformHint();
mScreenRect.left = mWindowSpaceLeft;
mScreenRect.top = mWindowSpaceTop;
@@ -1130,9 +1132,9 @@
}
final boolean realSizeChanged = performSurfaceTransaction(viewRoot,
- translator, creating, sizeChanged);
- final boolean redrawNeeded = sizeChanged || creating ||
- (mVisible && !mDrawFinished);
+ translator, creating, sizeChanged, hintChanged);
+ final boolean redrawNeeded = sizeChanged || creating || hintChanged
+ || (mVisible && !mDrawFinished);
try {
SurfaceHolder.Callback[] callbacks = null;
@@ -1158,7 +1160,7 @@
c.surfaceCreated(mSurfaceHolder);
}
}
- if (creating || formatChanged || sizeChanged
+ if (creating || formatChanged || sizeChanged || hintChanged
|| visibleChanged || realSizeChanged) {
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "surfaceChanged -- format=" + mFormat
@@ -1234,6 +1236,7 @@
private void setBufferSize(Transaction transaction) {
if (mUseBlastAdapter) {
+ mBlastSurfaceControl.setTransformHint(mTransformHint);
mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat);
} else {
transaction.setBufferSize(mSurfaceControl, mSurfaceWidth, mSurfaceHeight);
@@ -1330,6 +1333,8 @@
if (mBlastBufferQueue != null) {
mBlastBufferQueue.destroy();
}
+ mTransformHint = viewRoot.getSurfaceTransformHint();
+ mBlastSurfaceControl.setTransformHint(mTransformHint);
mBlastBufferQueue = new BLASTBufferQueue(name, mBlastSurfaceControl, mSurfaceWidth,
mSurfaceHeight, mFormat);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a8fe875..9450801 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -20703,8 +20703,7 @@
}
/**
- * Return the window this view is currently attached to. Used in
- * {@link android.app.ActivityView} to communicate with WM.
+ * Return the window this view is currently attached to.
* @hide
*/
protected IWindow getWindow() {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d42e0c3..5a248af 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -461,6 +461,9 @@
protected final ViewFrameInfo mViewFrameInfo = new ViewFrameInfo();
private final InputEventAssigner mInputEventAssigner = new InputEventAssigner();
+ // Set to true if mSurfaceControl is used for Webview Overlay
+ private boolean mIsForWebviewOverlay;
+
/**
* Update the Choreographer's FrameInfo object with the timing information for the current
* ViewRootImpl instance. Erase the data in the current ViewFrameInfo to prepare for the next
@@ -1368,12 +1371,33 @@
HardwareRenderer.ASurfaceTransactionCallback callback = (nativeTransactionObj,
nativeSurfaceControlObj,
frameNr) -> {
- Transaction t = new Transaction(nativeTransactionObj);
- mergeWithNextTransaction(t, frameNr);
+ if (mBlastBufferQueue == null) {
+ return false;
+ } else {
+ mBlastBufferQueue.mergeWithNextTransaction(nativeTransactionObj, frameNr);
+ return true;
+ }
};
mAttachInfo.mThreadedRenderer.setASurfaceTransactionCallback(callback);
}
+ /**
+ * Register a callback to be executed when Webview overlay needs a surface control.
+ * This callback will be executed on RenderThread worker thread, and released inside native code
+ * when CanvasContext is destroyed.
+ */
+ private void addPrepareSurfaceControlForWebviewCallback() {
+ HardwareRenderer.PrepareSurfaceControlForWebviewCallback callback = () -> {
+ // make mSurfaceControl transparent, so child surface controls are visible
+ if (mIsForWebviewOverlay) return;
+ synchronized (ViewRootImpl.this) {
+ mIsForWebviewOverlay = true;
+ }
+ mTransaction.setOpaque(mSurfaceControl, false).apply();
+ };
+ mAttachInfo.mThreadedRenderer.setPrepareSurfaceControlForWebviewCallback(callback);
+ }
+
@UnsupportedAppUsage
private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
mAttachInfo.mHardwareAccelerated = false;
@@ -1418,6 +1442,7 @@
if (mHardwareRendererObserver != null) {
mAttachInfo.mThreadedRenderer.addObserver(mHardwareRendererObserver);
}
+ addPrepareSurfaceControlForWebviewCallback();
addASurfaceTransactionCallback();
mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl);
}
@@ -5269,7 +5294,16 @@
// b) When loosing control, controller can restore server state by taking last
// dispatched state as truth.
mInsetsController.onStateChanged((InsetsState) args.arg1);
- mInsetsController.onControlsChanged((InsetsSourceControl[]) args.arg2);
+ InsetsSourceControl[] controls = (InsetsSourceControl[]) args.arg2;
+ if (mAdded) {
+ mInsetsController.onControlsChanged(controls);
+ } else if (controls != null) {
+ for (InsetsSourceControl control : controls) {
+ if (control != null) {
+ control.release(SurfaceControl::release);
+ }
+ }
+ }
args.recycle();
break;
}
@@ -7743,6 +7777,7 @@
}
}
if (mAttachInfo.mThreadedRenderer != null) {
+ addPrepareSurfaceControlForWebviewCallback();
addASurfaceTransactionCallback();
mAttachInfo.mThreadedRenderer.setSurfaceControl(mSurfaceControl);
}
@@ -7790,7 +7825,14 @@
return;
}
- mTransaction.setOpaque(mSurfaceControl, opaque).apply();
+ synchronized (this) {
+ if (mIsForWebviewOverlay) {
+ mIsSurfaceOpaque = false;
+ return;
+ }
+ mTransaction.setOpaque(mSurfaceControl, opaque).apply();
+ }
+
mIsSurfaceOpaque = opaque;
}
@@ -8107,6 +8149,10 @@
}
}
+ // If our window is removed, we might not get notified about losing control.
+ // Invoking this can release the leashes as soon as possible instead of relying on GC.
+ mInsetsController.onControlsChanged(null);
+
mAdded = false;
}
WindowManagerGlobal.getInstance().doRemoveView(this);
@@ -10353,4 +10399,8 @@
});
return true;
}
+
+ int getSurfaceTransformHint() {
+ return mSurfaceControl.getTransformHint();
+ }
}
diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java
index d8cd605..27821fd 100644
--- a/core/java/android/view/ViewRootInsetsControllerHost.java
+++ b/core/java/android/view/ViewRootInsetsControllerHost.java
@@ -110,6 +110,10 @@
@Override
public void dispatchWindowInsetsAnimationEnd(@NonNull WindowInsetsAnimation animation) {
if (DEBUG) Log.d(TAG, "windowInsetsAnimation ended");
+ if (mViewRoot.mView == null) {
+ // The view has already detached from window.
+ return;
+ }
mViewRoot.mView.dispatchWindowInsetsAnimationEnd(animation);
}
diff --git a/core/java/android/view/WindowInsetsAnimation.java b/core/java/android/view/WindowInsetsAnimation.java
index ab5b5ba..6576eea 100644
--- a/core/java/android/view/WindowInsetsAnimation.java
+++ b/core/java/android/view/WindowInsetsAnimation.java
@@ -360,6 +360,13 @@
* finished, and then revert to the starting state of the animation in the first
* {@link #onProgress} callback by using post-layout view properties like {@link View#setX}
* and related methods.
+ *
+ * <p>Note that the animation might be cancelled before {@link #onStart} is dispatched. On
+ * {@link android.os.Build.VERSION_CODES#S S} and later, {@link #onEnd} is immediately
+ * dispatched without an {@link #onStart} in that case.
+ * On {@link android.os.Build.VERSION_CODES#R R}, no callbacks are dispatched after
+ * {@code #onPrepare} for such an animation.
+ *
* <p>
* Note: If the animation is application controlled by using
* {@link WindowInsetsController#controlWindowInsetsAnimation}, the end state of the
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 10ae691..ce6d034 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -286,6 +286,15 @@
return this;
}
+ boolean hasSameComposingSpan(@NonNull ContentCaptureEvent other) {
+ return mComposingStart == other.mComposingStart && mComposingEnd == other.mComposingEnd;
+ }
+
+ boolean hasSameSelectionSpan(@NonNull ContentCaptureEvent other) {
+ return mSelectionStartIndex == other.mSelectionStartIndex
+ && mSelectionEndIndex == other.mSelectionEndIndex;
+ }
+
private int getComposingStart() {
return mComposingStart;
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index c633268..cc47f09 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -341,11 +341,7 @@
}
}
- try {
- flush(FLUSH_REASON_SESSION_FINISHED);
- } finally {
- onDestroy();
- }
+ onDestroy();
}
abstract void onDestroy();
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index d8ac779..e0a7bf2 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -263,7 +263,13 @@
@Override
void onDestroy() {
mHandler.removeMessages(MSG_FLUSH);
- mHandler.post(() -> destroySession());
+ mHandler.post(() -> {
+ try {
+ flush(FLUSH_REASON_SESSION_FINISHED);
+ } finally {
+ destroySession();
+ }
+ });
}
/**
@@ -317,9 +323,11 @@
if (!hasStarted() && eventType != ContentCaptureEvent.TYPE_SESSION_STARTED
&& eventType != ContentCaptureEvent.TYPE_CONTEXT_UPDATED) {
// TODO(b/120494182): comment when this could happen (dialogs?)
- Log.v(TAG, "handleSendEvent(" + getDebugState() + ", "
- + ContentCaptureEvent.getTypeAsString(eventType)
- + "): dropping because session not started yet");
+ if (sVerbose) {
+ Log.v(TAG, "handleSendEvent(" + getDebugState() + ", "
+ + ContentCaptureEvent.getTypeAsString(eventType)
+ + "): dropping because session not started yet");
+ }
return;
}
if (mDisabled.get()) {
@@ -362,7 +370,10 @@
final CharSequence lastText = lastEvent.getText();
final boolean bothNonEmpty = !TextUtils.isEmpty(lastText)
&& !TextUtils.isEmpty(text);
- boolean equalContent = TextUtils.equals(lastText, text);
+ boolean equalContent =
+ TextUtils.equals(lastText, text)
+ && lastEvent.hasSameComposingSpan(event)
+ && lastEvent.hasSameSelectionSpan(event);
if (equalContent) {
addEvent = false;
} else if (bothNonEmpty) {
@@ -571,9 +582,11 @@
private ParceledListSlice<ContentCaptureEvent> clearEvents() {
// NOTE: we must save a reference to the current mEvents and then set it to to null,
// otherwise clearing it would clear it in the receiving side if the service is also local.
- final List<ContentCaptureEvent> events = mEvents == null
- ? Collections.EMPTY_LIST
- : new ArrayList<>(mEvents);
+ if (mEvents == null) {
+ return new ParceledListSlice<>(Collections.EMPTY_LIST);
+ }
+
+ final List<ContentCaptureEvent> events = new ArrayList<>(mEvents);
mEvents.clear();
return new ParceledListSlice<>(events);
}
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index bdd1206..c4540b0 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -163,6 +163,17 @@
}
/**
+ * Called after only the composing region is modified (so it isn't called if the text also
+ * changes).
+ * <p>
+ * Default implementation does nothing.
+ *
+ * @hide
+ */
+ public void endComposingRegionEditInternal() {
+ }
+
+ /**
* Default implementation calls {@link #finishComposingText()} and
* {@code setImeConsumesInput(false)}.
*/
@@ -468,6 +479,7 @@
// Note: sendCurrentText does nothing unless mFallbackMode is set
sendCurrentText();
endBatchEdit();
+ endComposingRegionEditInternal();
}
return true;
}
@@ -734,6 +746,7 @@
// Note: sendCurrentText does nothing unless mFallbackMode is set
sendCurrentText();
endBatchEdit();
+ endComposingRegionEditInternal();
}
return true;
}
diff --git a/core/java/android/view/translation/TranslationCapability.java b/core/java/android/view/translation/TranslationCapability.java
index d104b24..65b749a 100644
--- a/core/java/android/view/translation/TranslationCapability.java
+++ b/core/java/android/view/translation/TranslationCapability.java
@@ -61,6 +61,13 @@
* was dropped.</p>
*/
public static final @ModelState int STATE_NOT_AVAILABLE = 4;
+ /**
+ * The translation between the source and target specs were removed from the system, but is
+ * still available to be downloaded again.
+ *
+ * @hide
+ */
+ public static final @ModelState int STATE_REMOVED_AND_AVAILABLE = 1000;
/**
* The state of translation readiness between {@code mSourceSpec} and {@code mTargetSpec}.
@@ -134,7 +141,8 @@
STATE_AVAILABLE_TO_DOWNLOAD,
STATE_DOWNLOADING,
STATE_ON_DEVICE,
- STATE_NOT_AVAILABLE
+ STATE_NOT_AVAILABLE,
+ STATE_REMOVED_AND_AVAILABLE
})
@Retention(RetentionPolicy.SOURCE)
@DataClass.Generated.Member
@@ -152,6 +160,8 @@
return "STATE_ON_DEVICE";
case STATE_NOT_AVAILABLE:
return "STATE_NOT_AVAILABLE";
+ case STATE_REMOVED_AND_AVAILABLE:
+ return "STATE_REMOVED_AND_AVAILABLE";
default: return Integer.toHexString(value);
}
}
@@ -255,13 +265,15 @@
if (!(mState == STATE_AVAILABLE_TO_DOWNLOAD)
&& !(mState == STATE_DOWNLOADING)
&& !(mState == STATE_ON_DEVICE)
- && !(mState == STATE_NOT_AVAILABLE)) {
+ && !(mState == STATE_NOT_AVAILABLE)
+ && !(mState == STATE_REMOVED_AND_AVAILABLE)) {
throw new java.lang.IllegalArgumentException(
"state was " + mState + " but must be one of: "
+ "STATE_AVAILABLE_TO_DOWNLOAD(" + STATE_AVAILABLE_TO_DOWNLOAD + "), "
+ "STATE_DOWNLOADING(" + STATE_DOWNLOADING + "), "
+ "STATE_ON_DEVICE(" + STATE_ON_DEVICE + "), "
- + "STATE_NOT_AVAILABLE(" + STATE_NOT_AVAILABLE + ")");
+ + "STATE_NOT_AVAILABLE(" + STATE_NOT_AVAILABLE + "), "
+ + "STATE_REMOVED_AND_AVAILABLE(" + STATE_REMOVED_AND_AVAILABLE + ")");
}
this.mSourceSpec = sourceSpec;
@@ -293,10 +305,10 @@
};
@DataClass.Generated(
- time = 1621545303074L,
+ time = 1624307114468L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/translation/TranslationCapability.java",
- inputSignatures = "public static final @android.view.translation.TranslationCapability.ModelState int STATE_AVAILABLE_TO_DOWNLOAD\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_DOWNLOADING\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_ON_DEVICE\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_NOT_AVAILABLE\nprivate final @android.view.translation.TranslationCapability.ModelState int mState\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mTargetSpec\nprivate final boolean mUiTranslationEnabled\nprivate final @android.view.translation.TranslationContext.TranslationFlag int mSupportedTranslationFlags\nclass TranslationCapability extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstDefs=true, genToString=true, genConstructor=false)")
+ inputSignatures = "public static final @android.view.translation.TranslationCapability.ModelState int STATE_AVAILABLE_TO_DOWNLOAD\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_DOWNLOADING\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_ON_DEVICE\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_NOT_AVAILABLE\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_REMOVED_AND_AVAILABLE\nprivate final @android.view.translation.TranslationCapability.ModelState int mState\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mTargetSpec\nprivate final boolean mUiTranslationEnabled\nprivate final @android.view.translation.TranslationContext.TranslationFlag int mSupportedTranslationFlags\nclass TranslationCapability extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstDefs=true, genToString=true, genConstructor=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 5ac878d..592993c 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -424,7 +424,7 @@
if (callback == null) {
if (view instanceof TextView) {
// developer doesn't provide their override, we set the default TextView
- // implememtation.
+ // implementation.
callback = new TextViewTranslationCallback();
view.setViewTranslationCallback(callback);
} else {
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 8d27cde..cf6807e 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -33,7 +33,6 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.Trace;
-import android.os.UserHandle;
import android.util.AndroidRuntimeException;
import android.util.ArraySet;
import android.util.Log;
@@ -468,12 +467,9 @@
sTimestamps.mCreateContextStart = SystemClock.uptimeMillis();
try {
// Construct an app context to load the Java code into the current app.
- Context webViewContext = initialApplication.createPackageContextAsUser(
- ai.packageName,
- Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY,
- UserHandle.getUserHandleForUid(ai.uid),
- PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
+ Context webViewContext = initialApplication.createApplicationContext(
+ ai,
+ Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
sPackageInfo = newPackageInfo;
return webViewContext;
} finally {
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index 64570a8..e1d6012 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -8,6 +8,6 @@
mount@google.com
njawad@google.com
-per-file TextView.java, EditText.java, Editor.java = siyamed@google.com, nona@google.com, clarabayarri@google.com
+per-file TextView*, EditText.java, Editor.java = siyamed@google.com, nona@google.com, clarabayarri@google.com
per-file SpellChecker.java = file:../view/inputmethod/OWNERS
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 8a044fd..e827f0a 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -5831,9 +5831,8 @@
return context;
}
try {
- return context.createPackageContextAsUser(mApplication.packageName,
- Context.CONTEXT_RESTRICTED,
- UserHandle.getUserHandleForUid(mApplication.uid));
+ return context.createApplicationContext(mApplication,
+ Context.CONTEXT_RESTRICTED);
} catch (NameNotFoundException e) {
Log.e(LOG_TAG, "Package name " + mApplication.packageName + " not found");
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3c4fd5e..37374ef 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -129,7 +129,6 @@
import android.text.method.TimeKeyListener;
import android.text.method.TransformationMethod;
import android.text.method.TransformationMethod2;
-import android.text.method.TranslationTransformationMethod;
import android.text.method.WordIterator;
import android.text.style.CharacterStyle;
import android.text.style.ClickableSpan;
@@ -199,7 +198,6 @@
import android.view.translation.UiTranslationController;
import android.view.translation.ViewTranslationCallback;
import android.view.translation.ViewTranslationRequest;
-import android.view.translation.ViewTranslationResponse;
import android.widget.RemoteViews.RemoteView;
import com.android.internal.annotations.VisibleForTesting;
@@ -10832,11 +10830,19 @@
}
}
+ notifyContentCaptureTextChanged();
+ }
+
+ /**
+ * Notifies the ContentCapture service that the text of the view has changed (only if
+ * ContentCapture has been notified of this view's existence already).
+ *
+ * @hide
+ */
+ public void notifyContentCaptureTextChanged() {
// TODO(b/121045053): should use a flag / boolean to keep status of SHOWN / HIDDEN instead
// of using isLaidout(), so it's not called in cases where it's laid out but a
// notifyAppeared was not sent.
-
- // ContentCapture
if (isLaidOut() && isImportantForContentCapture() && getNotifiedContentCaptureAppeared()) {
final ContentCaptureManager cm = mContext.getSystemService(ContentCaptureManager.class);
if (cm != null && cm.isContentCaptureEnabled()) {
@@ -13925,8 +13931,8 @@
Log.w(LOG_TAG, "Cannot create translation request. editable = "
+ isTextEditable() + ", isPassword = " + isPassword + ", selectable = "
+ isTextSelectable());
- return;
}
+ return;
}
// TODO(b/176488462): apply the view's important for translation
requestBuilder.setValue(ViewTranslationRequest.ID_TEXT,
@@ -13938,33 +13944,4 @@
}
requestsCollector.accept(requestBuilder.build());
}
-
- /**
- *
- * Called when the content from {@link #onCreateViewTranslationRequest} had been translated by
- * the TranslationService. The default implementation will replace the current
- * {@link TransformationMethod} to transform the original text to the translated text display.
- *
- * @param response a {@link ViewTranslationResponse} that contains the translated information
- * which can be shown in the view.
- */
- @Override
- public void onViewTranslationResponse(@NonNull ViewTranslationResponse response) {
- // set ViewTranslationResponse
- super.onViewTranslationResponse(response);
- // TODO(b/178353965): move to ViewTranslationCallback.onShow()
- ViewTranslationCallback callback = getViewTranslationCallback();
- if (callback instanceof TextViewTranslationCallback) {
- TextViewTranslationCallback textViewDefaultCallback =
- (TextViewTranslationCallback) callback;
- TranslationTransformationMethod oldTranslationMethod =
- textViewDefaultCallback.getTranslationTransformation();
- TransformationMethod originalTranslationMethod = oldTranslationMethod != null
- ? oldTranslationMethod.getOriginalTransformationMethod() : mTransformation;
- TranslationTransformationMethod newTranslationMethod =
- new TranslationTransformationMethod(response, originalTranslationMethod);
- // TODO(b/178353965): well-handle setTransformationMethod.
- textViewDefaultCallback.setTranslationTransformation(newTranslationMethod);
- }
- }
}
diff --git a/core/java/android/widget/TextViewTranslationCallback.java b/core/java/android/widget/TextViewTranslationCallback.java
index a7d5ee4..e1b04f8 100644
--- a/core/java/android/widget/TextViewTranslationCallback.java
+++ b/core/java/android/widget/TextViewTranslationCallback.java
@@ -56,26 +56,6 @@
private CharSequence mContentDescription;
- /**
- * Invoked by the platform when receiving the successful {@link ViewTranslationResponse} for the
- * view that provides the translatable information by {@link View#createTranslationRequest} and
- * sent by the platform.
- */
- void setTranslationTransformation(TranslationTransformationMethod method) {
- if (method == null) {
- if (DEBUG) {
- Log.w(TAG, "setTranslationTransformation: should not set null "
- + "TranslationTransformationMethod");
- }
- return;
- }
- mTranslationTransformation = method;
- }
-
- TranslationTransformationMethod getTranslationTransformation() {
- return mTranslationTransformation;
- }
-
private void clearTranslationTransformation() {
if (DEBUG) {
Log.v(TAG, "clearTranslationTransformation: " + mTranslationTransformation);
@@ -88,34 +68,33 @@
*/
@Override
public boolean onShowTranslation(@NonNull View view) {
- if (view.getViewTranslationResponse() == null) {
- Log.wtf(TAG, "onShowTranslation() shouldn't be called before "
+ ViewTranslationResponse response = view.getViewTranslationResponse();
+ if (response == null) {
+ Log.e(TAG, "onShowTranslation() shouldn't be called before "
+ "onViewTranslationResponse().");
return false;
}
- if (mTranslationTransformation != null) {
- final TransformationMethod transformation = mTranslationTransformation;
- runWithAnimation(
- (TextView) view,
- () -> {
- mIsShowingTranslation = true;
- ((TextView) view).setTransformationMethod(transformation);
- });
- ViewTranslationResponse response = view.getViewTranslationResponse();
- if (response.getKeys().contains(ViewTranslationRequest.ID_CONTENT_DESCRIPTION)) {
- CharSequence translatedContentDescription =
- response.getValue(ViewTranslationRequest.ID_CONTENT_DESCRIPTION).getText();
- if (!TextUtils.isEmpty(translatedContentDescription)) {
- mContentDescription = view.getContentDescription();
- view.setContentDescription(translatedContentDescription);
- }
+ if (mTranslationTransformation == null) {
+ TransformationMethod originalTranslationMethod =
+ ((TextView) view).getTransformationMethod();
+ mTranslationTransformation = new TranslationTransformationMethod(response,
+ originalTranslationMethod);
+ }
+ final TransformationMethod transformation = mTranslationTransformation;
+ runWithAnimation(
+ (TextView) view,
+ () -> {
+ mIsShowingTranslation = true;
+ // TODO(b/178353965): well-handle setTransformationMethod.
+ ((TextView) view).setTransformationMethod(transformation);
+ });
+ if (response.getKeys().contains(ViewTranslationRequest.ID_CONTENT_DESCRIPTION)) {
+ CharSequence translatedContentDescription =
+ response.getValue(ViewTranslationRequest.ID_CONTENT_DESCRIPTION).getText();
+ if (!TextUtils.isEmpty(translatedContentDescription)) {
+ mContentDescription = view.getContentDescription();
+ view.setContentDescription(translatedContentDescription);
}
- } else {
- if (DEBUG) {
- // TODO(b/182433547): remove before S release
- Log.w(TAG, "onShowTranslation(): no translated text.");
- }
- return false;
}
return true;
}
@@ -126,7 +105,7 @@
@Override
public boolean onHideTranslation(@NonNull View view) {
if (view.getViewTranslationResponse() == null) {
- Log.wtf(TAG, "onHideTranslation() shouldn't be called before "
+ Log.e(TAG, "onHideTranslation() shouldn't be called before "
+ "onViewTranslationResponse().");
return false;
}
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index 4a3bf91..148986a 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -292,6 +292,7 @@
private SurfaceView createSurfaceView(@NonNull SplashScreenView view) {
final SurfaceView surfaceView = new SurfaceView(view.getContext());
+ surfaceView.setPadding(0, 0, 0, 0);
if (mSurfacePackage == null) {
if (DEBUG) {
Log.d(TAG,
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
index a600a94..c57afbc 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
@@ -16,6 +16,7 @@
package com.android.internal.accessibility.util;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
@@ -28,6 +29,7 @@
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__UNKNOWN;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON_LONG_PRESS;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY;
@@ -37,6 +39,8 @@
import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_WINDOW;
import android.content.ComponentName;
+import android.content.Context;
+import android.provider.Settings;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.ShortcutType;
@@ -50,50 +54,54 @@
private AccessibilityStatsLogUtils() {}
/**
- * Logs accessibility feature name that is assigned to the shortcut also its shortcut type.
+ * Logs accessibility feature name that is assigned to the given {@code shortcutType}.
* Calls this when clicking the shortcut {@link AccessibilityManager#ACCESSIBILITY_BUTTON} or
- * {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY}
+ * {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY}.
*
+ * @param context context used to retrieve the {@link Settings} provider
* @param componentName component name of the accessibility feature
- * @param shortcutType accessibility shortcut type {@link ShortcutType}
+ * @param shortcutType accessibility shortcut type
*/
- public static void logAccessibilityShortcutActivated(ComponentName componentName,
- @ShortcutType int shortcutType) {
- logAccessibilityShortcutActivated(componentName, shortcutType, UNKNOWN_STATUS);
+ public static void logAccessibilityShortcutActivated(Context context,
+ ComponentName componentName, @ShortcutType int shortcutType) {
+ logAccessibilityShortcutActivatedInternal(componentName,
+ convertToLoggingShortcutType(context, shortcutType), UNKNOWN_STATUS);
}
/**
- * Logs accessibility feature name that is assigned to the shortcut also its shortcut type and
- * enabled status. Calls this when clicking the shortcut
- * {@link AccessibilityManager#ACCESSIBILITY_BUTTON}
- * or {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY}
+ * Logs accessibility feature name that is assigned to the given {@code shortcutType} and the
+ * {@code serviceEnabled} status.
+ * Calls this when clicking the shortcut {@link AccessibilityManager#ACCESSIBILITY_BUTTON}
+ * or {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY}.
*
+ * @param context context used to retrieve the {@link Settings} provider
* @param componentName component name of the accessibility feature
* @param shortcutType accessibility shortcut type
* @param serviceEnabled {@code true} if the service is enabled
*/
- public static void logAccessibilityShortcutActivated(ComponentName componentName,
- @ShortcutType int shortcutType, boolean serviceEnabled) {
- logAccessibilityShortcutActivated(componentName, shortcutType,
+ public static void logAccessibilityShortcutActivated(Context context,
+ ComponentName componentName, @ShortcutType int shortcutType, boolean serviceEnabled) {
+ logAccessibilityShortcutActivatedInternal(componentName,
+ convertToLoggingShortcutType(context, shortcutType),
convertToLoggingServiceStatus(serviceEnabled));
}
/**
- * Logs accessibility feature name that is assigned to the shortcut also its shortcut type and
- * status code. Calls this when clicking the shortcut
- * {@link AccessibilityManager#ACCESSIBILITY_BUTTON}
- * or {@link AccessibilityManager#ACCESSIBILITY_SHORTCUT_KEY}
+ * Logs accessibility feature name that is assigned to the given {@code loggingShortcutType} and
+ * {@code loggingServiceStatus} code.
*
- * @param componentName component name of the accessibility feature
- * @param shortcutType accessibility shortcut type {@link ShortcutType}
- * @param serviceStatus The service status code. 0 denotes unknown_status, 1 denotes enabled, 2
- * denotes disabled.
+ * @param componentName component name of the accessibility feature
+ * @param loggingShortcutType accessibility shortcut type for logging. 0 denotes
+ * unknown_type, 1 denotes accessibility button, 2 denotes volume
+ * key, 3 denotes triple tap on the screen, 4 denotes long press on
+ * accessibility button, 5 denotes accessibility floating menu.
+ * @param loggingServiceStatus The service status code for logging. 0 denotes unknown_status, 1
+ * denotes enabled, 2 denotes disabled.
*/
- private static void logAccessibilityShortcutActivated(ComponentName componentName,
- @ShortcutType int shortcutType, int serviceStatus) {
+ private static void logAccessibilityShortcutActivatedInternal(ComponentName componentName,
+ int loggingShortcutType, int loggingServiceStatus) {
FrameworkStatsLog.write(FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED,
- componentName.flattenToString(), convertToLoggingShortcutType(shortcutType),
- serviceStatus);
+ componentName.flattenToString(), loggingShortcutType, loggingServiceStatus);
}
/**
@@ -144,10 +152,19 @@
convertToLoggingMagnificationMode(mode));
}
- private static int convertToLoggingShortcutType(@ShortcutType int shortcutType) {
+ private static boolean isFloatingMenuEnabled(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, /* def= */ -1)
+ == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+ }
+
+ private static int convertToLoggingShortcutType(Context context,
+ @ShortcutType int shortcutType) {
switch (shortcutType) {
case ACCESSIBILITY_BUTTON:
- return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON;
+ return isFloatingMenuEnabled(context)
+ ? ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU
+ : ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON;
case ACCESSIBILITY_SHORTCUT_KEY:
return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY;
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 08db74f..7000ed7 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -464,8 +464,8 @@
private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED = 5;
private static final int LIST_VIEW_UPDATE_MESSAGE = 6;
- private static final int WATCHDOG_TIMEOUT_MAX_MILLIS = 10000;
- private static final int WATCHDOG_TIMEOUT_MIN_MILLIS = 3000;
+ private static final int WATCHDOG_TIMEOUT_MAX_MILLIS = 1000;
+ private static final int WATCHDOG_TIMEOUT_MIN_MILLIS = 300;
private boolean mMinTimeoutPassed = false;
diff --git a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
index ec99c95..d0214e6 100644
--- a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
+++ b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl
@@ -78,4 +78,7 @@
* @param status The status about the result of requesting update state action.
*/
void onStatusReported(int status);
+
+ /** Called when the hotword detection process is restarted */
+ void onProcessRestarted();
}
diff --git a/core/java/com/android/internal/content/F2fsUtils.java b/core/java/com/android/internal/content/F2fsUtils.java
new file mode 100644
index 0000000..27f1b30
--- /dev/null
+++ b/core/java/com/android/internal/content/F2fsUtils.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.content;
+
+import android.annotation.NonNull;
+import android.content.ContentResolver;
+import android.os.Environment;
+import android.os.incremental.IncrementalManager;
+import android.provider.Settings.Secure;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility methods to work with the f2fs file system.
+ */
+public final class F2fsUtils {
+ private static final String TAG = "F2fsUtils";
+ private static final boolean DEBUG_F2FS = false;
+
+ /** Directory containing kernel features */
+ private static final File sKernelFeatures =
+ new File("/sys/fs/f2fs/features");
+ /** File containing features enabled on "/data" */
+ private static final File sUserDataFeatures =
+ new File("/dev/sys/fs/by-name/userdata/features");
+ private static final File sDataDirectory = Environment.getDataDirectory();
+ /** Name of the compression feature */
+ private static final String COMPRESSION_FEATURE = "compression";
+
+ private static final boolean sKernelCompressionAvailable;
+ private static final boolean sUserDataCompressionAvailable;
+
+ static {
+ sKernelCompressionAvailable = isCompressionEnabledInKernel();
+ if (!sKernelCompressionAvailable) {
+ if (DEBUG_F2FS) {
+ Slog.d(TAG, "f2fs compression DISABLED; feature not part of the kernel");
+ }
+ }
+ sUserDataCompressionAvailable = isCompressionEnabledOnUserData();
+ if (!sUserDataCompressionAvailable) {
+ if (DEBUG_F2FS) {
+ Slog.d(TAG, "f2fs compression DISABLED; feature not enabled on filesystem");
+ }
+ }
+ }
+
+ /**
+ * Releases compressed blocks from eligible installation artifacts.
+ * <p>
+ * Modern f2fs implementations starting in {@code S} support compression
+ * natively within the file system. The data blocks of specific installation
+ * artifacts [eg. .apk, .so, ...] can be compressed at the file system level,
+ * making them look and act like any other uncompressed file, but consuming
+ * a fraction of the space.
+ * <p>
+ * However, the unused space is not free'd automatically. Instead, we must
+ * manually tell the file system to release the extra blocks [the delta between
+ * the compressed and uncompressed block counts] back to the free pool.
+ * <p>
+ * Because of how compression works within the file system, once the blocks
+ * have been released, the file becomes read-only and cannot be modified until
+ * the free'd blocks have again been reserved from the free pool.
+ */
+ public static void releaseCompressedBlocks(ContentResolver resolver, File file) {
+ if (!sKernelCompressionAvailable || !sUserDataCompressionAvailable) {
+ return;
+ }
+
+ // NOTE: Retrieving this setting means we need to delay releasing cblocks
+ // of any APKs installed during the PackageManagerService constructor. Instead
+ // of being able to release them in the constructor, they can only be released
+ // immediately prior to the system being available. When we no longer need to
+ // read this setting, move cblock release back to the package manager constructor.
+ final boolean releaseCompressBlocks =
+ Secure.getInt(resolver, Secure.RELEASE_COMPRESS_BLOCKS_ON_INSTALL, 1) != 0;
+ if (!releaseCompressBlocks) {
+ if (DEBUG_F2FS) {
+ Slog.d(TAG, "SKIP; release compress blocks not enabled");
+ }
+ return;
+ }
+ if (!isCompressionAllowed(file)) {
+ if (DEBUG_F2FS) {
+ Slog.d(TAG, "SKIP; compression not allowed");
+ }
+ return;
+ }
+ final File[] files = getFilesToRelease(file);
+ if (files == null || files.length == 0) {
+ if (DEBUG_F2FS) {
+ Slog.d(TAG, "SKIP; no files to compress");
+ }
+ return;
+ }
+ for (int i = files.length - 1; i >= 0; --i) {
+ final long releasedBlocks = nativeReleaseCompressedBlocks(files[i].getAbsolutePath());
+ if (DEBUG_F2FS) {
+ Slog.d(TAG, "RELEASED " + releasedBlocks + " blocks"
+ + " from \"" + files[i] + "\"");
+ }
+ }
+ }
+
+ /**
+ * Returns {@code true} if compression is allowed on the file system containing
+ * the given file.
+ * <p>
+ * NOTE: The return value does not mean if the given file, or any other file
+ * on the same file system, is actually compressed. It merely determines whether
+ * not files <em>may</em> be compressed.
+ */
+ private static boolean isCompressionAllowed(@NonNull File file) {
+ final String filePath;
+ try {
+ filePath = file.getCanonicalPath();
+ } catch (IOException e) {
+ if (DEBUG_F2FS) {
+ Slog.d(TAG, "f2fs compression DISABLED; could not determine path");
+ }
+ return false;
+ }
+ if (IncrementalManager.isIncrementalPath(filePath)) {
+ if (DEBUG_F2FS) {
+ Slog.d(TAG, "f2fs compression DISABLED; file on incremental fs");
+ }
+ return false;
+ }
+ if (!isChild(sDataDirectory, filePath)) {
+ if (DEBUG_F2FS) {
+ Slog.d(TAG, "f2fs compression DISABLED; file not on /data");
+ }
+ return false;
+ }
+ if (DEBUG_F2FS) {
+ Slog.d(TAG, "f2fs compression ENABLED");
+ }
+ return true;
+ }
+
+ /**
+ * Returns {@code true} if the given child is a descendant of the base.
+ */
+ private static boolean isChild(@NonNull File base, @NonNull String childPath) {
+ try {
+ base = base.getCanonicalFile();
+
+ File parentFile = new File(childPath).getCanonicalFile();
+ while (parentFile != null) {
+ if (base.equals(parentFile)) {
+ return true;
+ }
+ parentFile = parentFile.getParentFile();
+ }
+ return false;
+ } catch (IOException ignore) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns whether or not the compression feature is enabled in the kernel.
+ * <p>
+ * NOTE: This doesn't mean compression is enabled on a particular file system
+ * or any files have been compressed. Only that the functionality is enabled
+ * on the device.
+ */
+ private static boolean isCompressionEnabledInKernel() {
+ final File[] features = sKernelFeatures.listFiles();
+ if (features == null || features.length == 0) {
+ if (DEBUG_F2FS) {
+ Slog.d(TAG, "ERROR; no kernel features");
+ }
+ return false;
+ }
+ for (int i = features.length - 1; i >= 0; --i) {
+ final File feature = features[i];
+ if (COMPRESSION_FEATURE.equals(features[i].getName())) {
+ if (DEBUG_F2FS) {
+ Slog.d(TAG, "FOUND kernel compression feature");
+ }
+ return true;
+ }
+ }
+ if (DEBUG_F2FS) {
+ Slog.d(TAG, "ERROR; kernel compression feature not found");
+ }
+ return false;
+ }
+
+ /**
+ * Returns whether or not the compression feature is enabled on user data [ie. "/data"].
+ * <p>
+ * NOTE: This doesn't mean any files have been compressed. Only that the functionality
+ * is enabled on the file system.
+ */
+ private static boolean isCompressionEnabledOnUserData() {
+ if (!sUserDataFeatures.exists()
+ || !sUserDataFeatures.isFile()
+ || !sUserDataFeatures.canRead()) {
+ if (DEBUG_F2FS) {
+ Slog.d(TAG, "ERROR; filesystem features not available");
+ }
+ return false;
+ }
+ final List<String> configLines;
+ try {
+ configLines = Files.readAllLines(sUserDataFeatures.toPath());
+ } catch (IOException ignore) {
+ if (DEBUG_F2FS) {
+ Slog.d(TAG, "ERROR; couldn't read filesystem features");
+ }
+ return false;
+ }
+ if (configLines == null
+ || configLines.size() > 1
+ || TextUtils.isEmpty(configLines.get(0))) {
+ if (DEBUG_F2FS) {
+ Slog.d(TAG, "ERROR; no filesystem features");
+ }
+ return false;
+ }
+ final String[] features = configLines.get(0).split(",");
+ for (int i = features.length - 1; i >= 0; --i) {
+ if (COMPRESSION_FEATURE.equals(features[i].trim())) {
+ if (DEBUG_F2FS) {
+ Slog.d(TAG, "FOUND filesystem compression feature");
+ }
+ return true;
+ }
+ }
+ if (DEBUG_F2FS) {
+ Slog.d(TAG, "ERROR; filesystem compression feature not found");
+ }
+ return false;
+ }
+
+ /**
+ * Returns all files contained within the directory at any depth from the given path.
+ */
+ private static List<File> getFilesRecursive(@NonNull File path) {
+ final File[] allFiles = path.listFiles();
+ if (allFiles == null) {
+ return null;
+ }
+ final ArrayList<File> files = new ArrayList<>();
+ for (File f : allFiles) {
+ if (f.isDirectory()) {
+ files.addAll(getFilesRecursive(f));
+ } else if (f.isFile()) {
+ files.add(f);
+ }
+ }
+ return files;
+ }
+
+ /**
+ * Returns all files contained within the directory at any depth from the given path.
+ */
+ private static File[] getFilesToRelease(@NonNull File codePath) {
+ final List<File> files = getFilesRecursive(codePath);
+ if (files == null) {
+ if (codePath.isFile()) {
+ return new File[] { codePath };
+ }
+ return null;
+ }
+ if (files.size() == 0) {
+ return null;
+ }
+ return files.toArray(new File[files.size()]);
+ }
+
+ private static native long nativeReleaseCompressedBlocks(String path);
+
+}
diff --git a/core/java/com/android/internal/content/OWNERS b/core/java/com/android/internal/content/OWNERS
new file mode 100644
index 0000000..c42bee6
--- /dev/null
+++ b/core/java/com/android/internal/content/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 36137
+include /core/java/android/content/pm/OWNERS
+
+per-file ReferrerIntent.aidl = file:/services/core/java/com/android/server/am/OWNERS
+per-file ReferrerIntent.java = file:/services/core/java/com/android/server/am/OWNERS
diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java
index b38f623e..3f3c9bd 100644
--- a/core/java/com/android/internal/content/om/OverlayConfig.java
+++ b/core/java/com/android/internal/content/om/OverlayConfig.java
@@ -111,7 +111,7 @@
// Rebase the system partitions and settings file on the specified root directory.
partitions = new ArrayList<>(PackagePartitions.getOrderedPartitions(
p -> new OverlayPartition(
- new File(rootDirectory, p.getFolder().getPath()),
+ new File(rootDirectory, p.getNonConicalFolder().getPath()),
p)));
}
diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index bd90890..19183b8 100644
--- a/core/java/com/android/internal/display/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java
@@ -31,9 +31,6 @@
import android.util.MathUtils;
import android.view.Display;
-import java.util.LinkedList;
-import java.util.Queue;
-
/**
* BrightnessSynchronizer helps convert between the int (old) system and float
* (new) system for storing the brightness. It has methods to convert between the two and also
@@ -43,12 +40,11 @@
private static final int MSG_UPDATE_FLOAT = 1;
private static final int MSG_UPDATE_INT = 2;
+ private static final int MSG_UPDATE_BOTH = 3;
private static final String TAG = "BrightnessSynchronizer";
private static final Uri BRIGHTNESS_URI =
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
- private static final Uri BRIGHTNESS_FLOAT_URI =
- Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FLOAT);
// The tolerance within which we consider brightness values approximately equal to eachother.
// This value is approximately 1/3 of the smallest possible brightness value.
@@ -57,8 +53,6 @@
private DisplayManager mDisplayManager;
private final Context mContext;
- private final Queue<Object> mWriteHistory = new LinkedList<>();
-
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
@@ -69,6 +63,9 @@
case MSG_UPDATE_INT:
updateBrightnessIntFromFloat(Float.intBitsToFloat(msg.arg1));
break;
+ case MSG_UPDATE_BOTH:
+ updateBoth(Float.intBitsToFloat(msg.arg1));
+ break;
default:
super.handleMessage(msg);
}
@@ -139,7 +136,7 @@
/**
* Translates specified value from the float brightness system to the int brightness system,
- * given the min/max of each range. Accounts for special values such as OFF and invalid values.
+ * given the min/max of each range. Accounts for special values such as OFF and invalid values.
* Value returned as a float primitive (to preserve precision), but is a value within the
* int-system range.
*/
@@ -168,49 +165,63 @@
}
/**
- * Updates the float setting based on a passed in int value. This is called whenever the int
- * setting changes. mWriteHistory keeps a record of the values that been written to the settings
- * from either this method or updateBrightnessIntFromFloat. This is to ensure that the value
- * being set is due to an external value being set, rather than the updateBrightness* methods.
- * The intention of this is to avoid race conditions when the setting is being changed
- * frequently and to ensure we are not reacting to settings changes from this file.
+ * Updates the settings based on a passed in int value. This is called whenever the int
+ * setting changes. mPreferredSettingValue holds the most recently updated brightness value
+ * as a float that we would like the display to be set to.
+ *
+ * We then schedule an update to both the int and float settings, but, remove all the other
+ * messages to update all, to prevent us getting stuck in a loop.
+ *
* @param value Brightness value as int to store in the float setting.
*/
private void updateBrightnessFloatFromInt(int value) {
- Object topOfQueue = mWriteHistory.peek();
- if (topOfQueue != null && topOfQueue.equals(value)) {
- mWriteHistory.poll();
- } else {
- if (brightnessFloatToInt(mPreferredSettingValue) == value) {
- return;
- }
- float newBrightnessFloat = brightnessIntToFloat(value);
- mWriteHistory.offer(newBrightnessFloat);
- mPreferredSettingValue = newBrightnessFloat;
- mDisplayManager.setBrightness(Display.DEFAULT_DISPLAY, newBrightnessFloat);
+ if (brightnessFloatToInt(mPreferredSettingValue) == value) {
+ return;
}
+
+ mPreferredSettingValue = brightnessIntToFloat(value);
+ final int newBrightnessAsIntBits = Float.floatToIntBits(mPreferredSettingValue);
+ mHandler.removeMessages(MSG_UPDATE_BOTH);
+ mHandler.obtainMessage(MSG_UPDATE_BOTH, newBrightnessAsIntBits, 0).sendToTarget();
}
/**
- * Updates the int setting based on a passed in float value. This is called whenever the float
- * setting changes. mWriteHistory keeps a record of the values that been written to the settings
- * from either this method or updateBrightnessFloatFromInt. This is to ensure that the value
- * being set is due to an external value being set, rather than the updateBrightness* methods.
- * The intention of this is to avoid race conditions when the setting is being changed
- * frequently and to ensure we are not reacting to settings changes from this file.
+ * Updates the settings based on a passed in float value. This is called whenever the float
+ * setting changes. mPreferredSettingValue holds the most recently updated brightness value
+ * as a float that we would like the display to be set to.
+ *
+ * We then schedule an update to both the int and float settings, but, remove all the other
+ * messages to update all, to prevent us getting stuck in a loop.
+ *
* @param value Brightness setting as float to store in int setting.
*/
private void updateBrightnessIntFromFloat(float value) {
- int newBrightnessInt = brightnessFloatToInt(value);
- Object topOfQueue = mWriteHistory.peek();
- if (topOfQueue != null && topOfQueue.equals(value)) {
- mWriteHistory.poll();
- } else {
- mWriteHistory.offer(newBrightnessInt);
- mPreferredSettingValue = value;
+ if (floatEquals(mPreferredSettingValue, value)) {
+ return;
+ }
+
+ mPreferredSettingValue = value;
+ final int newBrightnessAsIntBits = Float.floatToIntBits(mPreferredSettingValue);
+ mHandler.removeMessages(MSG_UPDATE_BOTH);
+ mHandler.obtainMessage(MSG_UPDATE_BOTH, newBrightnessAsIntBits, 0).sendToTarget();
+ }
+
+
+ /**
+ * Updates both setting values if they have changed
+ * mDisplayManager.setBrightness automatically checks for changes
+ * Settings.System.putIntForUser needs to be checked, to prevent an extra callback to this class
+ *
+ * @param newBrightnessFloat Brightness setting as float to store in both settings
+ */
+ private void updateBoth(float newBrightnessFloat) {
+ int newBrightnessInt = brightnessFloatToInt(newBrightnessFloat);
+ mDisplayManager.setBrightness(Display.DEFAULT_DISPLAY, newBrightnessFloat);
+ if (getScreenBrightnessInt(mContext) != newBrightnessInt) {
Settings.System.putIntForUser(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS, newBrightnessInt, UserHandle.USER_CURRENT);
}
+
}
/**
diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java
index af21f81..9ced609 100644
--- a/core/java/com/android/internal/infra/ServiceConnector.java
+++ b/core/java/com/android/internal/infra/ServiceConnector.java
@@ -228,7 +228,7 @@
private final int mBindingFlags;
private final @Nullable Function<IBinder, I> mBinderAsInterface;
private final @NonNull Handler mHandler;
- private final @NonNull Executor mExecutor;
+ protected final @NonNull Executor mExecutor;
private volatile I mService = null;
private boolean mBinding = false;
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 26d6a0c..aabcd7f 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -43,6 +43,10 @@
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR;
@@ -53,6 +57,7 @@
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -153,6 +158,11 @@
public static final int CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET = 27;
public static final int CUJ_SETTINGS_PAGE_SCROLL = 28;
public static final int CUJ_LOCKSCREEN_UNLOCK_ANIMATION = 29;
+ public static final int CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON = 30;
+ public static final int CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER = 31;
+ public static final int CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE = 32;
+ public static final int CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON = 33;
+ public static final int CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP = 34;
private static final int NO_STATSD_LOGGING = -1;
@@ -191,6 +201,11 @@
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_UNLOCK_ANIMATION,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
};
private static volatile InteractionJankMonitor sInstance;
@@ -240,6 +255,11 @@
CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET,
CUJ_SETTINGS_PAGE_SCROLL,
CUJ_LOCKSCREEN_UNLOCK_ANIMATION,
+ CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON,
+ CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER,
+ CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE,
+ CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON,
+ CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -578,6 +598,16 @@
return "SETTINGS_PAGE_SCROLL";
case CUJ_LOCKSCREEN_UNLOCK_ANIMATION:
return "LOCKSCREEN_UNLOCK_ANIMATION";
+ case CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON:
+ return "SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON";
+ case CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER:
+ return "SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER";
+ case CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE:
+ return "SHADE_APP_LAUNCH_FROM_QS_TILE";
+ case CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON:
+ return "SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON";
+ case CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP:
+ return "STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/os/AppZygoteInit.java b/core/java/com/android/internal/os/AppZygoteInit.java
index 0e83e41..f925afc 100644
--- a/core/java/com/android/internal/os/AppZygoteInit.java
+++ b/core/java/com/android/internal/os/AppZygoteInit.java
@@ -91,7 +91,9 @@
} else {
Constructor<?> ctor = cl.getConstructor();
ZygotePreload preloadObject = (ZygotePreload) ctor.newInstance();
+ Zygote.markOpenedFilesBeforePreload();
preloadObject.doPreload(appInfo);
+ Zygote.allowFilesOpenedByPreload();
}
} catch (ReflectiveOperationException e) {
Log.e(TAG, "AppZygote application preload failed for "
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index 4f2f973b..dfd561a 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -23,7 +23,10 @@
/**
* Contains power usage of an application, system service, or hardware type.
+ *
+ * @deprecated Please use BatteryStatsManager.getBatteryUsageStats instead.
*/
+@Deprecated
public class BatterySipper implements Comparable<BatterySipper> {
@UnsupportedAppUsage
public int userId;
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index b20f50d..608782a 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -59,7 +59,10 @@
*
* The caller must initialize this class as soon as activity object is ready to use (for example, in
* onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy().
+ *
+ * @deprecated Please use BatteryStatsManager.getBatteryUsageStats instead.
*/
+@Deprecated
public class BatteryStatsHelper {
static final boolean DEBUG = false;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 5dfc5fa..dab3e9f 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -341,6 +341,19 @@
}
}
+ /**
+ * Listener for the battery stats reset.
+ */
+ public interface BatteryResetListener {
+
+ /**
+ * Callback invoked immediately prior to resetting battery stats.
+ */
+ void prepareForBatteryStatsReset();
+ }
+
+ private BatteryResetListener mBatteryResetListener;
+
public interface BatteryCallback {
public void batteryNeedsCpuUpdate();
public void batteryPowerChanged(boolean onBattery);
@@ -10736,6 +10749,10 @@
}
}
+ PowerProfile getPowerProfile() {
+ return mPowerProfile;
+ }
+
/**
* Starts tracking CPU time-in-state for threads of the system server process,
* keeping a separate account of threads receiving incoming binder calls.
@@ -11184,6 +11201,10 @@
mDischargeCounter.reset(false, elapsedRealtimeUs);
}
+ public void setBatteryResetListener(BatteryResetListener batteryResetListener) {
+ mBatteryResetListener = batteryResetListener;
+ }
+
public void resetAllStatsCmdLocked() {
final long mSecUptime = mClocks.uptimeMillis();
long uptimeUs = mSecUptime * 1000;
@@ -11219,6 +11240,10 @@
}
private void resetAllStatsLocked(long uptimeMillis, long elapsedRealtimeMillis) {
+ if (mBatteryResetListener != null) {
+ mBatteryResetListener.prepareForBatteryStatsReset();
+ }
+
final long uptimeUs = uptimeMillis * 1000;
final long elapsedRealtimeUs = elapsedRealtimeMillis * 1000;
mStartCount = 0;
@@ -12116,6 +12141,15 @@
}
}
}
+
+ void reset() {
+ idleTimeMs = 0;
+ rxTimeMs = 0;
+ txTimeMs = 0;
+ energy = 0;
+ uidRxBytes.clear();
+ uidTxBytes.clear();
+ }
}
private final BluetoothActivityInfoCache mLastBluetoothActivityInfo
@@ -12142,6 +12176,15 @@
mHasBluetoothReporting = true;
+ if (info.getControllerRxTimeMillis() < mLastBluetoothActivityInfo.rxTimeMs
+ || info.getControllerTxTimeMillis() < mLastBluetoothActivityInfo.txTimeMs
+ || info.getControllerIdleTimeMillis() < mLastBluetoothActivityInfo.idleTimeMs
+ || info.getControllerEnergyUsed() < mLastBluetoothActivityInfo.energy) {
+ // A drop in accumulated Bluetooth stats is a sign of a Bluetooth crash.
+ // Reset the preserved previous snapshot in order to restart accumulating deltas.
+ mLastBluetoothActivityInfo.reset();
+ }
+
final long rxTimeMs =
info.getControllerRxTimeMillis() - mLastBluetoothActivityInfo.rxTimeMs;
final long txTimeMs =
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index 3aaccdd..8943db6 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -38,14 +38,24 @@
public class BatteryUsageStatsProvider {
private final Context mContext;
private final BatteryStats mStats;
+ private final BatteryUsageStatsStore mBatteryUsageStatsStore;
private final PowerProfile mPowerProfile;
private final Object mLock = new Object();
private List<PowerCalculator> mPowerCalculators;
public BatteryUsageStatsProvider(Context context, BatteryStats stats) {
+ this(context, stats, null);
+ }
+
+ @VisibleForTesting
+ public BatteryUsageStatsProvider(Context context, BatteryStats stats,
+ BatteryUsageStatsStore batteryUsageStatsStore) {
mContext = context;
mStats = stats;
- mPowerProfile = new PowerProfile(mContext);
+ mBatteryUsageStatsStore = batteryUsageStatsStore;
+ mPowerProfile = stats instanceof BatteryStatsImpl
+ ? ((BatteryStatsImpl) stats).getPowerProfile()
+ : new PowerProfile(context);
}
private List<PowerCalculator> getPowerCalculators() {
@@ -126,6 +136,15 @@
private BatteryUsageStats getBatteryUsageStats(BatteryUsageStatsQuery query,
long currentTimeMs) {
+ if (query.getToTimestamp() == 0) {
+ return getCurrentBatteryUsageStats(query, currentTimeMs);
+ } else {
+ return getAggregatedBatteryUsageStats(query);
+ }
+ }
+
+ private BatteryUsageStats getCurrentBatteryUsageStats(BatteryUsageStatsQuery query,
+ long currentTimeMs) {
final long realtimeUs = elapsedRealtime() * 1000;
final long uptimeUs = uptimeMillis() * 1000;
@@ -209,6 +228,25 @@
BatteryStats.STATS_SINCE_CHARGED) / 1000;
}
+ private BatteryUsageStats getAggregatedBatteryUsageStats(BatteryUsageStatsQuery query) {
+ final boolean includePowerModels = (query.getFlags()
+ & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_POWER_MODELS) != 0;
+
+ final BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(
+ mStats.getCustomEnergyConsumerNames(), includePowerModels);
+ final long[] timestamps = mBatteryUsageStatsStore.listBatteryUsageStatsTimestamps();
+ for (long timestamp : timestamps) {
+ if (timestamp > query.getFromTimestamp() && timestamp <= query.getToTimestamp()) {
+ final BatteryUsageStats snapshot =
+ mBatteryUsageStatsStore.loadBatteryUsageStats(timestamp);
+ if (snapshot != null) {
+ builder.add(snapshot);
+ }
+ }
+ }
+ return builder.build();
+ }
+
private long elapsedRealtime() {
if (mStats instanceof BatteryStatsImpl) {
return ((BatteryStatsImpl) mStats).mClocks.elapsedRealtime();
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsStore.java b/core/java/com/android/internal/os/BatteryUsageStatsStore.java
new file mode 100644
index 0000000..5c97602
--- /dev/null
+++ b/core/java/com/android/internal/os/BatteryUsageStatsStore.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.Handler;
+import android.util.AtomicFile;
+import android.util.LongArray;
+import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.StandardOpenOption;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeMap;
+
+/**
+ * A storage mechanism for BatteryUsageStats snapshots.
+ */
+public class BatteryUsageStatsStore {
+ private static final String TAG = "BatteryUsageStatsStore";
+
+ private static final List<BatteryUsageStatsQuery> BATTERY_USAGE_STATS_QUERY = List.of(
+ new BatteryUsageStatsQuery.Builder()
+ .setMaxStatsAgeMs(0)
+ .includePowerModels()
+ .build());
+ private static final String BATTERY_USAGE_STATS_DIR = "battery-usage-stats";
+ private static final String SNAPSHOT_FILE_EXTENSION = ".bus";
+ private static final String DIR_LOCK_FILENAME = ".lock";
+ private static final String CONFIG_FILENAME = "config";
+ private static final String BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY =
+ "BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP";
+ private static final long MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES = 100 * 1024;
+
+ private final Context mContext;
+ private final BatteryStatsImpl mBatteryStats;
+ private final File mStoreDir;
+ private final File mLockFile;
+ private final AtomicFile mConfigFile;
+ private final long mMaxStorageBytes;
+ private final Handler mHandler;
+ private final BatteryUsageStatsProvider mBatteryUsageStatsProvider;
+
+ public BatteryUsageStatsStore(Context context, BatteryStatsImpl stats, File systemDir,
+ Handler handler) {
+ this(context, stats, systemDir, handler, MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES);
+ }
+
+ @VisibleForTesting
+ public BatteryUsageStatsStore(Context context, BatteryStatsImpl batteryStats, File systemDir,
+ Handler handler, long maxStorageBytes) {
+ mContext = context;
+ mBatteryStats = batteryStats;
+ mStoreDir = new File(systemDir, BATTERY_USAGE_STATS_DIR);
+ mLockFile = new File(mStoreDir, DIR_LOCK_FILENAME);
+ mConfigFile = new AtomicFile(new File(mStoreDir, CONFIG_FILENAME));
+ mHandler = handler;
+ mMaxStorageBytes = maxStorageBytes;
+ mBatteryStats.setBatteryResetListener(this::prepareForBatteryStatsReset);
+ mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(mContext, mBatteryStats);
+ }
+
+ private void prepareForBatteryStatsReset() {
+ final List<BatteryUsageStats> stats =
+ mBatteryUsageStatsProvider.getBatteryUsageStats(BATTERY_USAGE_STATS_QUERY);
+ if (stats.isEmpty()) {
+ Slog.wtf(TAG, "No battery usage stats generated");
+ return;
+ }
+
+ mHandler.post(() -> storeBatteryUsageStats(stats.get(0)));
+ }
+
+ private void storeBatteryUsageStats(BatteryUsageStats stats) {
+ try (FileLock lock = lockSnapshotDirectory()) {
+ if (!mStoreDir.exists()) {
+ if (!mStoreDir.mkdirs()) {
+ Slog.e(TAG,
+ "Could not create a directory for battery usage stats snapshots");
+ return;
+ }
+ }
+ File file = makeSnapshotFilename(stats.getStatsEndTimestamp());
+ try {
+ writeXmlFileLocked(stats, file);
+ } catch (Exception e) {
+ Slog.e(TAG, "Cannot save battery usage stats", e);
+ }
+
+ removeOldSnapshotsLocked();
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot lock battery usage stats directory", e);
+ }
+ }
+
+ /**
+ * Returns the timestamps of the stored BatteryUsageStats snapshots. The timestamp corresponds
+ * to the time the snapshot was taken {@link BatteryUsageStats#getStatsEndTimestamp()}.
+ */
+ public long[] listBatteryUsageStatsTimestamps() {
+ LongArray timestamps = new LongArray(100);
+ try (FileLock lock = lockSnapshotDirectory()) {
+ for (File file : mStoreDir.listFiles()) {
+ String fileName = file.getName();
+ if (fileName.endsWith(SNAPSHOT_FILE_EXTENSION)) {
+ try {
+ String fileNameWithoutExtension = fileName.substring(0,
+ fileName.length() - SNAPSHOT_FILE_EXTENSION.length());
+ timestamps.add(Long.parseLong(fileNameWithoutExtension));
+ } catch (NumberFormatException e) {
+ Slog.wtf(TAG, "Invalid format of BatteryUsageStats snapshot file name: "
+ + fileName);
+ }
+ }
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot lock battery usage stats directory", e);
+ }
+ return timestamps.toArray();
+ }
+
+ /**
+ * Reads the specified snapshot of BatteryUsageStats. Returns null if the snapshot
+ * does not exist.
+ */
+ @Nullable
+ public BatteryUsageStats loadBatteryUsageStats(long timestamp) {
+ try (FileLock lock = lockSnapshotDirectory()) {
+ File file = makeSnapshotFilename(timestamp);
+ try {
+ return readXmlFileLocked(file);
+ } catch (Exception e) {
+ Slog.e(TAG, "Cannot read battery usage stats", e);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot lock battery usage stats directory", e);
+ }
+ return null;
+ }
+
+ /**
+ * Saves the supplied timestamp of the BATTERY_USAGE_STATS_BEFORE_RESET statsd atom pull
+ * in persistent file.
+ */
+ public void setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(long timestamp) {
+ Properties props = new Properties();
+ try (FileLock lock = lockSnapshotDirectory()) {
+ try (InputStream in = mConfigFile.openRead()) {
+ props.load(in);
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot load config file " + mConfigFile, e);
+ }
+ props.put(BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY,
+ String.valueOf(timestamp));
+ FileOutputStream out = null;
+ try {
+ out = mConfigFile.startWrite();
+ props.store(out, "Statsd atom pull timestamps");
+ mConfigFile.finishWrite(out);
+ } catch (IOException e) {
+ mConfigFile.failWrite(out);
+ Slog.e(TAG, "Cannot save config file " + mConfigFile, e);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot lock battery usage stats directory", e);
+ }
+ }
+
+ /**
+ * Retrieves the previously saved timestamp of the last BATTERY_USAGE_STATS_BEFORE_RESET
+ * statsd atom pull.
+ */
+ public long getLastBatteryUsageStatsBeforeResetAtomPullTimestamp() {
+ Properties props = new Properties();
+ try (FileLock lock = lockSnapshotDirectory()) {
+ try (InputStream in = mConfigFile.openRead()) {
+ props.load(in);
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot load config file " + mConfigFile, e);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot lock battery usage stats directory", e);
+ }
+ return Long.parseLong(
+ props.getProperty(BATTERY_USAGE_STATS_BEFORE_RESET_TIMESTAMP_PROPERTY, "0"));
+ }
+
+ private FileLock lockSnapshotDirectory() throws IOException {
+ mLockFile.getParentFile().mkdirs();
+ mLockFile.createNewFile();
+ return FileChannel.open(mLockFile.toPath(), StandardOpenOption.WRITE).lock();
+ }
+
+ /**
+ * Creates a file name by formatting the timestamp as 19-digit zero-padded number.
+ * This ensures that sorted directory list follows the chronological order.
+ */
+ private File makeSnapshotFilename(long statsEndTimestamp) {
+ return new File(mStoreDir, String.format(Locale.ENGLISH, "%019d", statsEndTimestamp)
+ + SNAPSHOT_FILE_EXTENSION);
+ }
+
+ private void writeXmlFileLocked(BatteryUsageStats stats, File file) throws IOException {
+ try (OutputStream out = new FileOutputStream(file)) {
+ TypedXmlSerializer serializer = Xml.newBinarySerializer();
+ serializer.setOutput(out, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ stats.writeXml(serializer);
+ serializer.endDocument();
+ }
+ }
+
+ private BatteryUsageStats readXmlFileLocked(File file)
+ throws IOException, XmlPullParserException {
+ try (InputStream in = new FileInputStream(file)) {
+ TypedXmlPullParser parser = Xml.newBinaryPullParser();
+ parser.setInput(in, StandardCharsets.UTF_8.name());
+ return BatteryUsageStats.createFromXml(parser);
+ }
+ }
+
+ private void removeOldSnapshotsLocked() {
+ // Read the directory list into a _sorted_ map. The alphanumeric ordering
+ // corresponds to the historical order of snapshots because the file names
+ // are timestamps zero-padded to the same length.
+ long totalSize = 0;
+ TreeMap<File, Long> mFileSizes = new TreeMap<>();
+ for (File file : mStoreDir.listFiles()) {
+ final long fileSize = file.length();
+ totalSize += fileSize;
+ if (file.getName().endsWith(SNAPSHOT_FILE_EXTENSION)) {
+ mFileSizes.put(file, fileSize);
+ }
+ }
+
+ while (totalSize > mMaxStorageBytes) {
+ final Map.Entry<File, Long> entry = mFileSizes.firstEntry();
+ if (entry == null) {
+ break;
+ }
+
+ File file = entry.getKey();
+ if (!file.delete()) {
+ Slog.e(TAG, "Cannot delete battery usage stats " + file);
+ }
+ totalSize -= entry.getValue();
+ mFileSizes.remove(file);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 6f911cb..6ce7cea 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -220,7 +220,8 @@
public CallSession callStarted(Binder binder, int code, int workSourceUid) {
noteNativeThreadId();
- if (!canCollect()) {
+ // We always want to collect data for latency if it's enabled, regardless of device state.
+ if (!mCollectLatencyData && !canCollect()) {
return null;
}
@@ -267,6 +268,11 @@
mLatencyObserver.callEnded(s);
}
+ // Latency collection has already been processed so check if the rest should be processed.
+ if (!canCollect()) {
+ return;
+ }
+
UidEntry uidEntry = null;
final boolean recordCall;
if (s.recordedCall) {
@@ -1190,15 +1196,12 @@
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) {
+ public SettingsObserver(Context context, BinderCallsStats binderCallsStats) {
super(BackgroundThread.getHandler());
mContext = context;
context.getContentResolver().registerContentObserver(mUri, false, this);
mBinderCallsStats = binderCallsStats;
- mProcessSource = processSource;
// Always kick once to ensure that we match current state
onChange();
}
diff --git a/core/java/com/android/internal/os/BinderLatencyObserver.java b/core/java/com/android/internal/os/BinderLatencyObserver.java
index ed7e172..20cf102 100644
--- a/core/java/com/android/internal/os/BinderLatencyObserver.java
+++ b/core/java/com/android/internal/os/BinderLatencyObserver.java
@@ -370,4 +370,9 @@
public Runnable getStatsdPushRunnable() {
return mLatencyObserverRunnable;
}
+
+ @VisibleForTesting
+ public int getProcessSource() {
+ return mProcessSource;
+ }
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 0c9dded..e4e28a9 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -500,6 +500,36 @@
}
/**
+ * Scans file descriptors in /proc/self/fd/, stores their metadata from readlink(2)/stat(2) when
+ * available. Saves this information in a global on native side, to be used by subsequent call
+ * to allowFilesOpenedByPreload(). Fatally fails if the FDs are of unsupported type and are not
+ * explicitly allowed. Ignores repeated invocations.
+ *
+ * Inspecting the FDs is more permissive than in forkAndSpecialize() because preload is invoked
+ * earlier and hence needs to allow a few open sockets. The checks in forkAndSpecialize()
+ * enforce that these sockets are closed when forking.
+ */
+ static void markOpenedFilesBeforePreload() {
+ nativeMarkOpenedFilesBeforePreload();
+ }
+
+ private static native void nativeMarkOpenedFilesBeforePreload();
+
+ /**
+ * By scanning /proc/self/fd/ determines file descriptor numbers in this process opened since
+ * the first call to markOpenedFilesBeforePreload(). These FDs are treated as 'owned' by the
+ * custom preload of the App Zygote - the app is responsible for not sharing data with its other
+ * processes using these FDs, including by lseek(2). File descriptor types and file names are
+ * not checked. Changes in FDs recorded by markOpenedFilesBeforePreload() are not expected and
+ * kill the current process.
+ */
+ static void allowFilesOpenedByPreload() {
+ nativeAllowFilesOpenedByPreload();
+ }
+
+ private static native void nativeAllowFilesOpenedByPreload();
+
+ /**
* Installs a seccomp filter that limits setresuid()/setresgid() to the passed-in range
* @param uidGidMin The smallest allowed uid/gid
* @param uidGidMax The largest allowed uid/gid
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 1a23cc1..134b158 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -70,12 +70,12 @@
import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.util.AndroidRuntimeException;
-import android.view.AttachedSurfaceControl;
import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.util.TypedValue;
+import android.view.AttachedSurfaceControl;
import android.view.ContextThemeWrapper;
import android.view.CrossWindowBlurListeners;
import android.view.Gravity;
@@ -3148,7 +3148,6 @@
if (cb == null || isDestroyed()) {
result = false;
} else {
- sendCloseSystemWindows("search");
int deviceId = event.getDeviceId();
SearchEvent searchEvent = null;
if (deviceId != 0) {
diff --git a/core/java/com/android/internal/util/function/DodecConsumer.java b/core/java/com/android/internal/util/function/DodecConsumer.java
new file mode 100644
index 0000000..b4d2fb9
--- /dev/null
+++ b/core/java/com/android/internal/util/function/DodecConsumer.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Consumer;
+
+
+/**
+ * A 12-argument {@link Consumer}
+ *
+ * @hide
+ */
+public interface DodecConsumer<A, B, C, D, E, F, G, H, I, J, K, L> {
+ void accept(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l);
+}
diff --git a/core/java/com/android/internal/util/function/DodecFunction.java b/core/java/com/android/internal/util/function/DodecFunction.java
new file mode 100644
index 0000000..178b2c1
--- /dev/null
+++ b/core/java/com/android/internal/util/function/DodecFunction.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Function;
+
+/**
+ * A 12-argument {@link Function}
+ *
+ * @hide
+ */
+public interface DodecFunction<A, B, C, D, E, F, G, H, I, J, K, L, R> {
+ R apply(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l);
+}
diff --git a/core/java/com/android/internal/util/function/DodecPredicate.java b/core/java/com/android/internal/util/function/DodecPredicate.java
new file mode 100644
index 0000000..d3a2b85
--- /dev/null
+++ b/core/java/com/android/internal/util/function/DodecPredicate.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util.function;
+
+import java.util.function.Predicate;
+
+/**
+ * A 12-argument {@link Predicate}
+ *
+ * @hide
+ */
+public interface DodecPredicate<A, B, C, D, E, F, G, H, I, J, K, L> {
+ boolean test(A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l);
+}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
index a60cc0f..f073c1c0 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
@@ -23,6 +23,8 @@
import com.android.internal.util.function.DecConsumer;
import com.android.internal.util.function.DecFunction;
+import com.android.internal.util.function.DodecConsumer;
+import com.android.internal.util.function.DodecFunction;
import com.android.internal.util.function.HeptConsumer;
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexConsumer;
@@ -188,7 +190,7 @@
A arg1) {
return acquire(PooledLambdaImpl.sPool,
function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -205,7 +207,7 @@
A arg1) {
return acquire(PooledLambdaImpl.sPool,
function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -222,7 +224,7 @@
A arg1) {
return acquire(PooledLambdaImpl.sPool,
function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -253,7 +255,7 @@
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -273,7 +275,7 @@
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -291,7 +293,7 @@
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -309,7 +311,7 @@
A arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -327,7 +329,7 @@
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -345,7 +347,7 @@
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -364,7 +366,7 @@
ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.BOOLEAN, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -384,7 +386,7 @@
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.BOOLEAN, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -405,7 +407,7 @@
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4, E arg5) {
return acquire(PooledLambdaImpl.sPool,
function, 5, 1, ReturnType.BOOLEAN, arg1, arg2, arg3, arg4, arg5, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -423,7 +425,7 @@
ArgumentPlaceholder<A> arg1, B arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -441,7 +443,7 @@
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -459,7 +461,7 @@
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -477,7 +479,7 @@
A arg1, ArgumentPlaceholder<B> arg2) {
return acquire(PooledLambdaImpl.sPool,
function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -509,7 +511,7 @@
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -530,7 +532,7 @@
A arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -549,7 +551,7 @@
A arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -568,7 +570,7 @@
ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -587,7 +589,7 @@
ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -606,7 +608,7 @@
A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -625,7 +627,7 @@
A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -644,7 +646,7 @@
A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -663,7 +665,7 @@
A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
return acquire(PooledLambdaImpl.sPool,
function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -696,7 +698,7 @@
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -718,7 +720,7 @@
A arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -738,7 +740,7 @@
A arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -758,7 +760,7 @@
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -778,7 +780,7 @@
ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -798,7 +800,7 @@
A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -818,7 +820,7 @@
A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -838,7 +840,7 @@
A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -858,7 +860,7 @@
A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -878,7 +880,7 @@
A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -898,7 +900,7 @@
A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
return acquire(PooledLambdaImpl.sPool,
function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -932,7 +934,7 @@
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -955,7 +957,7 @@
A arg1, B arg2, C arg3, D arg4, E arg5) {
return acquire(PooledLambdaImpl.sPool,
function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -976,7 +978,7 @@
function, A arg1, B arg2, C arg3, D arg4, E arg5) {
return acquire(PooledLambdaImpl.sPool,
function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1012,7 +1014,7 @@
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null, null, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1036,7 +1038,7 @@
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
return acquire(PooledLambdaImpl.sPool,
function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1058,7 +1060,7 @@
? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
return acquire(PooledLambdaImpl.sPool,
function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1095,7 +1097,7 @@
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1120,7 +1122,7 @@
? super G> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
return acquire(PooledLambdaImpl.sPool,
function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1144,7 +1146,7 @@
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
return acquire(PooledLambdaImpl.sPool,
function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1182,7 +1184,7 @@
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 7, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1209,7 +1211,7 @@
H arg8) {
return acquire(PooledLambdaImpl.sPool,
function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1234,7 +1236,7 @@
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8) {
return acquire(PooledLambdaImpl.sPool,
function, 8, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- null, null, null);
+ null, null, null, null);
}
/**
@@ -1274,7 +1276,7 @@
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 8, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- null, null, null);
+ null, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1302,7 +1304,7 @@
E arg5, F arg6, G arg7, H arg8, I arg9) {
return acquire(PooledLambdaImpl.sPool,
function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, null, null);
+ arg9, null, null, null);
}
/**
@@ -1328,7 +1330,7 @@
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9) {
return acquire(PooledLambdaImpl.sPool,
function, 9, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, null, null);
+ arg9, null, null, null);
}
/**
@@ -1369,7 +1371,7 @@
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 9, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, null, null);
+ arg9, null, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1398,7 +1400,7 @@
D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10) {
return acquire(PooledLambdaImpl.sPool,
function, 10, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, arg10, null);
+ arg9, arg10, null, null);
}
/**
@@ -1425,7 +1427,7 @@
A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10) {
return acquire(PooledLambdaImpl.sPool,
function, 10, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, arg10, null);
+ arg9, arg10, null, null);
}
/**
@@ -1467,7 +1469,7 @@
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 10, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
- arg8, arg9, arg10, null);
+ arg8, arg9, arg10, null, null);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
@@ -1498,7 +1500,7 @@
C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10, K arg11) {
return acquire(PooledLambdaImpl.sPool,
function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, arg10, arg11);
+ arg9, arg10, arg11, null);
}
/**
@@ -1528,7 +1530,7 @@
K arg11) {
return acquire(PooledLambdaImpl.sPool,
function, 11, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, arg10, arg11);
+ arg9, arg10, arg11, null);
}
/**
@@ -1571,7 +1573,118 @@
synchronized (Message.sPoolSync) {
PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
- arg8, arg9, arg10, arg11);
+ arg8, arg9, arg10, arg11, null);
+ return Message.obtain().setCallback(callback.recycleOnUse());
+ }
+ }
+
+ /**
+ * {@link PooledRunnable} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @param arg10 parameter supplied to {@code function} on call
+ * @param arg11 parameter supplied to {@code function} on call
+ * @param arg12 parameter supplied to {@code function} on call
+ * @return a {@link PooledRunnable}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
+ * arg11, arg12) }
+ */
+ static <A, B, C, D, E, F, G, H, I, J, K, L> PooledRunnable obtainRunnable(
+ DodecConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I, ? super J, ? super K,
+ ? super L> function,
+ A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10,
+ K arg11, L arg12) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 12, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9, arg10, arg11, arg12);
+ }
+
+ /**
+ * {@link PooledSupplier} factory
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @param arg10 parameter supplied to {@code function} on call
+ * @param arg11 parameter supplied to {@code function} on call
+ * @param arg12 parameter supplied to {@code function} on call
+ * @return a {@link PooledSupplier}, equivalent to lambda:
+ * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
+ * arg11) }
+ */
+ static <A, B, C, D, E, F, G, H, I, J, K, L, R> PooledSupplier<R> obtainSupplier(
+ DodecFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I, ? super J, ? super K, ? extends L,
+ ? extends R> function,
+ A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10,
+ K arg11, L arg12) {
+ return acquire(PooledLambdaImpl.sPool,
+ function, 11, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
+ arg9, arg10, arg11, arg12);
+ }
+
+ /**
+ * Factory of {@link Message}s that contain an
+ * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+ * {@link Message#getCallback internal callback}.
+ *
+ * The callback is equivalent to one obtainable via
+ * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)}
+ *
+ * Note that using this method with {@link android.os.Handler#handleMessage}
+ * is more efficient than the alternative of {@link android.os.Handler#post}
+ * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+ * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+ *
+ * You may optionally set a {@link Message#what} for the message if you want to be
+ * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+ * there's no need to do so
+ *
+ * @param function non-capturing lambda(typically an unbounded method reference)
+ * to be invoked on call
+ * @param arg1 parameter supplied to {@code function} on call
+ * @param arg2 parameter supplied to {@code function} on call
+ * @param arg3 parameter supplied to {@code function} on call
+ * @param arg4 parameter supplied to {@code function} on call
+ * @param arg5 parameter supplied to {@code function} on call
+ * @param arg6 parameter supplied to {@code function} on call
+ * @param arg7 parameter supplied to {@code function} on call
+ * @param arg8 parameter supplied to {@code function} on call
+ * @param arg9 parameter supplied to {@code function} on call
+ * @param arg10 parameter supplied to {@code function} on call
+ * @param arg11 parameter supplied to {@code function} on call
+ * @param arg12 parameter supplied to {@code function} on call
+ * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6,
+ * arg7, arg8, arg9, arg10, arg11) } when handled
+ */
+ static <A, B, C, D, E, F, G, H, I, J, K, L> Message obtainMessage(
+ DodecConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+ ? super G, ? super H, ? super I, ? super J, ? super K, ? super L> function,
+ A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10,
+ K arg11, L arg12) {
+ synchronized (Message.sPoolSync) {
+ PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+ function, 11, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6, arg7,
+ arg8, arg9, arg10, arg11, arg12);
return Message.obtain().setCallback(callback.recycleOnUse());
}
}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
index 1646a07..19f0816 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
@@ -28,6 +28,9 @@
import com.android.internal.util.function.DecConsumer;
import com.android.internal.util.function.DecFunction;
import com.android.internal.util.function.DecPredicate;
+import com.android.internal.util.function.DodecConsumer;
+import com.android.internal.util.function.DodecFunction;
+import com.android.internal.util.function.DodecPredicate;
import com.android.internal.util.function.HeptConsumer;
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HeptPredicate;
@@ -458,6 +461,28 @@
}
}
} break;
+
+ case 12: {
+ switch (returnType) {
+ case LambdaType.ReturnType.VOID: {
+ ((DodecConsumer) mFunc).accept(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4), popArg(5),
+ popArg(6), popArg(7), popArg(8), popArg(9), popArg(10), popArg(11));
+ return null;
+ }
+ case LambdaType.ReturnType.BOOLEAN: {
+ return (R) (Object) ((DodecPredicate) mFunc).test(popArg(0),
+ popArg(1), popArg(2), popArg(3), popArg(4),
+ popArg(5), popArg(6), popArg(7), popArg(8), popArg(9), popArg(10),
+ popArg(11));
+ }
+ case LambdaType.ReturnType.OBJECT: {
+ return (R) ((DodecFunction) mFunc).apply(popArg(0), popArg(1),
+ popArg(2), popArg(3), popArg(4), popArg(5),
+ popArg(6), popArg(7), popArg(8), popArg(9), popArg(10), popArg(11));
+ }
+ }
+ } break;
}
throw new IllegalStateException("Unknown function type: " + LambdaType.toString(funcType));
}
@@ -523,7 +548,8 @@
*/
static <E extends PooledLambda> E acquire(Pool pool, Object func,
int fNumArgs, int numPlaceholders, int fReturnType, Object a, Object b, Object c,
- Object d, Object e, Object f, Object g, Object h, Object i, Object j, Object k) {
+ Object d, Object e, Object f, Object g, Object h, Object i, Object j, Object k,
+ Object l) {
PooledLambdaImpl r = acquire(pool);
if (DEBUG) {
Log.i(LOG_TAG,
@@ -543,6 +569,7 @@
+ ", i = " + i
+ ", j = " + j
+ ", k = " + k
+ + ", l = " + l
+ ")");
}
r.mFunc = Objects.requireNonNull(func);
@@ -560,6 +587,7 @@
setIfInBounds(r.mArgs, 8, i);
setIfInBounds(r.mArgs, 9, j);
setIfInBounds(r.mArgs, 10, k);
+ setIfInBounds(r.mArgs, 11, l);
return (E) r;
}
diff --git a/core/java/com/android/internal/view/ListViewCaptureHelper.java b/core/java/com/android/internal/view/ListViewCaptureHelper.java
new file mode 100644
index 0000000..c25b4b5
--- /dev/null
+++ b/core/java/com/android/internal/view/ListViewCaptureHelper.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.view;
+
+import static com.android.internal.view.ScrollCaptureViewSupport.computeScrollAmount;
+import static com.android.internal.view.ScrollCaptureViewSupport.findScrollingReferenceView;
+import static com.android.internal.view.ScrollCaptureViewSupport.transformFromContainerToRequest;
+import static com.android.internal.view.ScrollCaptureViewSupport.transformFromRequestToContainer;
+
+import android.annotation.NonNull;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.View;
+import android.widget.ListView;
+
+/**
+ * Scroll capture support for ListView.
+ *
+ * @see ScrollCaptureViewSupport
+ */
+public class ListViewCaptureHelper implements ScrollCaptureViewHelper<ListView> {
+ private static final String TAG = "LVCaptureHelper";
+ private int mScrollDelta;
+ private boolean mScrollBarWasEnabled;
+ private int mOverScrollMode;
+
+ @Override
+ public void onPrepareForStart(@NonNull ListView view, Rect scrollBounds) {
+ mScrollDelta = 0;
+
+ mOverScrollMode = view.getOverScrollMode();
+ view.setOverScrollMode(View.OVER_SCROLL_NEVER);
+
+ mScrollBarWasEnabled = view.isVerticalScrollBarEnabled();
+ view.setVerticalScrollBarEnabled(false);
+ }
+
+ @Override
+ public ScrollResult onScrollRequested(@NonNull ListView listView, Rect scrollBounds,
+ Rect requestRect) {
+ Log.d(TAG, "-----------------------------------------------------------");
+ Log.d(TAG, "onScrollRequested(scrollBounds=" + scrollBounds + ", "
+ + "requestRect=" + requestRect + ")");
+
+ ScrollResult result = new ScrollResult();
+ result.requestedArea = new Rect(requestRect);
+ result.scrollDelta = mScrollDelta;
+ result.availableArea = new Rect(); // empty
+
+ if (!listView.isVisibleToUser() || listView.getChildCount() == 0) {
+ Log.w(TAG, "listView is empty or not visible, cannot continue");
+ return result; // result.availableArea == empty Rect
+ }
+
+ // Make requestRect relative to RecyclerView (from scrollBounds)
+ Rect requestedContainerBounds =
+ transformFromRequestToContainer(mScrollDelta, scrollBounds, requestRect);
+
+ Rect recyclerLocalVisible = new Rect();
+ listView.getLocalVisibleRect(recyclerLocalVisible);
+
+ // Expand request rect match visible bounds to center the requested rect vertically
+ Rect adjustedContainerBounds = new Rect(requestedContainerBounds);
+ int remainingHeight = recyclerLocalVisible.height() - requestedContainerBounds.height();
+ if (remainingHeight > 0) {
+ adjustedContainerBounds.inset(0, -remainingHeight / 2);
+ }
+
+ int scrollAmount = computeScrollAmount(recyclerLocalVisible, adjustedContainerBounds);
+ if (scrollAmount < 0) {
+ Log.d(TAG, "About to scroll UP (content moves down within parent)");
+ } else if (scrollAmount > 0) {
+ Log.d(TAG, "About to scroll DOWN (content moves up within parent)");
+ }
+ Log.d(TAG, "scrollAmount: " + scrollAmount);
+
+ View refView = findScrollingReferenceView(listView, scrollAmount);
+ int refTop = refView.getTop();
+
+ listView.scrollListBy(scrollAmount);
+ int scrollDistance = refTop - refView.getTop();
+ Log.d(TAG, "Parent view has scrolled vertically by " + scrollDistance + " px");
+
+ mScrollDelta += scrollDistance;
+ result.scrollDelta = mScrollDelta;
+ if (scrollDistance != 0) {
+ Log.d(TAG, "Scroll delta is now " + mScrollDelta + " px");
+ }
+
+ // Update, post-scroll
+ requestedContainerBounds = new Rect(
+ transformFromRequestToContainer(mScrollDelta, scrollBounds, requestRect));
+
+ listView.getLocalVisibleRect(recyclerLocalVisible);
+ if (requestedContainerBounds.intersect(recyclerLocalVisible)) {
+ result.availableArea = transformFromContainerToRequest(
+ mScrollDelta, scrollBounds, requestedContainerBounds);
+ }
+ Log.d(TAG, "-----------------------------------------------------------");
+ return result;
+ }
+
+
+ @Override
+ public void onPrepareForEnd(@NonNull ListView listView) {
+ // Restore original position and state
+ listView.scrollListBy(-mScrollDelta);
+ listView.setOverScrollMode(mOverScrollMode);
+ listView.setVerticalScrollBarEnabled(mScrollBarWasEnabled);
+ }
+}
diff --git a/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java b/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java
index 75bf1ce..b29cf1c 100644
--- a/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java
+++ b/core/java/com/android/internal/view/RecyclerViewCaptureHelper.java
@@ -16,7 +16,11 @@
package com.android.internal.view;
-import android.animation.ValueAnimator;
+import static com.android.internal.view.ScrollCaptureViewSupport.computeScrollAmount;
+import static com.android.internal.view.ScrollCaptureViewSupport.findScrollingReferenceView;
+import static com.android.internal.view.ScrollCaptureViewSupport.transformFromContainerToRequest;
+import static com.android.internal.view.ScrollCaptureViewSupport.transformFromRequestToContainer;
+
import android.annotation.NonNull;
import android.graphics.Rect;
import android.util.Log;
@@ -38,17 +42,10 @@
* @see ScrollCaptureViewSupport
*/
public class RecyclerViewCaptureHelper implements ScrollCaptureViewHelper<ViewGroup> {
-
- // Experiment
- private static final boolean DISABLE_ANIMATORS = false;
- // Experiment
- private static final boolean STOP_RENDER_THREAD = false;
-
private static final String TAG = "RVCaptureHelper";
private int mScrollDelta;
private boolean mScrollBarWasEnabled;
private int mOverScrollMode;
- private float mDurationScale;
@Override
public void onPrepareForStart(@NonNull ViewGroup view, Rect scrollBounds) {
@@ -59,152 +56,87 @@
mScrollBarWasEnabled = view.isVerticalScrollBarEnabled();
view.setVerticalScrollBarEnabled(false);
- if (DISABLE_ANIMATORS) {
- mDurationScale = ValueAnimator.getDurationScale();
- ValueAnimator.setDurationScale(0);
- }
- if (STOP_RENDER_THREAD) {
- view.getThreadedRenderer().stop();
- }
}
@Override
public ScrollResult onScrollRequested(@NonNull ViewGroup recyclerView, Rect scrollBounds,
Rect requestRect) {
+ Log.d(TAG, "-----------------------------------------------------------");
+ Log.d(TAG, "onScrollRequested(scrollBounds=" + scrollBounds + ", "
+ + "requestRect=" + requestRect + ")");
+
ScrollResult result = new ScrollResult();
result.requestedArea = new Rect(requestRect);
result.scrollDelta = mScrollDelta;
result.availableArea = new Rect(); // empty
- Log.d(TAG, "current scrollDelta: " + mScrollDelta);
if (!recyclerView.isVisibleToUser() || recyclerView.getChildCount() == 0) {
Log.w(TAG, "recyclerView is empty or not visible, cannot continue");
return result; // result.availableArea == empty Rect
}
- // move from scrollBounds-relative to parent-local coordinates
- Rect requestedContainerBounds = new Rect(requestRect);
- requestedContainerBounds.offset(0, -mScrollDelta);
- requestedContainerBounds.offset(scrollBounds.left, scrollBounds.top);
+ // Make requestRect relative to RecyclerView (from scrollBounds)
+ Rect requestedContainerBounds =
+ transformFromRequestToContainer(mScrollDelta, scrollBounds, requestRect);
- // requestedContainerBounds is now in recyclerview-local coordinates
- Log.d(TAG, "requestedContainerBounds: " + requestedContainerBounds);
-
- // Save a copy for later
- View anchor = findChildNearestTarget(recyclerView, requestedContainerBounds);
- if (anchor == null) {
- Log.d(TAG, "Failed to locate anchor view");
- return result; // result.availableArea == null
- }
-
- Log.d(TAG, "Anchor view:" + anchor);
- Rect requestedContentBounds = new Rect(requestedContainerBounds);
- recyclerView.offsetRectIntoDescendantCoords(anchor, requestedContentBounds);
-
- Log.d(TAG, "requestedContentBounds = " + requestedContentBounds);
- int prevAnchorTop = anchor.getTop();
- // Note: requestChildRectangleOnScreen may modify rectangle, must pass pass in a copy here
- Rect input = new Rect(requestedContentBounds);
- // Expand input rect to get the requested rect to be in the center
- int remainingHeight = recyclerView.getHeight() - recyclerView.getPaddingTop()
- - recyclerView.getPaddingBottom() - input.height();
- if (remainingHeight > 0) {
- input.inset(0, -remainingHeight / 2);
- }
- Log.d(TAG, "input (post center adjustment) = " + input);
-
- if (recyclerView.requestChildRectangleOnScreen(anchor, input, true)) {
- int scrolled = prevAnchorTop - anchor.getTop(); // inverse of movement
- Log.d(TAG, "RecyclerView scrolled by " + scrolled + " px");
- mScrollDelta += scrolled; // view.top-- is equivalent to parent.scrollY++
- result.scrollDelta = mScrollDelta;
- Log.d(TAG, "requestedContentBounds, (post-request-rect) = " + requestedContentBounds);
- }
-
- requestedContainerBounds.set(requestedContentBounds);
- recyclerView.offsetDescendantRectToMyCoords(anchor, requestedContainerBounds);
- Log.d(TAG, "requestedContainerBounds, (post-scroll): " + requestedContainerBounds);
-
- Rect recyclerLocalVisible = new Rect(scrollBounds);
+ Rect recyclerLocalVisible = new Rect();
recyclerView.getLocalVisibleRect(recyclerLocalVisible);
- Log.d(TAG, "recyclerLocalVisible: " + recyclerLocalVisible);
- if (!requestedContainerBounds.intersect(recyclerLocalVisible)) {
- // Requested area is still not visible
- Log.d(TAG, "requested bounds not visible!");
- return result;
+ // Expand request rect match visible bounds to center the requested rect vertically
+ Rect adjustedContainerBounds = new Rect(requestedContainerBounds);
+ int remainingHeight = recyclerLocalVisible.height() - requestedContainerBounds.height();
+ if (remainingHeight > 0) {
+ adjustedContainerBounds.inset(0, -remainingHeight / 2);
}
- Rect available = new Rect(requestedContainerBounds);
- available.offset(-scrollBounds.left, -scrollBounds.top);
- available.offset(0, mScrollDelta);
- result.availableArea = available;
- Log.d(TAG, "availableArea: " + result.availableArea);
+
+ int scrollAmount = computeScrollAmount(recyclerLocalVisible, adjustedContainerBounds);
+ if (scrollAmount < 0) {
+ Log.d(TAG, "About to scroll UP (content moves down within parent)");
+ } else if (scrollAmount > 0) {
+ Log.d(TAG, "About to scroll DOWN (content moves up within parent)");
+ }
+ Log.d(TAG, "scrollAmount: " + scrollAmount);
+
+ View refView = findScrollingReferenceView(recyclerView, scrollAmount);
+ int refTop = refView.getTop();
+
+ // Map the request into the child view coords
+ Rect requestedContentBounds = new Rect(adjustedContainerBounds);
+ recyclerView.offsetRectIntoDescendantCoords(refView, requestedContentBounds);
+ Log.d(TAG, "request rect, in child view space = " + requestedContentBounds);
+
+ // Note: requestChildRectangleOnScreen may modify rectangle, must pass pass in a copy here
+ Rect request = new Rect(requestedContentBounds);
+ recyclerView.requestChildRectangleOnScreen(refView, request, true);
+
+ int scrollDistance = refTop - refView.getTop();
+ Log.d(TAG, "Parent view scrolled vertically by " + scrollDistance + " px");
+
+ mScrollDelta += scrollDistance;
+ result.scrollDelta = mScrollDelta;
+ if (scrollDistance != 0) {
+ Log.d(TAG, "Scroll delta is now " + mScrollDelta + " px");
+ }
+
+ // Update, post-scroll
+ requestedContainerBounds = new Rect(
+ transformFromRequestToContainer(mScrollDelta, scrollBounds, requestRect));
+
+ // in case it might have changed (nested scrolling)
+ recyclerView.getLocalVisibleRect(recyclerLocalVisible);
+ if (requestedContainerBounds.intersect(recyclerLocalVisible)) {
+ result.availableArea = transformFromContainerToRequest(
+ mScrollDelta, scrollBounds, requestedContainerBounds);
+ }
+ Log.d(TAG, "-----------------------------------------------------------");
return result;
}
- /**
- * Find a view that is located "closest" to targetRect. Returns the first view to fully
- * vertically overlap the target targetRect. If none found, returns the view with an edge
- * nearest the target targetRect.
- *
- * @param parent the parent vertical layout
- * @param targetRect a rectangle in local coordinates of <code>parent</code>
- * @return a child view within parent matching the criteria or null
- */
- static View findChildNearestTarget(ViewGroup parent, Rect targetRect) {
- View selected = null;
- int minCenterDistance = Integer.MAX_VALUE;
- int maxOverlap = 0;
-
- // allowable center-center distance, relative to targetRect.
- // if within this range, taller views are preferred
- final float preferredRangeFromCenterPercent = 0.25f;
- final int preferredDistance =
- (int) (preferredRangeFromCenterPercent * targetRect.height());
-
- Rect parentLocalVis = new Rect();
- parent.getLocalVisibleRect(parentLocalVis);
- Log.d(TAG, "findChildNearestTarget: parentVis=" + parentLocalVis
- + " targetRect=" + targetRect);
-
- Rect frame = new Rect();
- for (int i = 0; i < parent.getChildCount(); i++) {
- final View child = parent.getChildAt(i);
- child.getHitRect(frame);
- Log.d(TAG, "child #" + i + " hitRect=" + frame);
-
- if (child.getVisibility() != View.VISIBLE) {
- Log.d(TAG, "child #" + i + " is not visible");
- continue;
- }
-
- int centerDistance = Math.abs(targetRect.centerY() - frame.centerY());
- Log.d(TAG, "child #" + i + " : center to center: " + centerDistance + "px");
-
- if (centerDistance < minCenterDistance) {
- // closer to center
- minCenterDistance = centerDistance;
- selected = child;
- } else if (frame.intersect(targetRect) && (frame.height() > preferredDistance)) {
- // within X% pixels of center, but taller
- selected = child;
- }
- }
- return selected;
- }
-
-
@Override
public void onPrepareForEnd(@NonNull ViewGroup view) {
// Restore original position and state
view.scrollBy(0, -mScrollDelta);
view.setOverScrollMode(mOverScrollMode);
view.setVerticalScrollBarEnabled(mScrollBarWasEnabled);
- if (DISABLE_ANIMATORS) {
- ValueAnimator.setDurationScale(mDurationScale);
- }
- if (STOP_RENDER_THREAD) {
- view.getThreadedRenderer().start();
- }
}
}
diff --git a/core/java/com/android/internal/view/ScrollCaptureInternal.java b/core/java/com/android/internal/view/ScrollCaptureInternal.java
index 4b9a160..ffee16a 100644
--- a/core/java/com/android/internal/view/ScrollCaptureInternal.java
+++ b/core/java/com/android/internal/view/ScrollCaptureInternal.java
@@ -25,6 +25,7 @@
import android.view.ScrollCaptureCallback;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ListView;
/**
* Provides built-in framework level Scroll Capture support for standard scrolling Views.
@@ -180,6 +181,11 @@
+ "[" + resolveId(view.getContext(), view.getId()) + "]"
+ " -> TYPE_RECYCLING");
}
+ if (view instanceof ListView) {
+ // ListView is special.
+ return new ScrollCaptureViewSupport<>((ListView) view,
+ new ListViewCaptureHelper());
+ }
return new ScrollCaptureViewSupport<>((ViewGroup) view,
new RecyclerViewCaptureHelper());
case TYPE_FIXED:
diff --git a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
index 8aa2d57..8d63a40 100644
--- a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
+++ b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java
@@ -21,10 +21,8 @@
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.HardwareRenderer;
-import android.graphics.Matrix;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.graphics.RenderNode;
import android.os.CancellationSignal;
import android.provider.Settings;
@@ -35,6 +33,7 @@
import android.view.ScrollCaptureSession;
import android.view.Surface;
import android.view.View;
+import android.view.ViewGroup;
import com.android.internal.view.ScrollCaptureViewHelper.ScrollResult;
@@ -88,6 +87,113 @@
return colorMode;
}
+ /**
+ * Maps a rect in request bounds relative space (relative to requestBounds) to container-local
+ * space, accounting for the provided value of scrollY.
+ *
+ * @param scrollY the current scroll offset to apply to rect
+ * @param requestBounds defines the local coordinate space of rect, within the container
+ * @param requestRect the rectangle to transform to container-local coordinates
+ * @return the same rectangle mapped to container bounds
+ */
+ public static Rect transformFromRequestToContainer(int scrollY, Rect requestBounds,
+ Rect requestRect) {
+ Rect requestedContainerBounds = new Rect(requestRect);
+ requestedContainerBounds.offset(0, -scrollY);
+ requestedContainerBounds.offset(requestBounds.left, requestBounds.top);
+ return requestedContainerBounds;
+ }
+
+ /**
+ * Maps a rect in container-local coordinate space to request space (relative to
+ * requestBounds), accounting for the provided value of scrollY.
+ *
+ * @param scrollY the current scroll offset of the container
+ * @param requestBounds defines the local coordinate space of rect, within the container
+ * @param containerRect the rectangle within the container local coordinate space
+ * @return the same rectangle mapped to within request bounds
+ */
+ public static Rect transformFromContainerToRequest(int scrollY, Rect requestBounds,
+ Rect containerRect) {
+ Rect requestRect = new Rect(containerRect);
+ requestRect.offset(-requestBounds.left, -requestBounds.top);
+ requestRect.offset(0, scrollY);
+ return requestRect;
+ }
+
+ /**
+ * Implements the core contract of requestRectangleOnScreen. Given a bounding rect and
+ * another rectangle, return the minimum scroll distance that will maximize the visible area
+ * of the requested rectangle.
+ *
+ * @param parentVisibleBounds the visible area
+ * @param requested the requested area
+ */
+ public static int computeScrollAmount(Rect parentVisibleBounds, Rect requested) {
+ final int height = parentVisibleBounds.height();
+ final int top = parentVisibleBounds.top;
+ final int bottom = parentVisibleBounds.bottom;
+ int scrollYDelta = 0;
+
+ if (requested.bottom > bottom && requested.top > top) {
+ // need to scroll DOWN (move views up) to get it in view:
+ // move just enough so that the entire rectangle is in view
+ // (or at least the first screen size chunk).
+
+ if (requested.height() > height) {
+ // just enough to get screen size chunk on
+ scrollYDelta += (requested.top - top);
+ } else {
+ // entire rect at bottom
+ scrollYDelta += (requested.bottom - bottom);
+ }
+ } else if (requested.top < top && requested.bottom < bottom) {
+ // need to scroll UP (move views down) to get it in view:
+ // move just enough so that entire rectangle is in view
+ // (or at least the first screen size chunk of it).
+
+ if (requested.height() > height) {
+ // screen size chunk
+ scrollYDelta -= (bottom - requested.bottom);
+ } else {
+ // entire rect at top
+ scrollYDelta -= (top - requested.top);
+ }
+ }
+ return scrollYDelta;
+ }
+
+ /**
+ * Locate a view to use as a reference, given an anticipated scrolling movement.
+ * <p>
+ * This view will be used to measure the actual movement of child views after scrolling.
+ * When scrolling down, the last (max(y)) view is used, otherwise the first (min(y)
+ * view. This helps to avoid recycling the reference view as a side effect of scrolling.
+ *
+ * @param parent the scrolling container
+ * @param expectedScrollDistance the amount of scrolling to perform
+ */
+ public static View findScrollingReferenceView(ViewGroup parent, int expectedScrollDistance) {
+ View selected = null;
+ Rect parentLocalVisible = new Rect();
+ parent.getLocalVisibleRect(parentLocalVisible);
+
+ final int childCount = parent.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = parent.getChildAt(i);
+ if (selected == null) {
+ selected = child;
+ } else if (expectedScrollDistance < 0) {
+ if (child.getTop() < selected.getTop()) {
+ selected = child;
+ }
+ } else if (child.getBottom() > selected.getBottom()) {
+ selected = child;
+ }
+ }
+ return selected;
+ }
+
@Override
public final void onScrollCaptureSearch(CancellationSignal signal, Consumer<Rect> onReady) {
if (signal.isCanceled()) {
@@ -198,12 +304,9 @@
private static final float LIGHT_RADIUS_DP = 800;
private static final String TAG = "ViewRenderer";
- private HardwareRenderer mRenderer;
- private RenderNode mCaptureRenderNode;
- private final RectF mTempRectF = new RectF();
- private final Rect mSourceRect = new Rect();
+ private final HardwareRenderer mRenderer;
+ private final RenderNode mCaptureRenderNode;
private final Rect mTempRect = new Rect();
- private final Matrix mTempMatrix = new Matrix();
private final int[] mTempLocation = new int[2];
private long mLastRenderedSourceDrawingId = -1;
private Surface mSurface;
@@ -313,11 +416,9 @@
}
private void transformToRoot(View local, Rect localRect, Rect outRect) {
- mTempMatrix.reset();
- local.transformMatrixToGlobal(mTempMatrix);
- mTempRectF.set(localRect);
- mTempMatrix.mapRect(mTempRectF);
- mTempRectF.round(outRect);
+ local.getLocationInWindow(mTempLocation);
+ outRect.set(localRect);
+ outRect.offset(mTempLocation[0], mTempLocation[1]);
}
public void setColorMode(@ColorMode int colorMode) {
diff --git a/core/java/com/android/internal/widget/CachingIconView.java b/core/java/com/android/internal/widget/CachingIconView.java
index 4a70f74..299cbe1 100644
--- a/core/java/com/android/internal/widget/CachingIconView.java
+++ b/core/java/com/android/internal/widget/CachingIconView.java
@@ -257,7 +257,7 @@
boolean hasColor = color != ColoredIconHelper.COLOR_INVALID;
if (background == null) {
// This is the pre-S style -- colored icon with no background.
- if (hasColor) {
+ if (hasColor && icon != null) {
icon.mutate().setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
}
} else {
@@ -265,7 +265,9 @@
// colorize the icon itself with the background color, creating an inverted effect.
if (hasColor) {
background.mutate().setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
- icon.mutate().setColorFilter(mBackgroundColor, PorterDuff.Mode.SRC_ATOP);
+ if (icon != null) {
+ icon.mutate().setColorFilter(mBackgroundColor, PorterDuff.Mode.SRC_ATOP);
+ }
} else {
background.mutate().setColorFilter(mBackgroundColor, PorterDuff.Mode.SRC_ATOP);
}
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index 3d054a5..02ffe8c 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -99,6 +99,12 @@
}
@Override
+ public void endComposingRegionEditInternal() {
+ // The ContentCapture service is interested in Composing-state changes.
+ mTextView.notifyContentCaptureTextChanged();
+ }
+
+ @Override
public void closeConnection() {
super.closeConnection();
synchronized(this) {
diff --git a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
index 4460e4a..ce6af49 100644
--- a/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
+++ b/core/java/com/android/internal/widget/EmphasizedNotificationButton.java
@@ -40,6 +40,7 @@
@RemoteViews.RemoteView
public class EmphasizedNotificationButton extends Button {
private final RippleDrawable mRipple;
+ private final GradientDrawable mBackground;
private boolean mPriority;
public EmphasizedNotificationButton(Context context) {
@@ -57,9 +58,10 @@
public EmphasizedNotificationButton(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- DrawableWrapper background = (DrawableWrapper) getBackground().mutate();
- mRipple = (RippleDrawable) background.getDrawable();
+ mRipple = (RippleDrawable) getBackground();
mRipple.mutate();
+ DrawableWrapper inset = (DrawableWrapper) mRipple.getDrawable(0);
+ mBackground = (GradientDrawable) inset.getDrawable();
}
@RemotableViewMethod
@@ -70,8 +72,7 @@
@RemotableViewMethod
public void setButtonBackground(ColorStateList color) {
- GradientDrawable inner = (GradientDrawable) mRipple.getDrawable(0);
- inner.setColor(color);
+ mBackground.setColor(color);
invalidate();
}
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 73c5460..3f756d7 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -1524,7 +1524,9 @@
if (virtualViewId != ExploreByTouchHelper.INVALID_ID) {
int row = (virtualViewId - VIRTUAL_BASE_VIEW_ID) / 3;
int col = (virtualViewId - VIRTUAL_BASE_VIEW_ID) % 3;
- return !mPatternDrawLookup[row][col];
+ if (row < 3) {
+ return !mPatternDrawLookup[row][col];
+ }
}
return false;
}
@@ -1570,7 +1572,6 @@
final Rect bounds = mTempRect;
final int row = ordinal / 3;
final int col = ordinal % 3;
- final CellState cell = mCellStates[row][col];
float centerX = getCenterXForColumn(col);
float centerY = getCenterYForRow(row);
float cellheight = mSquareHeight * mHitFactor * 0.5f;
diff --git a/core/java/com/android/server/AppWidgetBackupBridge.java b/core/java/com/android/server/AppWidgetBackupBridge.java
index 7d82d35..8e834a8 100644
--- a/core/java/com/android/server/AppWidgetBackupBridge.java
+++ b/core/java/com/android/server/AppWidgetBackupBridge.java
@@ -47,9 +47,9 @@
: null;
}
- public static void restoreStarting(int userId) {
+ public static void systemRestoreStarting(int userId) {
if (sAppWidgetService != null) {
- sAppWidgetService.restoreStarting(userId);
+ sAppWidgetService.systemRestoreStarting(userId);
}
}
@@ -59,9 +59,9 @@
}
}
- public static void restoreFinished(int userId) {
+ public static void systemRestoreFinished(int userId) {
if (sAppWidgetService != null) {
- sAppWidgetService.restoreFinished(userId);
+ sAppWidgetService.systemRestoreFinished(userId);
}
}
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index bd0de29..a8dcbaf 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -86,6 +86,7 @@
// and "allow-ignore-location-settings".
private static final int ALLOW_OVERRIDE_APP_RESTRICTIONS = 0x100;
private static final int ALLOW_IMPLICIT_BROADCASTS = 0x200;
+ private static final int ALLOW_VENDOR_APEX = 0x400;
private static final int ALLOW_ALL = ~0;
// property for runtime configuration differentiation
@@ -240,6 +241,7 @@
private final ArraySet<String> mRollbackWhitelistedPackages = new ArraySet<>();
private final ArraySet<String> mWhitelistedStagedInstallers = new ArraySet<>();
+ private final ArraySet<String> mAllowedVendorApexes = new ArraySet<>();
/**
* Map of system pre-defined, uniquely named actors; keys are namespace,
@@ -410,6 +412,10 @@
return mWhitelistedStagedInstallers;
}
+ public Set<String> getAllowedVendorApexes() {
+ return mAllowedVendorApexes;
+ }
+
public ArraySet<String> getAppDataIsolationWhitelistedApps() {
return mAppDataIsolationWhitelistedApps;
}
@@ -484,7 +490,7 @@
// Vendors are only allowed to customize these
int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS
- | ALLOW_ASSOCIATIONS;
+ | ALLOW_ASSOCIATIONS | ALLOW_VENDOR_APEX;
if (Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.O_MR1) {
// For backward compatibility
vendorPermissionFlag |= (ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS);
@@ -525,7 +531,8 @@
}
// Allow OEM to customize these
- int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS | ALLOW_ASSOCIATIONS;
+ int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS | ALLOW_ASSOCIATIONS
+ | ALLOW_VENDOR_APEX;
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "sysconfig"), oemPermissionFlag);
readPermissions(Environment.buildPath(
@@ -536,7 +543,8 @@
// the use of hidden APIs from the product partition.
int productPermissionFlag = ALLOW_FEATURES | ALLOW_LIBS | ALLOW_PERMISSIONS
| ALLOW_APP_CONFIGS | ALLOW_PRIVAPP_PERMISSIONS | ALLOW_HIDDENAPI_WHITELISTING
- | ALLOW_ASSOCIATIONS | ALLOW_OVERRIDE_APP_RESTRICTIONS | ALLOW_IMPLICIT_BROADCASTS;
+ | ALLOW_ASSOCIATIONS | ALLOW_OVERRIDE_APP_RESTRICTIONS | ALLOW_IMPLICIT_BROADCASTS
+ | ALLOW_VENDOR_APEX;
if (Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.R) {
// TODO(b/157393157): This must check product interface enforcement instead of
// DEVICE_INITIAL_SDK_INT for the devices without product interface enforcement.
@@ -663,6 +671,7 @@
(permissionFlag & ALLOW_OVERRIDE_APP_RESTRICTIONS) != 0;
final boolean allowImplicitBroadcasts = (permissionFlag & ALLOW_IMPLICIT_BROADCASTS)
!= 0;
+ final boolean allowVendorApex = (permissionFlag & ALLOW_VENDOR_APEX) != 0;
while (true) {
XmlUtils.nextElement(parser);
if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
@@ -1212,6 +1221,20 @@
}
XmlUtils.skipCurrentTag(parser);
} break;
+ case "allowed-vendor-apex": {
+ if (allowVendorApex) {
+ String pkgName = parser.getAttributeValue(null, "package");
+ if (pkgName == null) {
+ Slog.w(TAG, "<" + name + "> without package in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ mAllowedVendorApexes.add(pkgName);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
default: {
Slog.w(TAG, "Tag " + name + " is unknown in "
+ permFile + " at " + parser.getPositionDescription());
diff --git a/core/java/com/android/server/WidgetBackupProvider.java b/core/java/com/android/server/WidgetBackupProvider.java
index a2efbdd..5453c4d 100644
--- a/core/java/com/android/server/WidgetBackupProvider.java
+++ b/core/java/com/android/server/WidgetBackupProvider.java
@@ -28,7 +28,7 @@
public interface WidgetBackupProvider {
public List<String> getWidgetParticipants(int userId);
public byte[] getWidgetState(String packageName, int userId);
- public void restoreStarting(int userId);
+ public void systemRestoreStarting(int userId);
public void restoreWidgetState(String packageName, byte[] restoredState, int userId);
- public void restoreFinished(int userId);
+ public void systemRestoreFinished(int userId);
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 68388d98..125182c 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -84,6 +84,7 @@
android: {
srcs: [
"AndroidRuntime.cpp",
+ "com_android_internal_content_F2fsUtils.cpp",
"com_android_internal_content_NativeLibraryHelper.cpp",
"com_google_android_gles_jni_EGLImpl.cpp",
"com_google_android_gles_jni_GLImpl.cpp", // TODO: .arm
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 3debb3e..443bfce 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -189,6 +189,7 @@
extern int register_android_content_res_Configuration(JNIEnv* env);
extern int register_android_animation_PropertyValuesHolder(JNIEnv *env);
extern int register_android_security_Scrypt(JNIEnv *env);
+extern int register_com_android_internal_content_F2fsUtils(JNIEnv* env);
extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
extern int register_com_android_internal_content_om_OverlayConfig(JNIEnv *env);
extern int register_com_android_internal_net_NetworkUtilsInternal(JNIEnv* env);
@@ -1624,6 +1625,7 @@
REG_JNI(register_android_animation_PropertyValuesHolder),
REG_JNI(register_android_security_Scrypt),
+ REG_JNI(register_com_android_internal_content_F2fsUtils),
REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
REG_JNI(register_com_android_internal_os_DmabufInfoReader),
REG_JNI(register_com_android_internal_os_FuseAppLoop),
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index e47f18a..365a18d 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -566,8 +566,9 @@
env->ReleaseStringChars(clientPackageName,
reinterpret_cast<const jchar*>(rawClientName));
- sp<Camera> camera =
- Camera::connect(cameraId, clientName, Camera::USE_CALLING_UID, Camera::USE_CALLING_PID);
+ int targetSdkVersion = android_get_application_target_sdk_version();
+ sp<Camera> camera = Camera::connect(cameraId, clientName, Camera::USE_CALLING_UID,
+ Camera::USE_CALLING_PID, targetSdkVersion);
if (camera == NULL) {
return -EACCES;
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index d528428..e9e79dc3 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1775,6 +1775,16 @@
return static_cast<jint>(SurfaceComposerClient::getGPUContextPriority());
}
+static void nativeSetTransformHint(JNIEnv* env, jclass clazz, jlong nativeSurfaceControl,
+ jint transformHint) {
+ sp<SurfaceControl> surface(reinterpret_cast<SurfaceControl*>(nativeSurfaceControl));
+ if (surface == nullptr) {
+ return;
+ }
+ surface->setTransformHint(
+ ui::Transform::toRotationFlags(static_cast<ui::Rotation>(transformHint)));
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod sSurfaceControlMethods[] = {
@@ -1962,6 +1972,8 @@
(void*)nativeCreateJankDataListenerWrapper },
{"nativeGetGPUContextPriority", "()I",
(void*)nativeGetGPUContextPriority },
+ {"nativeSetTransformHint", "(JI)V",
+ (void*)nativeSetTransformHint },
// clang-format on
};
diff --git a/core/jni/com_android_internal_content_F2fsUtils.cpp b/core/jni/com_android_internal_content_F2fsUtils.cpp
new file mode 100644
index 0000000..8b9d59c
--- /dev/null
+++ b/core/jni/com_android_internal_content_F2fsUtils.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "F2fsUtils"
+
+#include "core_jni_helpers.h"
+
+#include <nativehelper/ScopedUtfChars.h>
+#include <nativehelper/jni_macros.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include <linux/f2fs.h>
+#include <linux/fs.h>
+
+#include <android-base/unique_fd.h>
+
+#include <utils/Log.h>
+
+#include <errno.h>
+#include <fcntl.h>
+
+#include <array>
+
+using namespace std::literals;
+
+namespace android {
+
+static jlong com_android_internal_content_F2fsUtils_nativeReleaseCompressedBlocks(JNIEnv *env,
+ jclass clazz,
+ jstring path) {
+ unsigned long long blkcnt;
+ int ret;
+ ScopedUtfChars filePath(env, path);
+
+ android::base::unique_fd fd(open(filePath.c_str(), O_RDONLY | O_CLOEXEC, 0));
+ if (fd < 0) {
+ ALOGW("Failed to open file: %s (%d)\n", filePath.c_str(), errno);
+ return 0;
+ }
+
+ long flags = 0;
+ ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
+ if (ret < 0) {
+ ALOGW("Failed to get flags for file: %s (%d)\n", filePath.c_str(), errno);
+ return 0;
+ }
+ if ((flags & FS_COMPR_FL) == 0) {
+ return 0;
+ }
+
+ ret = ioctl(fd, F2FS_IOC_RELEASE_COMPRESS_BLOCKS, &blkcnt);
+ if (ret < 0) {
+ return -errno;
+ }
+ return blkcnt;
+}
+
+static const std::array gMethods = {
+ MAKE_JNI_NATIVE_METHOD(
+ "nativeReleaseCompressedBlocks", "(Ljava/lang/String;)J",
+ com_android_internal_content_F2fsUtils_nativeReleaseCompressedBlocks),
+};
+
+int register_com_android_internal_content_F2fsUtils(JNIEnv *env) {
+ return RegisterMethodsOrDie(env, "com/android/internal/content/F2fsUtils", gMethods.data(),
+ gMethods.size());
+}
+
+}; // namespace android
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 4a1a272..502849e 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -27,9 +27,11 @@
#include <sys/types.h>
#include <dirent.h>
+#include <algorithm>
#include <array>
#include <atomic>
#include <functional>
+#include <iterator>
#include <list>
#include <optional>
#include <sstream>
@@ -2005,6 +2007,9 @@
__builtin_unreachable();
}
+static std::set<int>* gPreloadFds = nullptr;
+static bool gPreloadFdsExtracted = false;
+
// Utility routine to fork a process from the zygote.
pid_t zygote::ForkCommon(JNIEnv* env, bool is_system_server,
const std::vector<int>& fds_to_close,
@@ -2030,9 +2035,12 @@
__android_log_close();
AStatsSocket_close();
- // If this is the first fork for this zygote, create the open FD table. If
- // it isn't, we just need to check whether the list of open files has changed
- // (and it shouldn't in the normal case).
+ // If this is the first fork for this zygote, create the open FD table,
+ // verifying that files are of supported type and allowlisted. Otherwise (not
+ // the first fork), check that the open files have not changed. Newly open
+ // files are not expected, and will be disallowed in the future. Currently
+ // they are allowed if they pass the same checks as in the
+ // FileDescriptorTable::Create() above.
if (gOpenFdTable == nullptr) {
gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, fail_fn);
} else {
@@ -2128,7 +2136,12 @@
fds_to_ignore.push_back(gSystemServerSocketFd);
}
- pid_t pid = zygote::ForkCommon(env, false, fds_to_close, fds_to_ignore, true);
+ if (gPreloadFds && gPreloadFdsExtracted) {
+ fds_to_ignore.insert(fds_to_ignore.end(), gPreloadFds->begin(), gPreloadFds->end());
+ }
+
+ pid_t pid = zygote::ForkCommon(env, /* is_system_server= */ false, fds_to_close, fds_to_ignore,
+ true);
if (pid == 0) {
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, capabilities, capabilities,
@@ -2265,6 +2278,10 @@
}
fds_to_ignore.push_back(gSystemServerSocketFd);
}
+ if (gPreloadFds && gPreloadFdsExtracted) {
+ fds_to_ignore.insert(fds_to_ignore.end(), gPreloadFds->begin(), gPreloadFds->end());
+ }
+
return zygote::ForkCommon(env, /* is_system_server= */ false, fds_to_close,
fds_to_ignore, is_priority_fork == JNI_TRUE, purge);
}
@@ -2568,6 +2585,35 @@
#endif // defined(__aarch64__)
}
+static void com_android_internal_os_Zygote_nativeMarkOpenedFilesBeforePreload(JNIEnv* env, jclass) {
+ // Ignore invocations when too early or too late.
+ if (gPreloadFds) {
+ return;
+ }
+
+ // App Zygote Preload starts soon. Save FDs remaining open. After the
+ // preload finishes newly open files will be determined.
+ auto fail_fn = std::bind(zygote::ZygoteFailure, env, "zygote", nullptr, _1);
+ gPreloadFds = GetOpenFds(fail_fn).release();
+}
+
+static void com_android_internal_os_Zygote_nativeAllowFilesOpenedByPreload(JNIEnv* env, jclass) {
+ // Ignore invocations when too early or too late.
+ if (!gPreloadFds || gPreloadFdsExtracted) {
+ return;
+ }
+
+ // Find the newly open FDs, if any.
+ auto fail_fn = std::bind(zygote::ZygoteFailure, env, "zygote", nullptr, _1);
+ std::unique_ptr<std::set<int>> current_fds = GetOpenFds(fail_fn);
+ auto difference = std::make_unique<std::set<int>>();
+ std::set_difference(current_fds->begin(), current_fds->end(), gPreloadFds->begin(),
+ gPreloadFds->end(), std::inserter(*difference, difference->end()));
+ delete gPreloadFds;
+ gPreloadFds = difference.release();
+ gPreloadFdsExtracted = true;
+}
+
static const JNINativeMethod gMethods[] = {
{"nativeForkAndSpecialize",
"(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/"
@@ -2616,6 +2662,10 @@
(void*)com_android_internal_os_Zygote_nativeSupportsTaggedPointers},
{"nativeCurrentTaggingLevel", "()I",
(void*)com_android_internal_os_Zygote_nativeCurrentTaggingLevel},
+ {"nativeMarkOpenedFilesBeforePreload", "()V",
+ (void*)com_android_internal_os_Zygote_nativeMarkOpenedFilesBeforePreload},
+ {"nativeAllowFilesOpenedByPreload", "()V",
+ (void*)com_android_internal_os_Zygote_nativeAllowFilesOpenedByPreload},
};
int register_com_android_internal_os_Zygote(JNIEnv* env) {
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 7fa627b..6f5cc53 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -52,7 +52,6 @@
static const char kFdPath[] = "/proc/self/fd";
-// static
FileDescriptorAllowlist* FileDescriptorAllowlist::Get() {
if (instance_ == nullptr) {
instance_ = new FileDescriptorAllowlist();
@@ -169,8 +168,8 @@
// Create a FileDescriptorInfo for a given file descriptor.
static FileDescriptorInfo* CreateFromFd(int fd, fail_fn_t fail_fn);
- // Checks whether the file descriptor associated with this object
- // refers to the same description.
+ // Checks whether the file descriptor associated with this object refers to
+ // the same description.
bool RefersToSameFile() const;
void ReopenOrDetach(fail_fn_t fail_fn) const;
@@ -185,8 +184,10 @@
const bool is_sock;
private:
+ // Constructs for sockets.
explicit FileDescriptorInfo(int fd);
+ // Constructs for non-socket file descriptors.
FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
int fd_flags, int fs_flags, off_t offset);
@@ -204,7 +205,6 @@
DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
};
-// static
FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, fail_fn_t fail_fn) {
struct stat f_stat;
// This should never happen; the zygote should always have the right set
@@ -465,42 +465,24 @@
}
}
-// static
+// TODO: Move the definitions here and eliminate the forward declarations. They
+// temporarily help making code reviews easier.
+static int ParseFd(dirent* dir_entry, int dir_fd);
+static std::unique_ptr<std::set<int>> GetOpenFdsIgnoring(const std::vector<int>& fds_to_ignore,
+ fail_fn_t fail_fn);
+
FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_ignore,
fail_fn_t fail_fn) {
- DIR* proc_fd_dir = opendir(kFdPath);
- if (proc_fd_dir == nullptr) {
- fail_fn(std::string("Unable to open directory ").append(kFdPath));
- }
-
- int dir_fd = dirfd(proc_fd_dir);
- dirent* dir_entry;
-
+ std::unique_ptr<std::set<int>> open_fds = GetOpenFdsIgnoring(fds_to_ignore, fail_fn);
std::unordered_map<int, FileDescriptorInfo*> open_fd_map;
- while ((dir_entry = readdir(proc_fd_dir)) != nullptr) {
- const int fd = ParseFd(dir_entry, dir_fd);
- if (fd == -1) {
- continue;
- }
-
- if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
- continue;
- }
-
+ for (auto fd : *open_fds) {
open_fd_map[fd] = FileDescriptorInfo::CreateFromFd(fd, fail_fn);
}
-
- if (closedir(proc_fd_dir) == -1) {
- fail_fn("Unable to close directory");
- }
-
return new FileDescriptorTable(open_fd_map);
}
-void FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore, fail_fn_t fail_fn) {
- std::set<int> open_fds;
-
- // First get the list of open descriptors.
+static std::unique_ptr<std::set<int>> GetOpenFdsIgnoring(const std::vector<int>& fds_to_ignore,
+ fail_fn_t fail_fn) {
DIR* proc_fd_dir = opendir(kFdPath);
if (proc_fd_dir == nullptr) {
fail_fn(android::base::StringPrintf("Unable to open directory %s: %s",
@@ -508,6 +490,7 @@
strerror(errno)));
}
+ auto result = std::make_unique<std::set<int>>();
int dir_fd = dirfd(proc_fd_dir);
dirent* dir_entry;
while ((dir_entry = readdir(proc_fd_dir)) != nullptr) {
@@ -520,14 +503,26 @@
continue;
}
- open_fds.insert(fd);
+ result->insert(fd);
}
if (closedir(proc_fd_dir) == -1) {
fail_fn(android::base::StringPrintf("Unable to close directory: %s", strerror(errno)));
}
+ return result;
+}
- RestatInternal(open_fds, fail_fn);
+std::unique_ptr<std::set<int>> GetOpenFds(fail_fn_t fail_fn) {
+ const std::vector<int> nothing_to_ignore;
+ return GetOpenFdsIgnoring(nothing_to_ignore, fail_fn);
+}
+
+void FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore, fail_fn_t fail_fn) {
+ std::unique_ptr<std::set<int>> open_fds = GetOpenFdsIgnoring(fds_to_ignore, fail_fn);
+
+ // Check that the files did not change, and leave only newly opened FDs in
+ // |open_fds|.
+ RestatInternal(*open_fds, fail_fn);
}
// Reopens all file descriptors that are contained in the table.
@@ -548,6 +543,12 @@
: open_fd_map_(map) {
}
+FileDescriptorTable::~FileDescriptorTable() {
+ for (auto& it : open_fd_map_) {
+ delete it.second;
+ }
+}
+
void FileDescriptorTable::RestatInternal(std::set<int>& open_fds, fail_fn_t fail_fn) {
// ART creates a file through memfd for optimization purposes. We make sure
// there is at most one being created.
@@ -618,8 +619,7 @@
}
}
-// static
-int FileDescriptorTable::ParseFd(dirent* dir_entry, int dir_fd) {
+static int ParseFd(dirent* dir_entry, int dir_fd) {
char* end;
const int fd = strtol(dir_entry->d_name, &end, 10);
if ((*end) != '\0') {
diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h
index 14c318e..a28ebf1 100644
--- a/core/jni/fd_utils.h
+++ b/core/jni/fd_utils.h
@@ -69,6 +69,9 @@
DISALLOW_COPY_AND_ASSIGN(FileDescriptorAllowlist);
};
+// Returns the set of file descriptors currently open by the process.
+std::unique_ptr<std::set<int>> GetOpenFds(fail_fn_t fail_fn);
+
// A FileDescriptorTable is a collection of FileDescriptorInfo objects
// keyed by their FDs.
class FileDescriptorTable {
@@ -79,6 +82,14 @@
static FileDescriptorTable* Create(const std::vector<int>& fds_to_ignore,
fail_fn_t fail_fn);
+ ~FileDescriptorTable();
+
+ // Checks that the currently open FDs did not change their metadata from
+ // stat(2), readlink(2) etc. Ignores FDs from |fds_to_ignore|.
+ //
+ // Temporary: allows newly open FDs if they pass the same checks as in
+ // Create(). This will be further restricted. See TODOs in the
+ // implementation.
void Restat(const std::vector<int>& fds_to_ignore, fail_fn_t fail_fn);
// Reopens all file descriptors that are contained in the table. Returns true
@@ -91,8 +102,6 @@
void RestatInternal(std::set<int>& open_fds, fail_fn_t fail_fn);
- static int ParseFd(dirent* e, int dir_fd);
-
// Invariant: All values in this unordered_map are non-NULL.
std::unordered_map<int, FileDescriptorInfo*> open_fd_map_;
diff --git a/core/proto/android/server/apphibernationservice.proto b/core/proto/android/server/apphibernationservice.proto
index d341c4b..64c2a32 100644
--- a/core/proto/android/server/apphibernationservice.proto
+++ b/core/proto/android/server/apphibernationservice.proto
@@ -39,4 +39,5 @@
message GlobalLevelHibernationStateProto {
optional string package_name = 1;
optional bool hibernated = 2;
+ optional int64 saved_byte = 3;
}
\ No newline at end of file
diff --git a/core/res/res/drawable/btn_notification_emphasized.xml b/core/res/res/drawable/btn_notification_emphasized.xml
index 63707ab..29c51f2a 100644
--- a/core/res/res/drawable/btn_notification_emphasized.xml
+++ b/core/res/res/drawable/btn_notification_emphasized.xml
@@ -15,13 +15,13 @@
~ limitations under the License
-->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:insetLeft="@dimen/button_inset_horizontal_material"
- android:insetTop="@dimen/button_inset_vertical_material"
- android:insetRight="@dimen/button_inset_horizontal_material"
- android:insetBottom="@dimen/button_inset_vertical_material">
- <ripple android:color="?attr/colorControlHighlight">
- <item>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?attr/colorControlHighlight">
+ <item>
+ <inset
+ android:insetLeft="@dimen/button_inset_horizontal_material"
+ android:insetTop="@dimen/button_inset_vertical_material"
+ android:insetRight="@dimen/button_inset_horizontal_material"
+ android:insetBottom="@dimen/button_inset_vertical_material">
<shape android:shape="rectangle">
<corners android:radius="@dimen/notification_action_button_radius" />
<padding android:left="12dp"
@@ -30,6 +30,6 @@
android:bottom="@dimen/button_padding_vertical_material" />
<solid android:color="@color/white" />
</shape>
- </item>
- </ripple>
-</inset>
+ </inset>
+ </item>
+</ripple>
diff --git a/core/res/res/drawable/chooser_direct_share_icon_placeholder.xml b/core/res/res/drawable/chooser_direct_share_icon_placeholder.xml
index 838cb49..bd8dba8 100644
--- a/core/res/res/drawable/chooser_direct_share_icon_placeholder.xml
+++ b/core/res/res/drawable/chooser_direct_share_icon_placeholder.xml
@@ -26,7 +26,7 @@
<group android:name="background">
<path android:pathData="M0,0 L 64,0 64,64 0,64 z"
- android:fillColor="@color/chooser_gradient_background"/>
+ android:fillColor="@android:color/transparent"/>
</group>
<!-- Gradient starts offscreen so it is not visible in the first frame before start -->
@@ -44,7 +44,7 @@
android:color="@android:color/transparent"
android:offset="0.0" />
<item
- android:color="@color/chooser_gradient_highlight"
+ android:color="@android:color/transparent"
android:offset="0.5" />
<item
android:color="@android:color/transparent"
@@ -58,7 +58,7 @@
shadow. Using clip-path is a more elegant solution but leaves awful jaggies around
the path's shape. -->
<group android:name="cover">
- <path android:fillColor="?attr/colorBackgroundFloating"
+ <path android:fillColor="@android:color/transparent"
android:pathData="M0,0 L64,0 L64,64 L0,64 L0,0 Z M59.0587325,42.453601 C60.3124932,39.2104785 61,35.6855272 61,32 C61,15.9837423 48.0162577,3 32,3 C15.9837423,3 3,15.9837423 3,32 C3,48.0162577 15.9837423,61 32,61 C35.6855272,61 39.2104785,60.3124932 42.453601,59.0587325 C44.3362195,60.2864794 46.5847839,61 49,61 C55.627417,61 61,55.627417 61,49 C61,46.5847839 60.2864794,44.3362195 59.0587325,42.453601 Z"/>
</group>
</vector>
diff --git a/core/res/res/layout/splash_screen_view.xml b/core/res/res/layout/splash_screen_view.xml
index e6d724f..0b7b49c 100644
--- a/core/res/res/layout/splash_screen_view.xml
+++ b/core/res/res/layout/splash_screen_view.xml
@@ -18,12 +18,15 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
+ android:padding="0dp"
android:orientation="vertical">
<View android:id="@+id/splashscreen_icon_view"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center"
+ android:padding="0dp"
+ android:background="@null"
android:contentDescription="@string/splash_screen_view_icon_description"/>
<View android:id="@+id/splashscreen_branding_view"
@@ -31,6 +34,8 @@
android:layout_width="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginBottom="60dp"
+ android:padding="0dp"
+ android:background="@null"
android:contentDescription="@string/splash_screen_view_branding_description"/>
</android.window.SplashScreenView>
\ No newline at end of file
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 85de02e..20a6b2b 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Probeer \'n ander vingerafdruk"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Te helder"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Probeer om dit te verstel"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Verander elke keer die posisie van jou vinger so effens"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Vingerafdruk is gestaaf"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Geen vingerafdrukke is geregistreer nie."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Hierdie toetstel het nie \'n vingerafdruksensor nie."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor is tydelik gedeaktiveer."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Sensor benodig kalibrering"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Vinger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gebruik vingerafdruk"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Gebruik vingerafdruk of skermslot"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Vingerafdrukikoon"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Gesigslot"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Skryf jou gesig weer in"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Skryf asseblief jou gesig weer in om herkenning te verbeter"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Stel Gesigslot op"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ontsluit jou foon deur daarna te kyk"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Stel meer maniere op om te ontsluit"</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"Teks na knipbord gekopieër."</string>
<string name="copied" msgid="4675902854553014676">"Gekopieer"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> het uit <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> geplak"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> van jou knipbord af geplak"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> het van jou knipbord af geplak"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> het teks geplak wat jy gekopieer het"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> het \'n prent geplak wat jy gekopieer het"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> het inhoud geplak wat jy gekopieer het"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Laat \'n program toe om te versoek dat pakkette uitgevee word."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"vra om batteryoptimerings te ignoreer"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Laat \'n program toe om toestemming te vra om batteryoptimerings vir daardie program ignoreer."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"navraag oor alle pakkette"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Laat \'n program toe om alle geïnstalleerde pakette te sien."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Klop twee keer vir zoembeheer"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Kon nie legstuk byvoeg nie."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Gaan"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 18cc4c6..589348f 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"ሌላ የጣት አሻራ ይሞክሩ"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"በጣም ብርሃናማ"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ለማስተካከል ይሞክሩ"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"በእያንዳንዱ ጊዜ የጣትዎን ቦታ በትንሹ ይለዋውጡ"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"የጣት አሻራ ትክክለኛነት ተረጋግጧል"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ምንም የጣት አሻራዎች አልተመዘገቡም።"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ይህ መሣሪያ የጣት አሻራ ዳሳሽ የለውም።"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ዳሳሽ ለጊዜው ተሰናክሏል።"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"ዳሳሽ ማስተካከልን ይፈልጋል"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ጣት <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"የጣት አሻራ ይጠቀሙ"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"የጣት አሻራ ወይም የማያ ገጽ መቆለፊያ ይጠቀሙ"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"የጣት አሻራ አዶ"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"በመልክ መክፈት"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"የእርስዎን ፊት ዳግመኛ ያስመዝግቡ"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"ማንነትን ለይቶ ማወቅን ለማሻሻል፣ እባክዎ የእርስዎን ፊት ዳግም ያስመዝግቡ"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"በመልክ መክፈትን ያዋቅሩ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ስልክዎን በመመልከት ያስከፍቱት"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"የሚከፍቱባቸው ተጨማሪ መንገዶችን ያቀናብሩ"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"አንድ መተግበሪያ የጥቅሎች ስረዛን እንዲጠይቅ ይፈቅዳል።"</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"የባትሪ ማትባቶችን ችላ ለማለት መጠየቅ"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"አንድ መተግበሪያ ለዚያ መተግበሪያ የባትሪ ማትባቶችን ችላ ለማለት እንዲጠይቅ ይፈቅድለታል።"</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"ሁሉንም ጥቅሎች ይጠይቁ"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"አንድ መተግበሪያ ሁሉንም የተጫኑ ጥቅሎችን እንዲያይ ይፈቅድለታል።"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ለአጉላ መቆጣጠሪያ ሁለት ጊዜ ነካ አድርግ"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ምግብር ማከል አልተቻለም።"</string>
<string name="ime_action_go" msgid="5536744546326495436">"ሂድ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 1d7ce15..6f09e20 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -146,7 +146,7 @@
<string name="wfcSpnFormat_wifi" msgid="1376356951297043426">"Wi-Fi"</string>
<string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"الاتصال عبر WiFi"</string>
<string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"VoWifi"</string>
- <string name="wifi_calling_off_summary" msgid="5626710010766902560">"إيقاف"</string>
+ <string name="wifi_calling_off_summary" msgid="5626710010766902560">"غير مفعّل"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"الاتصال عبر Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"الاتصال عبر شبكة الجوّال"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Wi-Fi فقط"</string>
@@ -597,6 +597,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"يمكنك تجربة بصمة إصبع أخرى."</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"الصورة ساطعة للغاية."</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"حاوِل تعديل بصمة الإصبع."</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"غيِّر موضع إصبعك قليلاً في كل مرة."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"تم مصادقة بصمة الإصبع"</string>
@@ -613,6 +614,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ليست هناك بصمات إصبع مسجَّلة."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"لا يحتوي هذا الجهاز على مستشعِر بصمات إصبع."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"تم إيقاف جهاز الاستشعار مؤقتًا."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"يحتاج المستشعر إلى المعايرة."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"الإصبع <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"استخدام بصمة الإصبع"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"استخدام بصمة الإصبع أو قفل الشاشة"</string>
@@ -622,8 +624,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"رمز بصمة الإصبع"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"فتح الجهاز بالتعرف على الوجه"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"إعادة تسجيل وجهك"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"لتحسين قدرة الجهاز على معرفة وجهك، يُرجى إعادة تسجيل الوجه."</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"إعداد ميزة \"فتح الجهاز بالتعرف على الوجه\""</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"يمكنك فتح قفل هاتفك بمجرّد النظر إلى الشاشة."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"إعداد المزيد من الطرق لفتح قفل الجهاز"</string>
@@ -1538,10 +1542,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"للسماح لتطبيق ما بطلب حذف الحِزم."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"طلب تجاهل تحسينات البطارية"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"للسماح للتطبيق بطلب الإذن لتجاهل تحسينات البطارية في هذا التطبيق."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"طلب البحث في كل الحِزم"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"يسمح هذا الإذن للتطبيق بعرض كل الحِزم المثبّتة."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"اضغط مرتين للتحكم في التكبير أو التصغير"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"تعذرت إضافة أداة."</string>
<string name="ime_action_go" msgid="5536744546326495436">"تنفيذ"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index b1bf980..2721c3e 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"অন্য এটা ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰি চাওক"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"অতি উজ্জ্বল"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"মিলাই চাওক"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"প্ৰতিবাৰতে আপোনাৰ আঙুলিটোৰ স্থান সামান্য সলনি কৰক"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ফিংগাৰপ্ৰিণ্টৰ সত্যাপন কৰা হ’ল"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"কোনো ফিংগাৰপ্ৰিণ্ট যোগ কৰা নহ\'ল।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"এই ডিভাইচটোত ফিংগাৰপ্ৰিণ্ট ছেন্সৰ নাই।"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ছেন্সৰটো সাময়িকভাৱে অক্ষম হৈ আছে।"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"ছেন্সৰৰ কেলিব্ৰেশ্বনৰ প্ৰয়োজন"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> আঙুলি"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ফিংগাৰপ্ৰিণ্ট অথবা স্ক্ৰীন লক ব্যৱহাৰ কৰক"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ফিংগাৰপ্ৰিণ্ট আইকন"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"মুখাৱয়বৰ দ্বাৰা আনলক কৰাৰ সুবিধা"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"আপোনাৰ মুখমণ্ডল পুনৰ পঞ্জীয়ণ কৰক"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"চিনাক্তকৰণৰ সুবিধাটো উন্নত কৰিবলৈ, অনুগ্ৰহ কৰি আপোনাৰ মুখমণ্ডল পুনৰ পঞ্জীয়ন কৰক"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"মুখাৱয়বৰে আনলক কৰাৰ সুবিধাটো ছেট আপ কৰক"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"আপোনাৰ ফ’নটোলৈ চাই সেইটো আনলক কৰক"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"আনলক কৰাৰ অধিক উপায় ছেট আপ কৰক"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"এপটোক পেকেজবোৰ মচাৰ অনুৰোধ কৰিবলৈ দিয়ে।"</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"বেটাৰি অপ্টিমাইজেশ্বন উপেক্ষা কৰিবলৈ বিচাৰক"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"কোনো এপক সেই এপটোৰ বাবে বেটাৰি অপ্টিমাইজেশ্বন উপেক্ষা কৰিবলৈ অনুমতি বিচাৰিবলৈ দিয়ে।"</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"আটাইবোৰ পেকেজত প্ৰশ্ন সোধক"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"এপক আটাইবোৰ ইনষ্টল কৰি থোৱা পেকেজ চাবলৈ দিয়ে।"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"জুম নিয়ন্ত্ৰণ কৰিবলৈ দুবাৰ টিপক"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ৱিজেট যোগ কৰিব পৰা নগ\'ল।"</string>
<string name="ime_action_go" msgid="5536744546326495436">"যাওক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index af66b0b..21de3da 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Başqa bir barmaq izini sınayın"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Çox işıqlıdır"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Tənzimləməyə çalışın"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Hər dəfə barmağınızın yerini bir az dəyişdirin"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Barmaq izi doğrulandı"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Barmaq izi qeydə alınmayıb."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda barmaq izi sensoru yoxdur."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor müvəqqəti deaktivdir."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Sensor tənzimlənməlidir"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Barmaq <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Barmaq izini istifadə edin"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Barmaq izi və ya ekran kilidindən istifadə edin"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Barmaq izi ikonası"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Üz ilə kiliddən çıxarma"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Üzünüzü yenidən qeydiyyatdan keçirin"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Tanınmanı təkmilləşdirmək üçün üzünüzü yenidən qeydiyyatdan keçirin"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Üz ilə kiliddən çıxarmanı ayarlayın"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefona baxaraq onu kiliddən çıxarın"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Kiliddən çıxarmağın daha çox yolunu ayarlayın"</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"Mətn panoya kopyalandı."</string>
<string name="copied" msgid="4675902854553014676">"Kopyalandı"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> tətbiqindən əlavə edilib"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> mübadilə buferinizdən əlavə edilib"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> datanı panodan əlavə edib"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kopyaladığınız mətni əlavə etdi"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kopyaladığınız şəkli əlavə etdi"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> kopyaladığınız kontenti əlavə etdi"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Tətbiqə paketlərin silinməsi sorğusunu göndərməyə icazə verir."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"batareya optimallaşdırmasını iqnor etmək üçün soruşun"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Tatareya optimallaşdırılmasını o tətbiq üçün iqnor edilməsinə icazə vermək məqsədilə soruşmağa tətbiqə icazə verilir."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"bütün paketlər üçün sorğu göndərin"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Tətbiqə bütün quraşdırılmış paketləri görmək icazəsi verir."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Zoom kontrolu üçün iki dəfə toxunun"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget əlavə edilə bilmədi."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Get"</string>
@@ -1626,7 +1628,7 @@
<string name="wireless_display_route_description" msgid="8297563323032966831">"Simsiz ekran"</string>
<string name="media_route_button_content_description" msgid="2299223698196869956">"İştirakçılar"</string>
<string name="media_route_chooser_title" msgid="6646594924991269208">"Cihaza qoş"</string>
- <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Ekranı cihaza yayımla"</string>
+ <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Ekran yayımı"</string>
<string name="media_route_chooser_searching" msgid="6119673534251329535">"Cihazlar axtarılır..."</string>
<string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"Ayarlar"</string>
<string name="media_route_controller_disconnect" msgid="7362617572732576959">"Əlaqəni kəsin"</string>
@@ -1697,15 +1699,15 @@
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"DEAKTİV"</string>
<string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> xidmətinin cihaza tam nəzarət etməsinə icazə verilsin?"</string>
<string name="accessibility_enable_service_encryption_warning" msgid="8603532708618236909">"<xliff:g id="SERVICE">%1$s</xliff:g> aktiv olarsa, cihazınız data şifrələnməsini genişləndirmək üçün ekran kilidini istifadə etməyəcək."</string>
- <string name="accessibility_service_warning_description" msgid="291674995220940133">"Tam nəzarət əlçatımlılıq ehtiyaclarınızı ödəyən bəzi tətbiqlər üçün uyğundur."</string>
+ <string name="accessibility_service_warning_description" msgid="291674995220940133">"Tam nəzarət icazəsi xüsusi imkanlara dair yardım edən tətbiqlərə lazımdır, digər tətbiqlərə lazım deyil."</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Baxış və nəzarət ekranı"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Ekrandakı bütün kontenti oxuya və kontenti digər tətbiqlərin üzərində göstərə bilər."</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Əməliyyatlara baxın və icra edin"</string>
- <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"O, tətbiq və ya avadanlıq sensoru ilə interaktivliyinizi izləyir və əvəzinizdən tətbiqlərlə qarşılıqlı əlaqəyə girir."</string>
+ <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Tətbiq və sensorlarla əlaqələrinizi izləyib tətbiqlərə adınızdan əmrlər verə bilər."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"İcazə verin"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"İmtina edin"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Funksiyanı istifadə etmək üçün onun üzərinə toxunun:"</string>
- <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Əlçatımlılıq düyməsi ilə istifadə edəcəyiniz funksiyaları seçin"</string>
+ <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Xüsusi imkanlar düyməsinin köməyilə işə salınacaq funksiyaları seçin"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Səs səviyyəsi düyməsinin qısayolu ilə istifadə edəcəyiniz funksiyaları seçin"</string>
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> deaktiv edilib"</string>
<string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Qısayolları redaktə edin"</string>
@@ -1722,7 +1724,7 @@
<string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Xüsusi imkanlar düyməsinə toxunanda istədiyiniz funksiyanı seçin:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Əlçatımlılıq jesti (iki barmağınızla ekranın aşağısından yuxarı doğru sürüşdürün) ilə istifadə edəcəyiniz funksiyanı seçin:"</string>
<string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Əlçatımlılıq jesti (üç barmağınızla ekranın aşağısından yuxarı doğru sürüşdürün) ilə istifadə edəcəyiniz funksiyanı seçin:"</string>
- <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Funksiyalar arasında keçid etmək üçün əlçatımlılıq düyməsinə toxunub saxlayın."</string>
+ <string name="accessibility_button_instructional_text" msgid="8853928358872550500">"Funksiyalar arasında keçid etmək üçün xüsusi imkanlar düyməsini basıb saxlayın."</string>
<string name="accessibility_gesture_instructional_text" msgid="9196230728837090497">"Funksiyalar arasında keçid etmək üçün iki barmağınızla yuxarı sürüşdürüb saxlayın."</string>
<string name="accessibility_gesture_3finger_instructional_text" msgid="3425123684990193765">"Funksiyalar arasında keçid etmək üçün üç barmağınızla yuxarı doğru sürüşdürüb saxlayın."</string>
<string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Böyütmə"</string>
@@ -1992,7 +1994,7 @@
<string name="pin_specific_target" msgid="7824671240625957415">"İşarələyin: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="unpin_target" msgid="3963318576590204447">"Çıxarın"</string>
<string name="unpin_specific_target" msgid="3859828252160908146">"İşarələməyin: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="app_info" msgid="6113278084877079851">"Tətbiq infosu"</string>
+ <string name="app_info" msgid="6113278084877079851">"Tətbiq haqqında"</string>
<string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="demo_starting_message" msgid="6577581216125805905">"Demo başlayır…"</string>
<string name="demo_restarting_message" msgid="1160053183701746766">"Cihaz sıfırlanır…"</string>
@@ -2139,7 +2141,7 @@
<string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"Sürətli Ayarlar"</string>
<string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"Yandırıb-söndürmə dialoqu"</string>
<string name="accessibility_system_action_lock_screen_label" msgid="5484190691945563838">"Kilid Ekranı"</string>
- <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"Ekran şəkli"</string>
+ <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"Skrinşot"</string>
<string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"Ekranda Əlçatımlılıq Qısayolu"</string>
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Ekranda Əlçatımlılıq Qısayolu Seçicisi"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Əlçatımlılıq Qısayolu"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 4cc46d9..3d88ae7 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -352,7 +352,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Dozvoljava aplikaciji da proširuje ili skuplja statusnu traku."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"prikazuje obaveštenja kao aktivnosti preko celog ekrana na zaključanom uređaju"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Omogućava aplikaciji da na zaključanom uređaju prikazuje obaveštenja kao aktivnosti preko celog ekrana."</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"instaliranje prečica"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Instaliranje prečica"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Omogućava aplikaciji da dodaje prečice na početni ekran bez intervencije korisnika."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"deinstaliranje prečica"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Omogućava aplikaciji da uklanja prečice sa početnog ekrana bez intervencije korisnika."</string>
@@ -365,11 +365,11 @@
<string name="permlab_receiveMms" msgid="4000650116674380275">"prijem tekstualnih poruka (MMS)"</string>
<string name="permdesc_receiveMms" msgid="958102423732219710">"Dozvoljava aplikaciji da prima i obrađuje MMS poruke. To znači da aplikacija može da nadgleda ili briše poruke koje se šalju uređaju, a da vam ih ne prikaže."</string>
<string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"Prosleđivanje poruka za mobilne uređaje na lokalitetu"</string>
- <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Dozvoljava aplikaciji da se vezuje za modul poruka za mobilne uređaje na lokalitetu da bi prosleđivala poruke za mobilne uređaje na lokalitetu onako kako su primljene. Obaveštenja poruka za mobilne uređaje na lokalitetu se na nekim lokacijama primaju kao upozorenja na hitne slučajeve. Zlonamerne aplikacije mogu da utiču na učinak ili ometaju rad uređaja kada se primi poruka o hitnom slučaju za mobilne uređaje na lokalitetu."</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Dozvoljava aplikaciji da se vezuje za modul poruka za mobilne uređaje na lokalitetu da bi prosleđivala poruke za mobilne uređaje na lokalitetu onako kako su primljene. Obaveštenja poruka za mobilne uređaje na lokalitetu se na nekim lokacijama primaju kao upozorenja na hitne slučajeve. Zlonamerne aplikacije mogu da utiču na performanse ili ometaju rad uređaja kada se primi poruka o hitnom slučaju za mobilne uređaje na lokalitetu."</string>
<string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Upravljanje odlaznim pozivima"</string>
<string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Omogućava aplikaciji da vidi detalje o odlaznim pozivima na uređaju i da kontroliše te pozive."</string>
<string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"čitanje poruka info servisa"</string>
- <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Omogućava aplikaciji da čita poruke info servisa koje uređaj prima. Upozorenja info servisa se na nekim lokacijama primaju kao upozorenja na hitne slučajeve. Zlonamerne aplikacije mogu da utiču na učinak ili ometaju funkcionisanje uređaja kada se primi poruka info servisa o hitnom slučaju."</string>
+ <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Omogućava aplikaciji da čita poruke info servisa koje uređaj prima. Upozorenja info servisa se na nekim lokacijama primaju kao upozorenja na hitne slučajeve. Zlonamerne aplikacije mogu da utiču na performanse ili ometaju funkcionisanje uređaja kada se primi poruka info servisa o hitnom slučaju."</string>
<string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"čitanje prijavljenih fidova"</string>
<string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"Dozvoljava aplikaciji da preuzima detalje o trenutno sinhronizovanim fidovima."</string>
<string name="permlab_sendSms" msgid="7757368721742014252">"šalje i pregleda SMS poruke"</string>
@@ -588,6 +588,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Probajte sa drugim otiskom prsta"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Previše je svetlo"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Probajte da prilagodite"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Svaki put lagano promenite položaj prsta"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisak prsta je potvrđen"</string>
@@ -604,6 +605,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije registrovan nijedan otisak prsta."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Senzor treba da se kalibriše"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Koristite otisak prsta"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Koristite otisak prsta ili zaključavanje ekrana"</string>
@@ -613,8 +615,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona otiska prsta"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Otključavanje licem"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Ponovo registrujte lice"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Da biste poboljšali prepoznavanje, ponovo registrujte lice"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Podesite otključavanje licem"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Otključajte telefon tako što ćete ga pogledati"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Podesite još načina za otključavanje"</string>
@@ -1022,7 +1026,7 @@
<string name="text_copied" msgid="2531420577879738860">"Tekst je kopiran u privremenu memoriju."</string>
<string name="copied" msgid="4675902854553014676">"Kopirano je"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"Aplikacija<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je nalepila podatke iz aplikacije <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"Sadržaj aplikacije <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je nalepljen u privr. memoriju"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je prelepio/la iz privremene memorije"</string>
<string name="pasted_text" msgid="4298871641549173733">"Aplikacija<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je nalepila tekst koji ste kopirali"</string>
<string name="pasted_image" msgid="4729097394781491022">"Aplikacija<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je nalepila sliku koju ste kopirali"</string>
<string name="pasted_content" msgid="646276353060777131">"Aplikacija<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> je nalepila sadržaj koji ste kopirali"</string>
@@ -1283,7 +1287,7 @@
<string name="heavy_weight_notification" msgid="8382784283600329576">"Aplikacija <xliff:g id="APP">%1$s</xliff:g> je pokrenuta"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Dodirnite da biste se vratili u igru"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Odaberite igru"</string>
- <string name="heavy_weight_switcher_text" msgid="6814316627367160126">"Da bi učinak bio bolji, možete da otvorite samo jednu od ovih igara odjednom."</string>
+ <string name="heavy_weight_switcher_text" msgid="6814316627367160126">"Da bi performanse bile bolje, može da bude otvorena samo jedna od ovih igara."</string>
<string name="old_app_action" msgid="725331621042848590">"Nazad na <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
<string name="new_app_action" msgid="547772182913269801">"Otvori <xliff:g id="NEW_APP">%1$s</xliff:g>"</string>
<string name="new_app_description" msgid="1958903080400806644">"<xliff:g id="OLD_APP">%1$s</xliff:g> će se zatvoriti bez čuvanja"</string>
@@ -1395,7 +1399,7 @@
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Omogućen je režim probnog korišćenja"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Obavite resetovanje na fabrička podešavanja da biste onemogućili režim probnog korišćenja."</string>
<string name="console_running_notification_title" msgid="6087888939261635904">"Serijska konzola je omogućena"</string>
- <string name="console_running_notification_message" msgid="7892751888125174039">"Učinak je smanjen. Da biste onemogući konzolu, proverite pokretački program."</string>
+ <string name="console_running_notification_message" msgid="7892751888125174039">"Performanse su smanjene. Da biste onemogući konzolu, proverite pokretački program."</string>
<string name="usb_contaminant_detected_title" msgid="4359048603069159678">"Tečnost ili nečistoća u USB portu"</string>
<string name="usb_contaminant_detected_message" msgid="7346100585390795743">"USB port je automatski isključen. Dodirnite da biste saznali više."</string>
<string name="usb_contaminant_not_detected_title" msgid="2651167729563264053">"Korišćenje USB porta je dozvoljeno"</string>
@@ -1478,10 +1482,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Omogućava da aplikacija zahteva brisanje paketa."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"traženje dozvole za ignorisanje optimizacija baterije"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Dozvoljava aplikaciji da traži dozvolu za ignorisanje optimizacija baterije za tu aplikaciju."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"slanje upita za sve pakete"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Dozvoljava aplikaciji da vidi sve instalirane pakete."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dodirnite dvaput za kontrolu zumiranja"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nije moguće dodati vidžet."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Idi"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 1423737..ab7fc6f 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -355,7 +355,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Дазваляе прыкладанню разгортваць ці згортваць радок стану."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"паказваць апавяшчэнні ў поўнаэкранным рэжыме на заблакіраванай прыладзе"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Дазваляе праграме паказваць апавяшчэнні ў поўнаэкранным рэжыме на заблакіраванай прыладзе"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"усталёўваць ярлыкі"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Стварэнне ярлыкоў"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Дазваляе праграме дадаваць ярлыкі на Галоўны экран без умяшання карыстальніка."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"выдаляць ярлыкі"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Дазваляе праграме выдаляць ярлыкі з Галоўнага экрана без умяшання карыстальніка."</string>
@@ -591,6 +591,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Паспрабуйце іншы адбітак пальца"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Занадта светла"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Паспрабуйце наладзіць"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Кожны раз крыху мяняйце пазіцыю пальца"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Адбітак пальца распазнаны"</string>
@@ -607,6 +608,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Адбіткі пальцаў не зарэгістраваны."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На гэтай прыладзе няма сканера адбіткаў пальцаў."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчык часова выключаны."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Патрабуецца каліброўка датчыка"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Палец <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Выкарыстоўваць адбітак пальца"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Выкарыстоўваць адбітак пальца ці блакіроўку экрана"</string>
@@ -616,8 +618,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Значок адбіткаў пальцаў"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Распазнаванне твару"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Паўтарыце рэгістрацыю твару"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Каб палепшыць распазнавальнасць, яшчэ раз выканайце рэгістрацыю твару"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Наладзьце распазнаванне твару"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Разблакіруйце свой тэлефон, паглядзеўшы на яго"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Наладзьце дадатковыя спосабы разблакіроўкі"</string>
@@ -1025,7 +1029,7 @@
<string name="text_copied" msgid="2531420577879738860">"Тэкст скапіраваны ў буфер абмену."</string>
<string name="copied" msgid="4675902854553014676">"Скапіравана"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"Праграма \"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\" была ўстаўлена з праграмы \"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>\""</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"Праграма (\"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\") уставіла даныя з буфера абмену"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"Праграма \"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\" уставіла даныя з буфера абмену"</string>
<string name="pasted_text" msgid="4298871641549173733">"Скапіраваны вамі тэкст устаўлены праграмай \"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\""</string>
<string name="pasted_image" msgid="4729097394781491022">"Скапіраваны вамі відарыс устаўлены праграмай \"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\""</string>
<string name="pasted_content" msgid="646276353060777131">"Скапіраванае вамі змесціва ўстаўлена праграмай \"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>\""</string>
@@ -1498,10 +1502,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Дазваляе праграме запытваць выдаленне пакетаў."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"запытваць дазвол на ігнараванне аптымізацыі акумулятара"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Дазваляе праграме запытваць дазвол на ігнараванне аптымізацыі акумулятара для гэтай праграмы."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"запыт усіх пакетаў"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Дазваляе праграме выяўляць усе ўсталяваныя пакеты."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Націсніце двойчы, каб кіраваць маштабаваннем"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Немагчыма дадаць віджэт."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Пачаць"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 92e1a86..4928baa 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Разрешава на приложението да разгъва или свива лентата на състоянието."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"показване на известия като активности на цял екран на заключено устройство"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Дава възможност на приложението да показва известия като активности на цял екран на заключено устройство"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"инсталиране на преки пътища"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Инсталиране на преки пътища"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Разрешава на приложението да добавя към началния екран преки пътища без намеса на потребителя."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"деинсталиране на преки пътища"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Разрешава на приложението да премахва преки пътища от началния екран без намеса на потребителя."</string>
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Опитайте с друг отпечатък"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Твърде светло е"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Опитайте да коригирате"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Всеки път променяйте леко позицията на пръста си"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечатъкът е удостоверен"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Няма регистрирани отпечатъци."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Това устройство няма сензор за отпечатъци."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензорът е временно деактивиран."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"За сензора се изисква калибриране"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Пръст <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Използване на отпечатък"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Използване на отпечатък или опцията за заключване на екрана"</string>
@@ -610,8 +612,8 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Икона за отпечатък"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Отключване с лице"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Регистрирайте отново лицето си"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"С цел подобряване на разпознаването регистрирайте отново лицето си"</string>
+ <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Проблем с отключването с лице"</string>
+ <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Докоснете, за да изтриете модела на лицето си, след което добавете лицето си отново"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Настройване на отключването с лице"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Отключвайте телефона си, като го погледнете"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Настройване на още начини за отключване на телефона"</string>
@@ -1458,10 +1460,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Разрешава на приложението да заявява изтриване на пакети."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"искане за пренебрегване на оптимизациите на батерията"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Разрешава на дадено приложение да иска разрешение за пренебрегване на свързаните с него оптимизации на батерията."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"заявка за всички пакети"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Разрешава на приложението да вижда всички инсталирани пакети."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Докоснете двукратно за управление на промяната на мащаба"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Приспособлението не можа да бъде добавено."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Старт"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 2a9ecf6..6f77ca6 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"অন্য আঙ্গুলের ছাপ দিয়ে চেষ্টা করুন"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"অত্যন্ত উজ্জ্বল"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"অ্যাডজাস্ট করার চেষ্টা করুন"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"প্রতিবার আঙ্গুলের ছাপ সেটআপ করার সময় আপনার আঙ্গুলের অবস্থান সামান্য পরিবর্তন করুন"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"আঙ্গুলের ছাপ যাচাই করা হয়েছে"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"কোনও আঙ্গুলের ছাপ নথিভুক্ত করা হয়নি।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"এই ডিভাইসে আঙ্গুলের ছাপ নেওয়ার সেন্সর নেই।"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"সেন্সর অস্থায়ীভাবে বন্ধ করা আছে।"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"সেন্সর ক্যালিব্রেট করতে হবে"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"আঙ্গুল <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"আঙ্গুলের ছাপ ব্যবহার করুন"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"আঙ্গুলের ছাপ অথবা স্ক্রিন লক ব্যবহার করুন"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"আঙ্গুলের ছাপ আইকন"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"\'ফেস আনলক\'"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"আপনার ফেস আবার এনরোল করুন"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"শনাক্তকরণের উন্নতি করতে আপনার ফেস আবার এনরোল করুন"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"\'ফেস আনলক\' সেট আপ করুন"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"আপনার ফোনের দিকে তাকিয়ে এটিকে আনলক করুন"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"আনলক করার জন্য বিভিন্ন উপায়ে সেট আপ করুন"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"একটি অ্যাপ্লিকেশানকে প্যাকেজগুলি মুছে দেওয়ার অনুরোধ জানাতে দেয়৷"</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ব্যাটারি অপ্টিমাইজেশন উপেক্ষা করার জন্য অনুমতি চাওয়া"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"কোনো অ্যাপের জন্য ব্যাটারি অপ্টিমাইজেশন উপেক্ষা করতে সেটিকে অনুমতির চাওয়ার মঞ্জুরি দেয়৷"</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"অন্যান্য সব প্যাকেজের তথ্য সম্পর্কিত কোয়েরি দেখুন"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"এটি কোনও অ্যাপকে সর্বদা ইনস্টল করা সব প্যাকেজ দেখতে অনুমতি দেয়।"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"জুম নিয়ন্ত্রণের জন্য দুবার ট্যাপ করুন"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"উইজেট যোগ করা যায়নি৷"</string>
<string name="ime_action_go" msgid="5536744546326495436">"যান"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index f811ac2..54dc9ad 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -352,7 +352,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Dozvoljava aplikaciji proširivanje ili sužavanje statusne trake."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"prikaz obavještenja kao aktivnosti preko cijelog ekrana na zaključanom uređaju"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Dozvoljava aplikaciji da prikazuje obavještenja kao aktivnosti preko cijelog ekrana na zaključanom uređaju"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"instaliranje prečica"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Instaliranje prečica"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Omogućava aplikaciji dodavanje prečice za početni ekran bez intervencije korisnika."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"uklanjanje prečica"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Omogućava aplikaciji uklanjanje prečice početnog ekrana bez intervencije korisnika."</string>
@@ -588,6 +588,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Pokušajte s drugim otiskom prsta"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Presvijetlo"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Pokušajte podesiti"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Svaki put blago promijenite položaj prsta"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisak prsta je potvrđen"</string>
@@ -604,6 +605,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije prijavljen nijedan otisak prsta."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Potrebno je kalibrirati senzor"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Koristi otisak prsta"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Koristi otisak prsta ili zaključavanje ekrana"</string>
@@ -613,8 +615,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona za otisak prsta"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Otključavanje licem"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Ponovo registrirajte lice"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Ponovo registrirajte lice da poboljšate prepoznavanje"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Postavite otključavanje licem"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Otključajte telefon gledajući u njega"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Postavite više načina otključavanja"</string>
@@ -1478,10 +1482,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Omogućava aplikaciji da zatraži brisanje paketa."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"traži zanemarivanje optimizacije baterije"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Omogućava aplikaciji da traži odobrenje za zanemarivanje optimizacije baterije za tu aplikaciju."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"upit za sve pakete"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Omogućava aplikaciji da pregleda sve instalirane pakete."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dodirnite dvaput za kontrolu uvećanja"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Dodavanje vidžeta nije uspjelo."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Započni"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 19762bf..b4b7032 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Permet que l\'aplicació desplegui o replegui la barra d\'estat."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"mostrar notificacions com a activitats de pantalla completa en un dispositiu bloquejat"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Permet a l\'aplicació mostrar notificacions com a activitats de pantalla completa en un dispositiu bloquejat"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"instal·lar dreceres"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Instal·lar dreceres"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Permet que una aplicació afegeixi dreceres a la pantalla d\'inici sense la intervenció de l\'usuari."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"desinstal·la dreceres"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Permet que l\'aplicació suprimeixi les dreceres de la pantalla d\'inici sense la intervenció de l\'usuari."</string>
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prova una altra empremta digital"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Hi ha massa llum"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Prova d\'ajustar l\'empremta digital"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Canvia lleugerament la posició del dit en cada intent"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"L\'empremta digital s\'ha autenticat"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No s\'ha registrat cap empremta digital."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aquest dispositiu no té sensor d\'empremtes digitals."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"El sensor està desactivat temporalment."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Cal calibrar el sensor"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dit <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utilitza l\'empremta digital"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utilitza l\'empremta digital o el bloqueig de pantalla"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icona d\'empremta digital"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Desbloqueig facial"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Torna a registrar la cara"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Per millorar el reconeixement, torna a registrar la cara"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configura Desbloqueig facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Mira el telèfon per desbloquejar-lo"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura més maneres de desbloquejar el dispositiu"</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"Text copiat al Porta-retalls."</string>
<string name="copied" msgid="4675902854553014676">"S\'ha copiat"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha enganxat dades de: <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha enganxat contingut del porta-retalls"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha enganxat dades del porta-retalls"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha enganxat text que has copiat"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha enganxat una imatge que has copiat"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha enganxat contingut que has copiat"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permet que una aplicació sol·liciti la supressió de paquets."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"Demanar permís per ignorar les optimitzacions de bateria"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permet que una aplicació demani permís per ignorar les optimitzacions de bateria per a l\'aplicació."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar tots els paquets"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permet que una aplicació vegi els paquets instal·lats."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Piqueu dos cops per controlar el zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"No s\'ha pogut afegir el widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Ves"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 95b2b55..869f62a 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -355,7 +355,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Umožňuje aplikaci rozbalit či sbalit stavový řádek."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"zobrazovat oznámení na celé obrazovce zamčeného zařízení"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Umožňuje aplikaci zobrazovat oznámení na celé obrazovce zamčeného zařízení"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"instalace zástupců"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Instalace zástupců"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Umožňuje aplikaci přidat zástupce na plochu bez zásahu uživatele."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"odinstalace zástupců"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Umožňuje aplikaci odebrat zástupce z plochy bez zásahu uživatele."</string>
@@ -591,6 +591,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Zkuste jiný otisk prstu"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Je příliš světlo"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Zkuste provést úpravu"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Pokaždé lehce změňte polohu prstu"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisk byl ověřen"</string>
@@ -607,6 +608,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nejsou zaregistrovány žádné otisky prstů."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zařízení nemá snímač otisků prstů."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je dočasně deaktivován."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Snímač vyžaduje kalibraci"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Použít otisk prstu"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Použít otisk prstu nebo zámek obrazovky"</string>
@@ -616,8 +618,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona otisku prstů"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Odemknutí obličejem"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Zaznamenejte obličej znovu"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Chcete-li rozpoznání zdokonalit, zaznamenejte obličej znovu"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Nastavte odemknutí obličejem"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefon odemknete pohledem"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Nastavte si více způsobů odemykání"</string>
@@ -1498,10 +1502,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Umožňuje aplikaci požádat o smazání balíčků."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"požádat o ignorování optimalizace využití baterie"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Povoluje aplikaci požádat o oprávnění ignorovat optimalizaci využití baterie, která pro ni je nastavena."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"zjistit všechny balíčky"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Umožňuje aplikaci načíst všechny nainstalované balíčky."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Poklepáním můžete ovládat přiblížení"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget nelze přidat."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Přejít"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 79379f1..8e9e89c 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prøv med et andet fingeraftryk"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Der er for lyst"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Prøv at justere den"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Flyt fingeren en smule hver gang"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeraftrykket blev godkendt"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Der er ikke registreret nogen fingeraftryk."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enhed har ingen fingeraftrykslæser."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensoren er midlertidigt deaktiveret."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Sensoren skal kalibreres"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Fingeraftryk <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Brug fingeraftryk"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Brug fingeraftryk eller skærmlås"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon for fingeraftryk"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Ansigtslås"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Registrer dit ansigt igen"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Registrer dit ansigt igen for at forbedre genkendelsen af det"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfigurer ansigtslås"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Lås din telefon op ved at kigge på den"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfigurer flere måder at låse op på"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Tillader, at en app anmoder om sletning af pakker."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"bede om at ignorere batterioptimeringer"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Gør det muligt for en app at bede om tilladelse til at ignorere batterioptimeringer for den pågældende app."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"forespørg om alle pakker"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Giver en app tilladelse til at se alle installerede pakker."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tryk to gange for zoomkontrol"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget kunne ikke tilføjes."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Gå"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index b8727e8..80a1302 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Anderen Fingerabdruck verwenden"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Zu hell"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Versuche, den Finger anders aufzulegen"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Ändere jedes Mal die Position deines Fingers"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerabdruck wurde authentifiziert"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Keine Fingerabdrücke erfasst."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dieses Gerät hat keinen Fingerabdrucksensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Der Sensor ist vorübergehend deaktiviert."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Sensor muss kalibriert werden"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Fingerabdruck verwenden"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Fingerabdruck oder Displaysperre verwenden"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerabdruck-Symbol"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Entsperrung per Gesichtserkennung"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Gesicht neu scannen lassen"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Für bessere Erkennung Gesicht neu scannen lassen"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Entsperrung per Gesichtserkennung einrichten"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Entsperre dein Smartphone, indem du es ansiehst"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Weitere Möglichkeiten zum Entsperren einrichten"</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"Text in Zwischenablage kopiert."</string>
<string name="copied" msgid="4675902854553014676">"Kopiert"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> hat etwas von <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> eingefügt"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> aus der Zwischenablage eingefügt"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> hat Informationen aus der Zwischenablage eingefügt"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> hat einen von dir kopierten Text eingefügt"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> hat ein von dir kopiertes Bild eingefügt"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> hat den von dir kopierten Inhalt eingefügt"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Ermöglicht der App, das Löschen von Paketen anzufordern."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"fragen, ob Akku-Leistungsoptimierungen ignoriert werden können"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Erlaubt einer App, nach der Berechtigung zum Ignorieren der Akku-Leistungsoptimierungen zu fragen."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"Alle Pakete abfragen"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Ermöglicht der App, alle installierten Pakete zu sehen."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Für Zoomeinstellung zweimal berühren"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget konnte nicht hinzugefügt werden."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Los"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 2aa10e5..a9aa27f 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Δοκιμάστε άλλο δακτυλικό αποτύπωμα"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Υπερβολικά έντονος φωτισμός"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Δοκιμάστε να το προσαρμόσετε"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Αλλάζετε ελαφρώς τη θέση του δακτύλου σας κάθε φορά."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Η ταυτότητα του δακτυλικού αποτυπώματος ελέγχθηκε"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Δεν έχουν καταχωριστεί δακτυλικά αποτυπώματα."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Αυτή η συσκευή δεν διαθέτει αισθητήρα δακτυλικού αποτυπώματος."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Ο αισθητήρας απενεργοποιήθηκε προσωρινά."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Ο αισθητήρας απαιτεί βαθμονόμηση"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Δάχτυλο <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Χρήση δακτυλικού αποτυπώματος"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Χρήση δακτυλικού αποτυπώματος ή κλειδώματος οθόνης"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Εικονίδιο δακτυλικών αποτυπωμάτων"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Ξεκλείδωμα με το πρόσωπο"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Εγγράψτε ξανά το πρόσωπό σας"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Για να βελτιώσετε την αναγνώριση, εγγράψτε ξανά το πρόσωπό σας"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Ρύθμιση της λειτουργίας Ξεκλείδωμα με το πρόσωπο"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ξεκλειδώστε το τηλέφωνό σας απλώς κοιτώντας το"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Ρυθμίστε περισσότερους τρόπους ξεκλειδώματος"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Επιτρέπει σε μια εφαρμογή να ζητά διαγραφή πακέτων."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"αίτημα αγνόησης βελτιστοποιήσεων μπαταρίας"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Επιτρέπει σε μια εφαρμογή να ζητήσει άδεια για την αγνόηση βελτιστοποιήσεων της μπαταρίας για τη συγκεκριμένη εφαρμογή."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"υποβολή ερωτήματος σε όλα τα πακέτα"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Επιτρέπει σε μια εφαρμογή να βλέπει όλα τα εγκατεστημένα πακέτα."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Πατήστε δύο φορές για έλεγχο εστίασης"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Δεν ήταν δυνατή η προσθήκη του γραφικού στοιχείου."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Μετάβαση"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 0817cb5..a05dd23 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Try another fingerprint"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Too bright"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Try adjusting"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Change the position of your finger slightly each time"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Sensor needs calibration"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Use fingerprint or screen lock"</string>
@@ -610,8 +612,8 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Face Unlock"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Re-enrol your face"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"To improve recognition, please re-enrol your face"</string>
+ <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Issue with Face Unlock"</string>
+ <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
@@ -1458,10 +1460,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Allows an application to request deletion of packages."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ask to ignore battery optimisations"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Allows an app to ask for permission to ignore battery optimisations for that app."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"query all packages"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Allows an app to see all installed packages."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tap twice for zoom control"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Couldn\'t add widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Go"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 6e3b041..f743717 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Try another fingerprint"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Too bright"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Try adjusting"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Change the position of your finger slightly each time"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Sensor needs calibration"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Use fingerprint or screen lock"</string>
@@ -610,8 +612,8 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Face Unlock"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Re-enrol your face"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"To improve recognition, please re-enrol your face"</string>
+ <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Issue with Face Unlock"</string>
+ <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
@@ -1458,10 +1460,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Allows an application to request deletion of packages."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ask to ignore battery optimisations"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Allows an app to ask for permission to ignore battery optimisations for that app."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"query all packages"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Allows an app to see all installed packages."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tap twice for zoom control"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Couldn\'t add widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Go"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 74973c4..60cb69b 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Try another fingerprint"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Too bright"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Try adjusting"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Change the position of your finger slightly each time"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Sensor needs calibration"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Use fingerprint or screen lock"</string>
@@ -610,8 +612,8 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Face Unlock"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Re-enrol your face"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"To improve recognition, please re-enrol your face"</string>
+ <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Issue with Face Unlock"</string>
+ <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
@@ -1458,10 +1460,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Allows an application to request deletion of packages."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ask to ignore battery optimisations"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Allows an app to ask for permission to ignore battery optimisations for that app."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"query all packages"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Allows an app to see all installed packages."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tap twice for zoom control"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Couldn\'t add widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Go"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 8fe3f42..168a203 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Try another fingerprint"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Too bright"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Try adjusting"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Change the position of your finger slightly each time"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Sensor needs calibration"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Use fingerprint or screen lock"</string>
@@ -610,8 +612,8 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Face Unlock"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Re-enrol your face"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"To improve recognition, please re-enrol your face"</string>
+ <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Issue with Face Unlock"</string>
+ <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
@@ -1458,10 +1460,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Allows an application to request deletion of packages."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ask to ignore battery optimisations"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Allows an app to ask for permission to ignore battery optimisations for that app."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"query all packages"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Allows an app to see all installed packages."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tap twice for zoom control"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Couldn\'t add widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Go"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 2d92123..cf932cf 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Try another fingerprint"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Too bright"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Try adjusting"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Change the position of your finger slightly each time"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerprint authenticated"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Sensor needs calibration"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Use fingerprint"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Use fingerprint or screen lock"</string>
@@ -610,8 +612,8 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Face Unlock"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Re-enroll your face"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"To improve recognition, please re-enroll your face"</string>
+ <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Issue with Face Unlock"</string>
+ <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
@@ -1458,10 +1460,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Allows an application to request deletion of packages."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ask to ignore battery optimizations"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Allows an app to ask for permission to ignore battery optimizations for that app."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"query all packages"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Allows an app to see all installed packages."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tap twice for zoom control"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Couldn\'t add widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Go"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 9102b89..0005303 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Permite que la aplicación muestre y oculte la barra de estado."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"mostrar notificaciones como actividades de pantalla completa en un dispositivo bloqueado"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Permite que la app muestre notificaciones como actividades de pantalla completa en un dispositivo bloqueado"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"instalar accesos directos"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Instalar accesos directos"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Permite que una aplicación agregue accesos directos a la pantalla principal sin que el usuario intervenga."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"desinstalar accesos directos"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Permite que la aplicación elimine accesos directos de la pantalla principal sin que el usuario intervenga."</string>
@@ -407,7 +407,7 @@
<string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"Permite que la aplicación se inicie en cuanto el sistema haya finalizado la inicialización. Esto puede ocasionar que la tablet tarde más en inicializarse y que la aplicación ralentice el funcionamiento general de la tablet al estar en ejecución constante."</string>
<string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"Permite que se active la app en cuanto el sistema haya terminado de iniciarse. Esto puede ocasionar que el dispositivo Android TV tarde más en arrancar y que la app, al estar en ejecución constante, ralentice el funcionamiento general."</string>
<string name="permdesc_receiveBootCompleted" product="default" msgid="7912677044558690092">"Permite que la aplicación se inicie en cuanto el sistema haya finalizado la inicialización. Esto puede ocasionar que el dispositivo tarde más en inicializarse y que la aplicación ralentice el funcionamiento general del dispositivo al estar en ejecución constante."</string>
- <string name="permlab_broadcastSticky" msgid="4552241916400572230">"enviar emisiones pegajosas"</string>
+ <string name="permlab_broadcastSticky" msgid="4552241916400572230">"enviar emisiones persistentes"</string>
<string name="permdesc_broadcastSticky" product="tablet" msgid="5058486069846384013">"Permite que la aplicación envíe transmisiones persistentes que permanecen después de que finaliza la transmisión. Un uso excesivo podría ralentizar la tablet o hacer que funcione de manera inestable al forzarla a utilizar mucha memoria."</string>
<string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"Permite que la app envíe transmisiones persistentes que permanecen después de que finaliza la emisión. Un uso excesivo podría ralentizar el dispositivo Android TV o forzarlo a utilizar demasiada memoria, lo que generaría un funcionamiento inestable."</string>
<string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"Permite que la aplicación envíe transmisiones persistentes que permanecen después de que finaliza la transmisión. Un uso excesivo podría ralentizar el dispositivo o hacer que funcione de manera inestable al forzarlo a utilizar mucha memoria."</string>
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prueba con otra huella dactilar"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Demasiada luz"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Prueba ajustarla"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Cambia un poco la posición del dedo cada vez"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Se autenticó la huella dactilar"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No se registraron huellas digitales."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas dactilares."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Se inhabilitó temporalmente el sensor."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Se debe calibrar el sensor"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar huella digital"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usar bloqueo de huella dactilar o pantalla"</string>
@@ -610,8 +612,8 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ícono de huella dactilar"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Desbloqueo facial"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Vuelve a registrar tu rostro"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Para mejorar el reconocimiento, vuelve a registrar tu rostro"</string>
+ <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problema con el Desbloqueo facial"</string>
+ <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Presiona para borrar el modelo de rostro y, luego, vuelve a agregar tu rostro"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configura Desbloqueo facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloquea el teléfono con solo mirarlo"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura más formas de desbloquear el dispositivo"</string>
@@ -1458,10 +1460,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permite que una aplicación solicite que se borren paquetes."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"solicitar permiso para ignorar las optimizaciones de la batería"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que una app solicite permiso para ignorar las optimizaciones de la batería."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"Búsqueda de todos los paquetes"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que una app vea todos los paquetes que se instalaron."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Presiona dos veces para obtener el control del zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"No se pudo agregar el widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Ir"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 07f805b..6520cf1 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prueba con otra huella digital"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Demasiada luz"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Prueba a mover el dedo"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Cambia ligeramente el dedo de posición cada vez"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Se ha autenticado la huella digital"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No se ha registrado ninguna huella digital."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas digitales."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"El sensor está inhabilitado en estos momentos."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Hace falta calibrar el sensor"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar huella digital"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usar huella digital o bloqueo de pantalla"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icono de huella digital"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Desbloqueo facial"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Volver a registrar la cara"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Para mejorar el reconocimiento, vuelve a registrar tu cara"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configura Desbloqueo facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloquea el teléfono con solo mirarlo"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura más formas de desbloqueo"</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"Texto copiado al portapapeles."</string>
<string name="copied" msgid="4675902854553014676">"Copiado"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha pegado contenido de <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"Se ha pegado <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> desde el portapapeles"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha pegado contenido desde el portapapeles"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha pegado texto que has copiado"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha pegado una imagen que has copiado"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha pegado contenido que has copiado"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permite a una aplicación solicitar la eliminación de paquetes."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"solicitar permiso para ignorar las optimizaciones de la batería"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que una aplicación solicite permiso para ignorar las optimizaciones de la batería."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos los paquetes"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que una aplicación vea todos los paquetes instalados."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Da dos toques para acceder al control de zoom."</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"No se ha podido añadir el widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Ir"</string>
@@ -1870,8 +1872,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizado por el administrador"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminado por el administrador"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Aceptar"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"El modo Ahorro de batería activa el tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales, ciertas funciones y algunas conexiones de red."</string>
- <string name="battery_saver_description" msgid="8518809702138617167">"El modo Ahorro de batería activa el tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales, ciertas funciones y algunas conexiones de red."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Ahorro de batería activa el tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales, ciertas funciones y algunas conexiones de red."</string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Ahorro de batería activa el tema oscuro y limita o desactiva la actividad en segundo plano, algunos efectos visuales, ciertas funciones y algunas conexiones de red."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Ahorro de datos evita que algunas aplicaciones envíen o reciban datos en segundo plano, lo que puede reducir el uso de datos. Una aplicación activa puede acceder a los datos, aunque con menos frecuencia. Esto significa que es posible que, por ejemplo, algunas imágenes no se muestren hasta que las toques."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"¿Activar Ahorro de datos?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activar"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 937161b..5435d13 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Võimaldab rakendusel laiendada või ahendada olekuriba."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"Kuva märguanded lukustatud seadmes täisekraantegevustena"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Lubab rakendusel märguandeid lukustatud seadmes täisekraantegevustena kuvada"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"otseteede installimine"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Otseteede installimine"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Lubab rakendusel lisada avakuva otseteid ilma kasutaja sekkumiseta."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"otseteede desinstallimine"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Lubab rakendusel eemaldada avakuva otseteid ilma kasutaja sekkumiseta."</string>
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Proovige teist sõrmejälge"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Liiga ere"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Proovige kohandada"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Muutke iga kord pisut oma sõrme asendit"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Sõrmejälg autenditi"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ühtegi sõrmejälge pole registreeritud."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Selles seadmes pole sõrmejäljeandurit."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Andur on ajutiselt keelatud."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Andurit on vaja kalibreerida"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Sõrmejälg <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Sõrmejälje kasutamine"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Sõrmejälje või ekraaniluku kasutamine"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Sõrmejälje ikoon"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Näoga avamine"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Registreerige oma nägu uuesti"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Tuvastamise parandamiseks registreerige oma nägu uuesti"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Näoga avamise seadistamine"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Avage telefon seda vaadates"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Seadistage rohkem viise avamiseks"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Võimaldab rakendusel taotleda pakettide kustutamist."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"küsida luba aku optimeerimise eiramiseks"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Lubab rakendusel küsida luba rakenduse aku optimeerimise eiramiseks."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"päringute esitamine kõikide pakettide kohta"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Võimaldab rakendusel näha kõiki installitud pakette."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Suumi kasutamiseks koputage kaks korda"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Vidinat ei saanud lisada."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Mine"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 3160134..b0b17a1 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Egoera-barra zabaltzeko edo tolesteko baimena ematen die aplikazioei."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"blokeatutako gailu batean jakinarazpenak pantaila osoko jarduera gisa bistaratzea"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Blokeatutako gailu batean jakinarazpenak pantaila osoko jarduera gisa bistaratzeko baimena ematen die aplikazioei"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"instalatu lasterbideak"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Instalatu lasterbideak"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Erabiltzaileak ezer egin gabe hasierako pantailan lasterbideak gehitzeko aukera ematen die aplikazioei."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"desinstalatu lasterbideak"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Erabiltzaileak ezer egin gabe hasierako pantailako lasterbideak kentzeko aukera ematen die aplikazioei."</string>
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Erabili beste hatz-marka bat"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Argi gehiegi dago"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Saiatu doituta"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Aldi bakoitzean, aldatu hatzaren posizioa apur bat"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Autentifikatu da hatz-marka"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ez da erregistratu hatz-markarik."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Gailu honek ez du hatz-marken sentsorerik."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sentsorea aldi baterako desgaitu da."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Sentsorea kalibratu egin behar da"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. hatza"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Erabili hatz-marka"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Erabili hatz-marka edo pantailaren blokeoa"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Hatz-markaren ikonoa"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Aurpegi bidez desblokeatzeko eginbidea"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Erregistratu aurpegia berriro"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Ezagutzea hobetzeko, erregistratu aurpegia berriro"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfiguratu aurpegi bidez desblokeatzeko eginbidea"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefonoa desblokeatzeko, begira iezaiozu"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfiguratu telefonoa desblokeatzeko modu gehiago"</string>
@@ -866,11 +870,11 @@
<string name="keyguard_password_enter_pin_code" msgid="6401406801060956153">"Idatzi PIN kodea"</string>
<string name="keyguard_password_enter_puk_code" msgid="3112256684547584093">"Idatzi PUKa eta PIN berria"</string>
<string name="keyguard_password_enter_puk_prompt" msgid="2825313071899938305">"PUK kodea"</string>
- <string name="keyguard_password_enter_pin_prompt" msgid="5505434724229581207">"PIN berria"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="5505434724229581207">"PIN kode berria"</string>
<string name="keyguard_password_entry_touch_hint" msgid="4032288032993261520"><font size="17">"Sakatu pasahitza idazteko"</font></string>
<string name="keyguard_password_enter_password_code" msgid="2751130557661643482">"Idatzi desblokeatzeko pasahitza"</string>
- <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"Idatzi desblokeatzeko PIN kodea"</string>
- <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"PIN kode okerra."</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"Idatzi desblokeatzeko PINa"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"PIN kodea okerra da."</string>
<string name="keyguard_label_text" msgid="3841953694564168384">"Desblokeatzeko, sakatu Menua eta, ondoren, 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"Larrialdietarako zenbakia"</string>
<string name="lockscreen_carrier_default" msgid="6192313772955399160">"Ez dago zerbitzurik"</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"Testua arbelean kopiatu da."</string>
<string name="copied" msgid="4675902854553014676">"Kopiatu da"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> aplikaziotik itsatsi da <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> aplikazioak arbeletik itsatsi du"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> aplikazioak arbeleko edukia itsatsi du"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> aplikazioak kopiatu duzun testua itsatsi du"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> aplikazioak kopiatu duzun irudia itsatsi du"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> aplikazioak kopiatu duzun edukia itsatsi du"</string>
@@ -1053,8 +1057,8 @@
<string name="last_month" msgid="1528906781083518683">"Azken hilabetea"</string>
<string name="older" msgid="1645159827884647400">"Zaharragoa"</string>
<string name="preposition_for_date" msgid="2780767868832729599">"<xliff:g id="DATE">%s</xliff:g>"</string>
- <string name="preposition_for_time" msgid="4336835286453822053">"ordua: <xliff:g id="TIME">%s</xliff:g>"</string>
- <string name="preposition_for_year" msgid="3149809685340130039">"urtea: <xliff:g id="YEAR">%s</xliff:g>"</string>
+ <string name="preposition_for_time" msgid="4336835286453822053">"<xliff:g id="TIME">%s</xliff:g>"</string>
+ <string name="preposition_for_year" msgid="3149809685340130039">"<xliff:g id="YEAR">%s</xliff:g>"</string>
<string name="day" msgid="8394717255950176156">"egun"</string>
<string name="days" msgid="4570879797423034973">"egun"</string>
<string name="hour" msgid="7796325297097314653">"ordu"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Paketeak ezabatzeko eskatzea baimentzen die aplikazioei."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"eskatu bateria-optimizazioei ez ikusi egitea"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Bateriaren optimizazioei ez ikusi egiteko baimena eskatzea baimentzen die aplikazioei."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"Kontsultatu pakete guztiak"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Instalatutako pakete guztiak ikusteko baimena ematen dio aplikazioari."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Sakatu birritan zooma kontrolatzeko"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Ezin izan da widgeta gehitu."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Joan"</string>
@@ -1649,14 +1651,14 @@
<item quantity="one">Saiatu berriro segundo bat igarotakoan.</item>
</plurals>
<string name="kg_pattern_instructions" msgid="8366024510502517748">"Marraztu eredua"</string>
- <string name="kg_sim_pin_instructions" msgid="6479401489471690359">"Idatzi SIMaren PIN kodea"</string>
+ <string name="kg_sim_pin_instructions" msgid="6479401489471690359">"Idatzi SIMaren PINa"</string>
<string name="kg_pin_instructions" msgid="7355933174673539021">"Idatzi PINa"</string>
<string name="kg_password_instructions" msgid="7179782578809398050">"Idatzi pasahitza"</string>
<string name="kg_puk_enter_puk_hint" msgid="6696187482616360994">"SIMa desgaitu egin da. Jarraitzeko, idatzi PUK kodea. Xehetasunak lortzeko, jarri operadorearekin harremanetan."</string>
<string name="kg_puk_enter_pin_hint" msgid="8190982314659429770">"Idatzi erabili nahi duzun PIN kodea"</string>
<string name="kg_enter_confirm_pin_hint" msgid="6372557107414074580">"Berretsi erabili nahi duzun PIN kodea"</string>
<string name="kg_sim_unlock_progress_dialog_message" msgid="8871937892678885545">"SIM txartela desblokeatzen…"</string>
- <string name="kg_password_wrong_pin_code" msgid="9013856346870572451">"PIN okerra."</string>
+ <string name="kg_password_wrong_pin_code" msgid="9013856346870572451">"PIN kodea okerra da."</string>
<string name="kg_invalid_sim_pin_hint" msgid="4821601451222564077">"Idatzi 4 eta 8 zenbaki arteko PINa."</string>
<string name="kg_invalid_sim_puk_hint" msgid="2539364558870734339">"PUK kodeak 8 zenbaki izan behar ditu."</string>
<string name="kg_invalid_puk" msgid="4809502818518963344">"Idatzi berriro PUK kode zuzena. Hainbat saiakera oker eginez gero, betiko desgaituko da SIMa."</string>
@@ -1729,7 +1731,7 @@
<string name="user_switched" msgid="7249833311585228097">"Erabiltzailea: <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="user_switching_message" msgid="1912993630661332336">"\"<xliff:g id="NAME">%1$s</xliff:g>\" erabiltzailera aldatzen…"</string>
<string name="user_logging_out_message" msgid="7216437629179710359">"<xliff:g id="NAME">%1$s</xliff:g> erabiltzailearen saioa amaitzen…"</string>
- <string name="owner_name" msgid="8713560351570795743">"\"Jabea\""</string>
+ <string name="owner_name" msgid="8713560351570795743">"Jabea"</string>
<string name="error_message_title" msgid="4082495589294631966">"Errorea"</string>
<string name="error_message_change_not_allowed" msgid="843159705042381454">"Administratzaileak ez du eman aldaketa egiteko baimena"</string>
<string name="app_not_found" msgid="3429506115332341800">"Ez da ekintza gauza dezakeen aplikaziorik aurkitu"</string>
@@ -1835,13 +1837,13 @@
<string name="reason_service_unavailable" msgid="5288405248063804713">"Inprimatze-zerbitzua ez dago gaituta"</string>
<string name="print_service_installed_title" msgid="6134880817336942482">"<xliff:g id="NAME">%s</xliff:g> zerbitzua instalatu da"</string>
<string name="print_service_installed_message" msgid="7005672469916968131">"Sakatu gaitzeko"</string>
- <string name="restr_pin_enter_admin_pin" msgid="1199419462726962697">"Idatzi administratzailearen PIN kodea"</string>
+ <string name="restr_pin_enter_admin_pin" msgid="1199419462726962697">"Idatzi administratzailearen PINa"</string>
<string name="restr_pin_enter_pin" msgid="373139384161304555">"Idatzi PINa"</string>
<string name="restr_pin_incorrect" msgid="3861383632940852496">"Okerra"</string>
<string name="restr_pin_enter_old_pin" msgid="7537079094090650967">"Oraingo PINa"</string>
<string name="restr_pin_enter_new_pin" msgid="3267614461844565431">"PIN berria"</string>
<string name="restr_pin_confirm_pin" msgid="7143161971614944989">"Berretsi PIN berria"</string>
- <string name="restr_pin_create_pin" msgid="917067613896366033">"Konfiguratu debekuak aldatu ahal izateko idatzi beharko den PIN kodea"</string>
+ <string name="restr_pin_create_pin" msgid="917067613896366033">"Konfiguratu debekuak aldatu ahal izateko idatzi beharko den PINa"</string>
<string name="restr_pin_error_doesnt_match" msgid="7063392698489280556">"PINak ez datoz bat. Saiatu berriro."</string>
<string name="restr_pin_error_too_short" msgid="1547007808237941065">"PINa laburregia da. Lau digitu izan behar ditu gutxienez."</string>
<plurals name="restr_pin_countdown" formatted="false" msgid="4427486903285216153">
@@ -2168,11 +2170,11 @@
<string name="miniresolver_open_in_work" msgid="152208044699347924">"Laneko profileko <xliff:g id="APP">%s</xliff:g> aplikazioan ireki nahi duzu?"</string>
<string name="miniresolver_use_personal_browser" msgid="776072682871133308">"Erabili arakatzaile pertsonala"</string>
<string name="miniresolver_use_work_browser" msgid="543575306251952994">"Erabili laneko arakatzailea"</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY" msgid="8050953231914637819">"SIMaren sarearen bidez desblokeatzeko PIN kodea"</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY" msgid="8050953231914637819">"SIMaren sarearen bidez desblokeatzeko PINa"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ENTRY" msgid="7164399703751688214">"SIMaren sareko azpimultzoaren bidez desblokeatzeko PINa"</string>
- <string name="PERSOSUBSTATE_SIM_CORPORATE_ENTRY" msgid="4447629474818217364">"Enpresaren SIMaren bidez desblokeatzeko PIN kodea"</string>
- <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ENTRY" msgid="973059024670737358">"SIMaren zerbitzu-hornitzailearen bidez desblokeatzeko PIN kodea"</string>
- <string name="PERSOSUBSTATE_SIM_SIM_ENTRY" msgid="4487435301206073787">"SIMaren bidez desblokeatzeko PIN kodea"</string>
+ <string name="PERSOSUBSTATE_SIM_CORPORATE_ENTRY" msgid="4447629474818217364">"Enpresaren SIMaren bidez desblokeatzeko PINa"</string>
+ <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ENTRY" msgid="973059024670737358">"SIMaren zerbitzu-hornitzailearen bidez desblokeatzeko PINa"</string>
+ <string name="PERSOSUBSTATE_SIM_SIM_ENTRY" msgid="4487435301206073787">"SIMaren bidez desblokeatzeko PINa"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_PUK_ENTRY" msgid="768060297218652809">"Idatzi PUK kodea"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_ENTRY" msgid="7129527319490548930">"Idatzi PUK kodea"</string>
<string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_ENTRY" msgid="2876126640607573252">"Idatzi PUK kodea"</string>
@@ -2181,9 +2183,9 @@
<string name="PERSOSUBSTATE_RUIM_NETWORK1_ENTRY" msgid="2974411408893410289">"RUIMaren 1 motako sarearen bidez desblokeatzeko PINa"</string>
<string name="PERSOSUBSTATE_RUIM_NETWORK2_ENTRY" msgid="687618528751880721">"RUIMaren 2 motako sarearen bidez desblokeatzeko PINa"</string>
<string name="PERSOSUBSTATE_RUIM_HRPD_ENTRY" msgid="6810596579655575381">"HRPD sarearen bidez desblokeatzeko PINa"</string>
- <string name="PERSOSUBSTATE_RUIM_CORPORATE_ENTRY" msgid="2715929642540980259">"Enpresaren RUIMaren bidez desblokeatzeko PIN kodea"</string>
- <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ENTRY" msgid="8557791623303951590">"RUIMaren zerbitzu-hornitzailearen bidez desblokeatzeko PIN kodea"</string>
- <string name="PERSOSUBSTATE_RUIM_RUIM_ENTRY" msgid="7382468767274580323">"RUIMaren bidez desblokeatzeko PIN kodea"</string>
+ <string name="PERSOSUBSTATE_RUIM_CORPORATE_ENTRY" msgid="2715929642540980259">"Enpresaren RUIMaren bidez desblokeatzeko PINa"</string>
+ <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ENTRY" msgid="8557791623303951590">"RUIMaren zerbitzu-hornitzailearen bidez desblokeatzeko PINa"</string>
+ <string name="PERSOSUBSTATE_RUIM_RUIM_ENTRY" msgid="7382468767274580323">"RUIMaren bidez desblokeatzeko PINa"</string>
<string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_ENTRY" msgid="6730880791104286987">"Idatzi PUK kodea"</string>
<string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_ENTRY" msgid="6432126539782267026">"Idatzi PUK kodea"</string>
<string name="PERSOSUBSTATE_RUIM_HRPD_PUK_ENTRY" msgid="1730510161529488920">"Idatzi PUK kodea"</string>
@@ -2191,8 +2193,8 @@
<string name="PERSOSUBSTATE_RUIM_RUIM_PUK_ENTRY" msgid="9129139686191167829">"Idatzi PUK kodea"</string>
<string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_ENTRY" msgid="2869929685874615358">"Idatzi PUK kodea"</string>
<string name="PERSOSUBSTATE_SIM_SPN_ENTRY" msgid="1238663472392741771">"SPNaren bidez desblokeatzeko PINa"</string>
- <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ENTRY" msgid="3988705848553894358">"Zerbitzu-hornitzailearen PLMN sare nagusi baliokidearen bidez desblokeatzeko PIN kodea"</string>
- <string name="PERSOSUBSTATE_SIM_ICCID_ENTRY" msgid="6186770686690993200">"ICCIDaren bidez desblokeatzeko PIN kodea"</string>
+ <string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ENTRY" msgid="3988705848553894358">"Zerbitzu-hornitzailearen PLMN sare nagusi baliokidearen bidez desblokeatzeko PINa"</string>
+ <string name="PERSOSUBSTATE_SIM_ICCID_ENTRY" msgid="6186770686690993200">"ICCIDaren bidez desblokeatzeko PINa"</string>
<string name="PERSOSUBSTATE_SIM_IMPI_ENTRY" msgid="7043865376145617024">"IMPIaren bidez desblokeatzeko PINa"</string>
<string name="PERSOSUBSTATE_SIM_NS_SP_ENTRY" msgid="6144227308185112176">"Sareko azpimultzoaren zerbitzu-hornitzailearen bidez desblokeatzeko PINa"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_IN_PROGRESS" msgid="4233355366318061180">"SIMaren sarearen bidez desblokeatzeko eskatzen…"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 3787d4c..4fbaa00 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"اثر انگشت دیگری را امتحان کنید"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"خیلی روشن است"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"اثر انگشت را تنظیم کنید"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"هربار موقعیت انگشتتان را کمی تغییر دهید"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"اثر انگشت اصالتسنجی شد"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"اثر انگشتی ثبت نشده است."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"این دستگاه حسگر اثر انگشت ندارد."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"حسگر بهطور موقت غیرفعال است."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"حسگر به واسنجی نیاز دارد"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"انگشت <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"استفاده از اثر انگشت"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"استفاده از اثر انگشت یا قفل صفحه"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"نماد اثر انگشت"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"قفلگشایی با چهره"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"ثبت مجدد چهره"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"برای بهبود تشخیص، لطفاً چهرهتان را دوباره ثبت کنید"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"راهاندازی «قفلگشایی با چهره»"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"برای باز کردن قفل تلفن خود به آن نگاه کنید"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"راهاندازی روشهای بیشتر برای باز کردن قفل"</string>
@@ -906,12 +910,12 @@
<string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"لطفاً به راهنمای کاربر مراجعه کرده یا با مرکز پشتیبانی از مشتریان تماس بگیرید."</string>
<string name="lockscreen_sim_locked_message" msgid="3160196135801185938">"سیم کارت قفل شد."</string>
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="2286497117428409709">"بازگشایی قفل سیم کارت…"</string>
- <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. \n\nپس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"الگوی بازگشایی قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. \n\nپساز <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
<string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"گذرواژهٔ خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه تایپ کردهاید. \n\nپس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"پین را<xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه تایپ کردهاید. \n\nپس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"شما الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. بعد از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته میشود که برای بازگشایی قفل رایانهٔ لوحی خود به Google وارد شوید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"<xliff:g id="NUMBER_0">%1$d</xliff:g> بار الگوی بازگشاییتان را اشتباه کشیدهاید. اگر <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر داشته باشید، از شما خواسته میشود با اطلاعات ورود به سیستم Google خود، قفل دستگاه Android TV را باز کنید.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دیگر دوباره امتحان کنید."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"الگوی قفلگشایی را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر از شما خواسته میشود که برای بازگشایی قفل گوشی خود به برنامه Google وارد شوید.\n\n پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"الگوی بازگشایی قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. بعداز <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته میشود که برای بازگشایی قفل رایانهٔ لوحیتان به Google وارد شوید.\n\n لطفاً پساز <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"الگوی بازگشایی قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. اگر <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر داشته باشید، از شما خواسته میشود با اطلاعات ورود به سیستم Google خود، قفل دستگاه Android TV را باز کنید.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دیگر دوباره امتحان کنید."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"الگوی بازگشایی قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. پساز <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر، از شما خواسته میشود که برای بازگشایی قفل گوشی به برنامه Google وارد شوید.\n\n پساز <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"شما به اشتباه <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اقدام به باز کردن قفل رایانهٔ لوحی کردهاید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر، رایانهٔ لوحی به پیشفرض کارخانه بازنشانی میشود و تمام دادههای کاربر از دست خواهد رفت."</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"<xliff:g id="NUMBER_0">%1$d</xliff:g> تلاش ناموفق برای باز کردن قفل Android TV خود داشتهاید. اگر <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر داشته باشید، دستگاه Android TV شما به تنظیمات پیشفرض کارخانه بازنشانی خواهد شد و همه دادههای کاربر ازدست خواهد رفت."</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="1166532464798446579">"شما به اشتباه <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اقدام به باز کردن قفل تلفن کردهاید. پس از<xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر، تلفن به پیشفرض کارخانه بازنشانی میشود و تمام دادههای کاربر از دست خواهد رفت."</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"به برنامه اجازه میدهد حذف بستهها را درخواست کند."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"درخواست نادیدهگرفتن بهینهسازی باتری"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"به یک برنامه اجازه میدهد جهت نادیده گرفتن بهینهسازی باتری برای خود مجوز درخواست کند."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"پُرسمان همه بستهها"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"به برنامه اجازه میدهد همه بستههای نصبشده را ببیند."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"برای کنترل بزرگنمایی، دو بار ضربه بزنید"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"افزودن ابزارک انجام نشد."</string>
<string name="ime_action_go" msgid="5536744546326495436">"برو"</string>
@@ -1671,16 +1673,16 @@
<string name="kg_login_checking_password" msgid="4676010303243317253">"درحال بررسی حساب..."</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"پین خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه تایپ کردید. \n\nپس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"گذرواژه خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه تایپ کردید. \n\nپس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدید. \n\nلطفاً پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"الگوی بازگشایی قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. \n\nلطفاً پساز <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"شما به اشتباه <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اقدام به باز کردن قفل رایانه لوحی کردهاید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر، رایانهٔ لوحی به پیشفرض کارخانه بازنشانی میشود و تمام دادههای کاربر از دست خواهد رفت."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"<xliff:g id="NUMBER_0">%1$d</xliff:g> تلاش ناموفق برای باز کردن قفل Android TV خود داشتهاید. اگر <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر داشته باشید، دستگاه Android TV شما به تنظیمات پیشفرض کارخانه بازنشانی خواهد شد و همه دادههای کاربر ازدست خواهد رفت."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"شما به اشتباه <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اقدام به باز کردن قفل تلفن کردهاید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر، تلفن به پیشفرض کارخانه بازنشانی میشود و تمام دادههای کاربر از دست خواهد رفت."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"شما به اشتباه <xliff:g id="NUMBER">%d</xliff:g> بار اقدام به باز کردن قفل رایانه لوحی کردهاید. رایانه لوحی اکنون به پیشفرض کارخانه بازنشانی میشود."</string>
<string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"<xliff:g id="NUMBER">%d</xliff:g> تلاش ناموفق برای باز کردن قفل Android TV خود داشتهاید. اکنون دستگاه Android TV شما به تنظیمات پیشفرض کارخانه بازنشانی میشود."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"شما به اشتباه <xliff:g id="NUMBER">%d</xliff:g> بار اقدام به باز کردن قفل تلفن کردهاید. این تلفن اکنون به پیشفرض کارخانه بازنشانی میشود."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"شما الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. بعد از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته میشود که با استفاده از یک حساب ایمیل قفل رایانه لوحی خود را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"<xliff:g id="NUMBER_0">%1$d</xliff:g> بار الگوی بازگشاییتان را اشتباه کشیدهاید. اگر <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر داشته باشید، از شما خواسته میشود بااستفاده از حساب ایمیل خود، قفل دستگاه Android TV را باز کنید.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دیگر دوباره امتحان کنید."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"شما الگوی بازگشایی قفل خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته میشود که با استفاده از یک حساب ایمیل قفل تلفن خود را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"الگوی بازگشایی قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. بعداز <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته میشود که بااستفاده از یک حساب ایمیل قفل رایانه لوحیتان را باز کنید.\n\n لطفاً پساز <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"الگوی بازگشایی قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. اگر <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر داشته باشید، از شما خواسته میشود بااستفاده از حساب ایمیلتان، قفل دستگاه Android TV را باز کنید.\n\n <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دیگر دوباره امتحان کنید."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"الگوی بازگشایی قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. پساز <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته میشود که بااستفاده از یک حساب ایمیل قفل تلفنتان را باز کنید.\n\n لطفاً پساز <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"حذف"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"میزان صدا را به بالاتر از حد توصیه شده افزایش میدهید؟\n\nگوش دادن به صداهای بلند برای مدت طولانی میتواند به شنواییتان آسیب وارد کند."</string>
@@ -1864,7 +1866,7 @@
<string name="managed_profile_label_badge_2" msgid="5673187309555352550">"کار دوم <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="managed_profile_label_badge_3" msgid="6882151970556391957">"کار سوم <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"درخواست کد پین قبل از برداشتن پین"</string>
- <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"درخواست الگوی باز کردن قفل قبل از برداشتن سنجاق"</string>
+ <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"درخواست الگوی بازگشایی قفل قبلاز برداشتن سنجاق"</string>
<string name="lock_to_app_unlock_password" msgid="9126722403506560473">"درخواست گذرواژه قبل از برداشتن سنجاق"</string>
<string name="package_installed_device_owner" msgid="7035926868974878525">"توسط سرپرست سیستم نصب شد"</string>
<string name="package_updated_device_owner" msgid="7560272363805506941">"توسط سرپرست سیستم بهروزرسانی شد"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 965970c..d88d166 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Antaa sovelluksen laajentaa tai tiivistää tilarivin."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"näyttää ilmoituksia koko näytön tapahtumina lukitulla laitteella"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Sallii sovelluksen näyttää ilmoituksia koko näytön tapahtumina lukitulla laitteella"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"asentaa pikakuvakkeita"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Pikakuvakkeiden asentaminen"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Antaa sovelluksen lisätä aloitusruudun pikakuvakkeita ilman käyttäjän toimia."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"poista pikakuvakkeita"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Antaa sovelluksen poistaa aloitusruudun pikakuvakkeita ilman käyttäjän toimia."</string>
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Kokeile toista sormenjälkeä"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Liian kirkas"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Kokeile muuttaa asentoa"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Liikuta sormeasi hieman joka kerralla"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Sormenjälki tunnistettu"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Sormenjälkiä ei ole otettu käyttöön."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Laitteessa ei ole sormenjälkitunnistinta."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Tunnistin poistettu väliaikaisesti käytöstä."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Tunnistin on kalibroitava"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Sormi <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Käytä sormenjälkeä"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Käytä sormenjälkeä tai näytön lukitusta"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Sormenjälkikuvake"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Kasvojentunnistusavaus"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Lisää kasvot uudelleen"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Lisää kasvosi uudelleen tunnistamisen parantamiseksi"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Ota kasvojentunnistusavaus käyttöön"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Avaa puhelimesi lukitus katsomalla laitetta"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Ota käyttöön lisää tapoja avata lukitus"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Antaa sovelluksen pyytää pakettien poistamista."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"Lupa ohittaa akun optimoinnit"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Sallii sovelluksen pyytää lupaa ohittaa tietyn sovelluksen akun optimoinnit."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"kaikkien pakettien näkeminen"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Antaa sovelluksen nähdä kaikki asennetut paketit."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Hallitse zoomausta napauttamalla kahdesti"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widgetin lisääminen epäonnistui."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Siirry"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 44754d7..da6a355 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Essayez une autre empreinte digitale"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Trop lumineux"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Essayez de l\'ajuster"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Modifiez légèrement la position de votre doigt chaque fois"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Empreinte digitale authentifiée"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Aucune empreinte digitale enregistrée."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Cet appareil ne possède pas de capteur d\'empreintes digitales."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Le capteur a été désactivé temporairement."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Le capteur doit être calibré"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utiliser l\'empreinte digitale"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utiliser l\'empreinte digitale ou le verrouillage de l\'écran"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icône d\'empreinte digitale"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Déverrouillage par reconnaissance faciale"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Inscrivez votre visage à nouveau"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Pour améliorer la reconnaissance, veuillez enregistrer à nouveau votre visage"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurer le déverrouillage par reconnaissance faciale"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Déverrouillez votre téléphone en le regardant"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurer d\'autres méthodes de déverrouillage"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permet à une application de demander la suppression de paquets."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"demander d\'ignorer les optimisations de la pile"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permet à une application de demander la permission d\'ignorer les optimisations de la pile."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"envoyer une requête à propos de tous les paquets"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permet à une application de voir tous les paquets installés."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Appuyer deux fois pour régler le zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Impossible d\'ajouter le widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Aller"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index cc22d83..2bdcf08 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Essayez une autre empreinte"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Trop de lumière"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Essayez de repositionner le doigt"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Changez légèrement de position chaque fois"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Empreinte digitale authentifiée"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Aucune empreinte digitale enregistrée."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aucun lecteur d\'empreinte digitale n\'est installé sur cet appareil."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Capteur temporairement désactivé."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Vous devez calibrer le capteur"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Doigt <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utiliser l\'empreinte digitale"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utiliser votre empreinte digitale ou le verrouillage de l\'écran"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icône d\'empreinte digitale"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Déverrouillage par reconnaissance faciale"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Enregistrer à nouveau votre visage"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Pour améliorer la reconnaissance, veuillez enregistrer à nouveau votre visage"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurer le déverrouillage facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Déverrouillez votre téléphone en le regardant"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurer d\'autres méthodes de déverrouillage"</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"Le texte a été copié dans le presse-papier."</string>
<string name="copied" msgid="4675902854553014676">"Copie effectuée"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> collé depuis <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a copié des données depuis le presse-papiers"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a collé des données depuis le presse-papiers"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a collé du texte que vous avez copié"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a collé une image que vous avez copiée"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a collé le contenu que vous avez copié"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permet à une application de demander la suppression de packages."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"demander à ignorer les optimisations de batterie"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Autorise une application à demander l\'autorisation d\'ignorer les optimisations de batterie pour cette application."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"interroger tous les packages"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Autorise une appli à voir tous les packages installés."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Appuyer deux fois pour régler le zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Impossible d\'ajouter le widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"OK"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index ba41c75..c21f23b 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Proba con outra impresión dixital"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Hai demasiada luz"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Proba a axustar a impresión dixital"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Cambia lixeiramente a posición do dedo en cada intento"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Autenticouse a impresión dixital"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Non se rexistraron impresións dixitais."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo non ten sensor de impresión dixital."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Desactivouse o sensor temporalmente."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"É necesario calibrar o sensor"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utilizar impresión dixital"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utilizar impresión dixital ou credencial do dispositivo"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icona de impresión dixital"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Desbloqueo facial"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Volve inscribir a túa cara"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Para mellorar o recoñecemento, inscribe de novo a túa cara"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurar o desbloqueo facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Mira o teléfono para desbloquealo"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura máis maneiras de desbloquear o dispositivo"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permite a unha aplicación solicitar a eliminación dos paquetes."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"pedir que se ignore a optimización da batería"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Fai que unha aplicación poida solicitar permiso para ignorar as optimizacións da batería."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos os paquetes"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que unha aplicación consulte todos os paquetes instalados."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Toca dúas veces para controlar o zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Non se puido engadir o widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Ir"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 4157e1f..d7da201 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"ઍપ્લિકેશનને સ્ટેટસ બાર વિસ્તૃત કરવાની અને સંકુચિત કરવાની મંજૂરી આપે છે."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"લૉક કરેલા ડિવાઇસ પર પૂર્ણ સ્ક્રીન પરની પ્રવૃતિઓની જેમ નોટિફિકેશન બતાવો"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"ઍપને લૉક કરેલા ડિવાઇસ પર પૂર્ણ સ્ક્રીન પરની પ્રવૃતિઓની જેમ નોટિફિકેશન બતાવવાની મંજૂરી આપે છે"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"શોર્ટકટ્સ ઇન્સ્ટોલ કરો"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"શૉર્ટકટ ઇન્સ્ટૉલ કરો"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"એપ્લિકેશનને વપરાશકર્તા હસ્તક્ષેપ વગર હોમસ્ક્રીન શોર્ટકટ્સ ઉમેરવાની મંજૂરી આપે છે."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"શોર્ટકટ્સ અનઇન્સ્ટોલ કરો"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"એપ્લિકેશનને વપરાશકર્તા હસ્તક્ષેપ વગર હોમસ્ક્રીન શોર્ટકટ્સ દૂર કરવાની મંજૂરી આપે છે."</string>
@@ -585,6 +585,8 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"અન્ય ફિંગરપ્રિન્ટ અજમાવી જુઓ"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"અતિશય પ્રકાશિત"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ગોઠવણી કરી જુઓ"</string>
+ <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) -->
+ <skip />
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ફિંગરપ્રિન્ટ પ્રમાણિત કરી"</string>
@@ -601,6 +603,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"કોઈ ફિંગરપ્રિન્ટની નોંધણી કરવામાં આવી નથી."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"આ ડિવાઇસમાં કોઈ ફિંગરપ્રિન્ટ સેન્સર નથી."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"સેન્સર હંગામી રૂપે બંધ કર્યું છે."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"સેન્સરને કેલિબ્રેટ કરવાની જરૂર છે"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"આંગળી <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ફિંગરપ્રિન્ટનો ઉપયોગ કરો"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ફિંગરપ્રિન્ટ અથવા સ્ક્રીન લૉકનો ઉપયોગ કરો"</string>
@@ -610,8 +613,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ફિંગરપ્રિન્ટ આયકન"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ફેસ અનલૉક"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"તમારા ચહેરાની ફરી નોંધણી કરાવો"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"ઓળખવાની પ્રક્રિયાને બહેતર બનાવવા માટે કૃપા કરીને તમારા ચહેરાની ફરી નોંધણી કરાવો"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"ફેસ અનલૉક સુવિધાનું સેટઅપ કરો"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"તમારા ફોનની તરફ જોઈને તેને અનલૉક કરો"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"અનલૉક કરવાની બીજી રીતોનું સેટઅપ કરો"</string>
@@ -1458,10 +1463,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ઍપ્લિકેશનને પૅકેજો કાઢી નાખવાની વિનંતી કરવાની મંજૂરી આપે છે."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"બૅટરી ઓપ્ટિમાઇઝેશન્સને અવગણવા માટે પૂછો"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ઍપ્લિકેશનને તે ઍપ્લિકેશન માટે બૅટરી ઓપ્ટિમાઇઝેશન્સને અવગણવાની પરવાનગી આપવા માટે પૂછવાની મંજૂરી આપે છે."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"બધા પૅકેજ જુઓ"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"કોઈ ઍપને ઇન્સ્ટૉલ કરેલા બધા પૅકેજ જોવાની મંજૂરી આપે છે."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ઝૂમ નિયંત્રણ માટે બેવાર ટૅપ કરો"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"વિજેટ ઉમેરી શકાયું નથી."</string>
<string name="ime_action_go" msgid="5536744546326495436">"જાઓ"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 6e8bd94..f4142c3 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"किसी दूसरे फ़िंगरप्रिंट से कोशिश करें"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"बहुत रोशनी है"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"सेंसर पर सही तरीके से उंगली लगाने की कोशिश करें"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"फ़िंगरप्रिंट सेट अप करते समय, अपनी उंगली को हर बार थोड़ी अलग स्थिति में रखें"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"फ़िंगरप्रिंट की पुष्टि हो गई"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कोई फ़िंगरप्रिंट रजिस्टर नहीं किया गया है."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"इस डिवाइस में फ़िंगरप्रिंट सेंसर नहीं है."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"सेंसर कुछ समय के लिए बंद कर दिया गया है."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"सेंसर को कैलिब्रेट करने की ज़रूरत है"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"फ़िंगरप्रिंट <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"फ़िंगरप्रिंट इस्तेमाल करें"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"फ़िंगरप्रिंट या स्क्रीन लॉक का क्रेडेंशियल इस्तेमाल करें"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"फ़िंगरप्रिंट आइकॉन"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"फ़ेस अनलॉक"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"अपना चेहरा फिर से दर्ज करें"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"कृपया अपना चेहरा फिर से दर्ज करें ताकि आपको बेहतर तरीके से पहचाना जा सके"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"फे़स अनलॉक की सुविधा सेट अप करें"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"अपने फ़ोन की तरफ़ देखकर उसे अनलॉक करें"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"फ़ोन को अनलॉक करने के दूसरे तरीके सेट अप करें"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"किसी ऐप्लिकेशन को पैकेज हटाने का अनुरोध करने देती है."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"बैटरी ऑप्टिमाइज़ेशन पर ध्यान ना देने के लिए पूछें"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"किसी ऐप्लिकेशन को उस ऐप्लिकेशन के लिए बैटरी ऑप्टिमाइज़ेशन पर ध्यान ना देने की अनुमति के लिए पूछने देता है."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"डिवाइस पर इंस्टॉल किए गए सभी पैकेज देखें"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"यह किसी ऐप्लिकेशन को, डिवाइस पर इंस्टॉल किए गए सभी पैकेज देखने की अनुमति देता है."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ज़ूम नियंत्रण के लिए दो बार टैप करें"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"विजेट नहीं जोड़ा जा सका."</string>
<string name="ime_action_go" msgid="5536744546326495436">"जाएं"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 98acb89..c6af158 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -588,6 +588,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Isprobajte drugi otisak prsta"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Presvijetlo"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Pokušajte ga prilagoditi"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Svaki put lagano promijenite položaj prsta"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Autentificirano otiskom prsta"</string>
@@ -604,6 +605,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije registriran nijedan otisak prsta."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor otiska prsta."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Potrebno je kalibrirati senzor"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Upotreba otiska prsta"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Upotreba otiska prsta ili zaključavanja zaslona"</string>
@@ -613,8 +615,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona otiska prsta"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Otključavanje licem"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Ponovo registrirajte svoje lice"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Za poboljšanje prepoznavanja ponovo registrirajte svoje lice"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Postavite otključavanje licem"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Otključajte telefon gledajući u njega"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Postavite više načina otključavanja"</string>
@@ -1022,7 +1026,7 @@
<string name="text_copied" msgid="2531420577879738860">"Tekst kopiran u međuspremnik."</string>
<string name="copied" msgid="4675902854553014676">"Kopirano"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"U aplikaciji <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> zalijepljen je sadržaj aplikacije <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"Zalijepljen je sadržaj aplikacije <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"Apl. <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> zalijepila je ovaj sadržaj iz međuspremnika"</string>
<string name="pasted_text" msgid="4298871641549173733">"Aplikacija <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> zalijepila je tekst koji ste kopirali"</string>
<string name="pasted_image" msgid="4729097394781491022">"Aplikacija <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> zalijepila je sliku koju ste kopirali"</string>
<string name="pasted_content" msgid="646276353060777131">"Aplikacija <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> zalijepila je ono što ste kopirali"</string>
@@ -1212,7 +1216,7 @@
<string name="whichEditApplication" msgid="6191568491456092812">"Uređivanje pomoću aplikacije"</string>
<string name="whichEditApplicationNamed" msgid="8096494987978521514">"Uređivanje pomoću aplikacije %1$s"</string>
<string name="whichEditApplicationLabel" msgid="1463288652070140285">"Uredi"</string>
- <string name="whichSendApplication" msgid="4143847974460792029">"Dijeli"</string>
+ <string name="whichSendApplication" msgid="4143847974460792029">"Dijeljenje"</string>
<string name="whichSendApplicationNamed" msgid="4470386782693183461">"Dijeljenje pomoću aplikacije %1$s"</string>
<string name="whichSendApplicationLabel" msgid="7467813004769188515">"Dijeli"</string>
<string name="whichSendToApplication" msgid="77101541959464018">"Pošalji aplikacijom"</string>
@@ -1478,10 +1482,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Aplikaciji omogućuje zahtijevanje brisanja paketa."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"tražiti zanemarivanje optimizacija baterije"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Aplikaciji omogućuje da traži dopuštenje za zanemarivanje optimizacija baterije za tu aplikaciju."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"slanje upita za sve pakete"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Aplikaciji omogućuje pregled instaliranih paketa."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dvaput dotaknite za upravljanje zumiranjem"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget nije moguće dodati."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Idi"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 58c3875..08d583d 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Próbálkozzon másik ujjlenyomattal"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Túl világos"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Próbálja beállítani"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Módosítsa minden alkalommal kis mértékben ujja helyzetét."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Ujjlenyomat hitelesítve"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nincsenek regisztrált ujjlenyomatok."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ez az eszköz nem rendelkezik ujjlenyomat-érzékelővel."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Az érzékelő átmenetileg le van tiltva."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Az érzékelő kalibrálást igényel"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. ujj"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Ujjlenyomat használata"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"A folytatás ujjlenyomattal vagy képernyőzárral lehetséges"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ujjlenyomat ikon"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Arcalapú feloldás"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Rögzítsen újra képet az arcáról"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"A felismerés javítása érdekében rögzítsen újra az arcáról készített képet"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Az Arcalapú feloldás beállítása"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Feloldhatja a zárolást úgy, hogy ránéz a telefonra"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"További feloldási módszerek beállítása"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Lehetővé teszi az alkalmazás számára, hogy csomagok törlését kérje."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"Akkumulátoroptimalizálási beállítások mellőzésének kérése"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Az alkalmazás engedélyt kérhet az akkumulátoroptimalizálási beállítások mellőzésére."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"az összes csomag lekérdezése"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Engedélyezi az adott alkalmazás számára, hogy lássa az összes telepített csomagot."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Érintse meg kétszer a nagyítás beállításához"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nem sikerült hozzáadni a modult."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Ugrás"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 860dcf9..e81b31d 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Թույլ է տալիս ծրագրին ընդլայնել կամ հետ ծալել կարգավիճակի գոտին:"</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"ցուցադրել ծանուցումներ կողպված սարքի էկրանին լիաէկրան ռեժիմում"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Թույլ է տալիս հավելվածին ծանուցումներ ցուցադրել կողպված սարքի էկրանին լիաէկրան ռեժիմում"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"տեղադրել դյուրանցումներ"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Դյուրանցումների տեղադրում"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Հավելվածին թույլ է տալիս ավելացնել գլխավոր էկրանի դյուրանցումներ՝ առանց օգտագործողի միջամտության:"</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"ապատեղադրել դյուրանցումները"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Հավելվածին թույլ է տալիս հեռացնել գլխավոր էկրանի դյուրանցումները՝ առանց օգտագործողի միջամտության:"</string>
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Փորձեք մեկ այլ մատնահետք"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Շատ լուսավոր է"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Փորձեք փոխել մատի դիրքը"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Ամեն անգամ թեթևակի փոխեք մատի դիրքը"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Մատնահետքը նույնականացվեց"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Գրանցված մատնահետք չկա:"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Այս սարքը չունի մատնահետքերի սկաներ։"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Տվիչը ժամանակավորապես անջատված է:"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Սկաներն անհրաժեշտ է չափաբերել"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Մատնահետք <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Օգտագործել մատնահետք"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Օգտագործել մատնահետք կամ էկրանի կողպում"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Մատնահետքի պատկերակ"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Դեմքով ապակողպում"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Նորից գրանցեք ձեր դեմքը"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Ճանաչումը լավացնելու համար նորից գրանցեք ձեր դեմքը"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Կարգավորեք դեմքով ապակողպումը"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ապակողպելու համար պարզապես նայեք հեռախոսին"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Կարգավորեք ապակողպելու այլ եղանակներ"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Թույլ է տալիս հավելվածին պահանջել փաթեթների ջնջում:"</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"հայցել մարտկոցի օպտիմալացումն անտեսելու թույլտվություն"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Հավելվածին հնարավորություն է տալիս հայցելու թույլտվություն՝ տվյալ հավելվածի համար մարտկոցի օպտիմալացումն անտեսելու համար:"</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"հարցում բոլոր փաթեթների համար"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Թույլ է տալիս հավելվածին տեսնել բոլոր տեղադրված փաթեթները։"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Հպեք երկու անգամ` խոշորացման վերահսկման համար"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Չհաջողվեց վիջեթ ավելացնել:"</string>
<string name="ime_action_go" msgid="5536744546326495436">"Առաջ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index b8649c1..17962cf 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -399,7 +399,7 @@
<string name="permdesc_persistentActivity" product="default" msgid="1914841924366562051">"Memungkinkan aplikasi membuat bagian dari dirinya sendiri terus-menerus berada dalam memori. Izin ini dapat membatasi memori yang tersedia untuk aplikasi lain sehingga menjadikan ponsel lambat."</string>
<string name="permlab_foregroundService" msgid="1768855976818467491">"jalankan layanan di latar depan"</string>
<string name="permdesc_foregroundService" msgid="8720071450020922795">"Mengizinkan aplikasi menggunakan layanan di latar depan."</string>
- <string name="permlab_getPackageSize" msgid="375391550792886641">"mengukur ruang penyimpanan apl"</string>
+ <string name="permlab_getPackageSize" msgid="375391550792886641">"mengukur ruang penyimpanan aplikasi"</string>
<string name="permdesc_getPackageSize" msgid="742743530909966782">"Mengizinkan apl mengambil kode, data, dan ukuran temboloknya"</string>
<string name="permlab_writeSettings" msgid="8057285063719277394">"ubah setelan sistem"</string>
<string name="permdesc_writeSettings" msgid="8293047411196067188">"Mengizinkan apl memodifikasi data setelan sistem. Apl berbahaya dapat merusak konfigurasi sistem anda."</string>
@@ -437,7 +437,7 @@
<string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"Aplikasi ini dapat menambahkan, menghapus, atau mengubah acara kalender di ponsel. Aplikasi ini dapat mengirim pesan yang kelihatannya berasal dari pemilik kalender, atau mengubah acara tanpa memberi tahu pemilik."</string>
<string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"akses perintah penyedia lokasi ekstra"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Memungkinkan aplikasi mengakses perintah penyedia lokasi ekstra. Tindakan ini memungkinkan aplikasi mengganggu pengoperasian GPS atau sumber lokasi lain."</string>
- <string name="permlab_accessFineLocation" msgid="6426318438195622966">"akses lokasi pasti hanya saat di latar depan"</string>
+ <string name="permlab_accessFineLocation" msgid="6426318438195622966">"akses lokasi akurat hanya saat di latar depan"</string>
<string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Aplikasi ini bisa mendapatkan lokasi pasti Anda dari layanan lokasi saat aplikasi sedang digunakan. Layanan lokasi untuk perangkat harus diaktifkan agar aplikasi bisa mendapatkan lokasi. Ini dapat meningkatkan penggunaan baterai."</string>
<string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"akses perkiraan lokasi hanya saat berada di latar depan"</string>
<string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Aplikasi ini bisa mendapatkan perkiraan lokasi Anda dari layanan lokasi saat aplikasi sedang digunakan. Layanan lokasi untuk perangkat harus diaktifkan agar aplikasi bisa mendapatkan lokasi."</string>
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Coba sidik jari lain"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Terlalu terang"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Coba sesuaikan"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Ubah sedikit posisi jari di setiap percobaan"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Sidik jari diautentikasi"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Tidak ada sidik jari yang terdaftar."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Perangkat ini tidak memiliki sensor sidik jari."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor dinonaktifkan untuk sementara."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Sensor memerlukan kalibrasi"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Jari <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gunakan sidik jari"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Gunakan sidik jari atau kunci layar"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon sidik jari"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Face Unlock"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Daftarkan kembali wajah Anda"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Untuk menyempurnakan pengenalan wajah, daftarkan kembali wajah Anda"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Siapkan Face Unlock"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Buka kunci ponsel dengan melihat ke ponsel"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Siapkan lebih banyak cara untuk membuka kunci"</string>
@@ -658,7 +662,7 @@
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Ikon wajah"</string>
- <string name="permlab_readSyncSettings" msgid="6250532864893156277">"baca setelan sinkron"</string>
+ <string name="permlab_readSyncSettings" msgid="6250532864893156277">"baca setelan sinkronisasi"</string>
<string name="permdesc_readSyncSettings" msgid="1325658466358779298">"Memungkinkan aplikasi membaca setelan sinkronisasi untuk sebuah akun. Misalnya, izin ini dapat menentukan apakah aplikasi Orang disinkronkan dengan sebuah akun."</string>
<string name="permlab_writeSyncSettings" msgid="6583154300780427399">"nyalakan dan matikan sinkronisasi"</string>
<string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"Memungkinkan aplikasi mengubah setelan sinkronisasi untuk sebuah akun. Misalnya, izin ini dapat digunakan untuk mengaktifkan sinkronisasi dari aplikasi Orang dengan sebuah akun."</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"Teks disalin ke papan klip."</string>
<string name="copied" msgid="4675902854553014676">"Disalin"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ditempelkan dari <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> menempel dari papan klip"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> menempelkan konten dari papan klip Anda"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> menempelkan teks yang Anda salin"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> menempelkan gambar yang Anda salin"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> menempelkan konten yang Anda salin"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Mengizinkan aplikasi meminta penghapusan paket."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"meminta mengabaikan pengoptimalan baterai"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Mengizinkan aplikasi meminta izin untuk mengabaikan pengoptimalan baterai bagi aplikasi tersebut."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"mengkueri semua paket"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Mengizinkan aplikasi melihat semua paket yang diinstal."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Ketuk dua kali untuk kontrol perbesar/perkecil"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Tidak dapat menambahkan widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Buka"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 0cf1eff..9b15ea6 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prófaðu annað fingrafar"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Of bjart"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Prófaðu að breyta stöðu fingursins"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Breyttu stöðu fingursins örlítið í hvert skipti"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingrafar staðfest"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Engin fingraför hafa verið skráð."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Þetta tæki er ekki með fingrafaralesara."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Slökkt tímabundið á skynjara."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Kvarða þarf skynjarann"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Fingur <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Nota fingrafar"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Nota fingrafar eða skjálás"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingrafaratákn"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Andlitsopnun"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Skráðu andlitið þitt aftur"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Skráðu andlitið þitt til að bæta kennsl"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Setja upp andlitsopnun"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Taktu símann úr lás með því að horfa á hann"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Settu upp fleiri leiðir til að taka úr lás"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Leyfir forriti að biðja um eyðingu pakka."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"biðja um að hunsa rafhlöðusparnað"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Gerir forriti kleift að biðja um heimild til að hunsa rafhlöðusparnað fyrir forritið."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"spyrja fyrir alla pakka"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Leyfir forriti að sjá alla uppsetta pakka."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Ýttu tvisvar til að opna aðdráttarstýringar"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Ekki tókst að bæta græju við."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Áfram"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 29dbd76..416f5ad 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prova con un\'altra impronta"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Troppa luce"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Prova a regolare"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Cambia leggermente la posizione del dito ogni volta"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Impronta autenticata"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nessuna impronta digitale registrata."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Questo dispositivo non dispone di sensore di impronte."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensore temporaneamente disattivato."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"È necessario calibrare il sensore"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dito <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usa l\'impronta"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usa l\'impronta o il blocco schermo"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icona dell\'impronta"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Sblocco con il volto"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Registra di nuovo il volto"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Per migliorare il riconoscimento, registra di nuovo il tuo volto"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configura lo sblocco con il volto"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Sblocca il telefono guardandolo"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura altri modi per sbloccare"</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"Testo copiato negli appunti."</string>
<string name="copied" msgid="4675902854553014676">"Copia eseguita"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"Dati dell\'app <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> incollati dall\'app <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"L\'app <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha incollato dati dagli appunti"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha incollato dati dagli appunti"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha incollato il testo che hai copiato"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha incollato un\'immagine che hai copiato"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ha incollato i contenuti che hai copiato"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Consente a un\'applicazione di richiedere l\'eliminazione di pacchetti."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"richiesta di ignorare le ottimizzazioni della batteria"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Consente a un\'app di chiedere l\'autorizzazione a ignorare le ottimizzazioni della batteria per quell\'app."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"Invio di query per tutti i pacchetti"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Consente a un\'app di visualizzare tutti i pacchetti installati."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tocca due volte per il comando dello zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Aggiunta del widget non riuscita."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Vai"</string>
@@ -1870,8 +1872,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Aggiornato dall\'amministratore"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Eliminato dall\'amministratore"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"L\'opzione Risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, nonché alcuni effetti visivi, funzionalità e connessioni di rete."</string>
- <string name="battery_saver_description" msgid="8518809702138617167">"L\'opzione Risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, nonché alcuni effetti visivi, funzionalità e connessioni di rete."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Il risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, nonché alcuni effetti visivi, funzionalità e connessioni di rete."</string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Il risparmio energetico attiva il tema scuro e limita o disattiva l\'attività in background, nonché alcuni effetti visivi, funzionalità e connessioni di rete."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Per contribuire a ridurre l\'utilizzo dei dati, la funzione Risparmio dati impedisce ad alcune app di inviare o ricevere dati in background. Un\'app in uso può accedere ai dati, ma potrebbe farlo con meno frequenza. Esempio: le immagini non vengono visualizzate finché non le tocchi."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Attivare Risparmio dati?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Attiva"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index dc43bde..18a651c 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -591,6 +591,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"יש להשתמש בטביעת אצבע אחרת"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"בהיר מדי"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"יש לנסות ולשנות את תנוחת האצבע"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"צריך לשנות מעט את תנוחת האצבע בכל פעם"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"טביעת האצבע אומתה"</string>
@@ -607,6 +608,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"לא נסרקו טביעות אצבע."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"במכשיר הזה אין חיישן טביעות אצבע."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"החיישן מושבת באופן זמני."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"צריך לכייל את החיישן"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"אצבע <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"שימוש בטביעת אצבע"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"שימוש בטביעת אצבע או בנעילת מסך"</string>
@@ -616,8 +618,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"סמל טביעת אצבע"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"פתיחה ע\"י זיהוי הפנים"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"יש לבצע סריקה חוזרת של הפנים שלך"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"לשיפור הזיהוי יש לסרוק מחדש את הפנים שלך"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"הגדרת התכונה \'פתיחה ע\"י זיהוי הפנים\'"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"יש להביט בטלפון כדי לבטל את נעילתו"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"אפשר להגדיר דרכים נוספות לביטול נעילה"</string>
@@ -1025,7 +1029,7 @@
<string name="text_copied" msgid="2531420577879738860">"הטקסט הועתק ללוח."</string>
<string name="copied" msgid="4675902854553014676">"ההעתקה בוצעה"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"האפליקציה <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> הודבקה מ-<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> הודבקה מהלוח שלך"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"אפליקציית <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ביצעה הדבקה מהלוח"</string>
<string name="pasted_text" msgid="4298871641549173733">"טקסט שהעתקת הודבק על ידי <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string>
<string name="pasted_image" msgid="4729097394781491022">"תמונה שהעתקת הודבקה על ידי <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string>
<string name="pasted_content" msgid="646276353060777131">"התוכן שהעתקת הודבק על ידי <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>"</string>
@@ -1498,10 +1502,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"מאפשרת לאפליקציה לבקש מחיקה של חבילות."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"בקשה להתעלם מאופטימיזציות של הסוללה"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"מאפשרת לאפליקציה לבקש רשות להתעלם מאופטימיזציות של הסוללה לאפליקציה הזו."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"שליחת שאילתות לכל החבילות"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"מאפשרת לאפליקציה לראות את כל החבילות המותקנות."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"יש להקיש פעמיים לשינוי המרחק מהתצוגה"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"לא ניתן להוסיף widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"התחלה"</string>
@@ -2123,7 +2125,7 @@
<string name="mmcc_imsi_unknown_in_hlr" msgid="227760698553988751">"ניהול התצורה של כרטיס ה-SIM לא מתאים לזיהוי קולי"</string>
<string name="mmcc_illegal_ms" msgid="7509650265233909445">"כרטיס ה-SIM לא מורשה לזיהוי קולי"</string>
<string name="mmcc_illegal_me" msgid="6505557881889904915">"הטלפון לא מורשה לזיהוי קולי"</string>
- <string name="mmcc_authentication_reject_msim_template" msgid="4480853038909922153">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> אינו מאושר לשימוש ברשת"</string>
+ <string name="mmcc_authentication_reject_msim_template" msgid="4480853038909922153">"ה-SIM <xliff:g id="SIMNUMBER">%d</xliff:g> לא אושר לשימוש ברשת"</string>
<string name="mmcc_imsi_unknown_in_hlr_msim_template" msgid="3688508325248599657">"אין ניהול תצורה עבור SIM <xliff:g id="SIMNUMBER">%d</xliff:g>"</string>
<string name="mmcc_illegal_ms_msim_template" msgid="832644375774599327">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> אינו מאושר לשימוש ברשת"</string>
<string name="mmcc_illegal_me_msim_template" msgid="4802735138861422802">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> אינו מאושר לשימוש ברשת"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index b282b44..fb7ae94 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"別の指紋をお試しください"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"明るすぎます"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"調整してみてください"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"毎回、指を置く位置を少し変えてください"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"指紋認証を完了しました"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"指紋が登録されていません。"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"このデバイスには指紋認証センサーがありません。"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"センサーが一時的に無効になっています。"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"センサーの調整が必要です"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"指紋 <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"指紋の使用"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"指紋または画面ロックの使用"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"指紋アイコン"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"顔認証"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"顔の再登録"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"認識を改善するには、顔を再登録してください"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"顔認証の設定"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"スマートフォンに顔を向けるとロックが解除されます"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"その他のロック解除方法の設定"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"パッケージの削除をリクエストすることをアプリに許可します。"</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"電池の最適化を無視するかどうかの確認"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"電池の最適化の無視についてアプリが確認することを許可します。"</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"すべてのパッケージを照会する"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"すべてのインストール済みパッケージを参照することをアプリに許可します。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ダブルタップでズームします"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ウィジェットを追加できませんでした。"</string>
<string name="ime_action_go" msgid="5536744546326495436">"移動"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index dbc92da..015a6d9 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"ცადეთ სხვა თითის ანაბეჭდი"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ზედმეტად ნათელია"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ცადეთ დარეგულირება"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"შეცვალეთ თითის დაჭერის ადგილი ოდნავ ყოველ ჯერზე"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"თითის ანაბეჭდი ავტორიზებულია"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"თითის ანაბეჭდები რეგისტრირებული არ არის."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ამ მოწყობილობას არ აქვს თითის ანაბეჭდის სენსორი."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"სენსორი დროებით გათიშულია."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"სენსორს კალიბრაცია სჭირდება"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"თითი <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"გამოიყენეთ თითის ანაბეჭდი"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"გამოიყენეთ თითის ანაბეჭდი ან ეკრანის დაბლოკვა"</string>
@@ -610,8 +612,8 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"თითის ანაბეჭდის ხატულა"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"განბლოკვა სახით"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"დაარეგისტრირეთ თქვენი სახე ხელახლა"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"ამოცნობის გასაუმჯობესებლად, გთხოვთ, ხელახლა დაარეგისტრიროთ თქვენი სახე"</string>
+ <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"პრობლემა სახით განბლოკვასთან დაკავშირებით"</string>
+ <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"შეეხეთ თქვენი სახის მოდელის წასაშლელად, შემდეგ დაამატეთ სახე ხელახლა"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"სახით განბლოკვის პარამეტრების დაყენება"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"განბლოკეთ თქვენი ტელეფონი შეხედვით"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"დააყენეთ განბლოკვის სხვა ხერხები"</string>
@@ -1019,7 +1021,7 @@
<string name="text_copied" msgid="2531420577879738860">"ტექსტი დაკოპირებულია გაცვლის ბუფერში."</string>
<string name="copied" msgid="4675902854553014676">"დაკოპირდა"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>-დან <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>-ში ჩასმული"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ჩასმულია თქვენი გაცვლის ბუფერიდან"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>-მა ჩასვა ტექსტი თქვენი გაცვლის ბუფერიდან"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>-მ(ა) ჩასვა თქვენ მიერ დაკოპირებული ტექსტი"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>-მ(ა) ჩასვა თქვენ მიერ დაკოპირებული სურათი"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>-მ(ა) ჩასვა თქვენ მიერ დაკოპირებული კონტენტი"</string>
@@ -1458,10 +1460,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"აპლიკაციას შეეძლება პაკეტების წაშლის მოთხოვნა."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ბატარეის ოპტიმიზაციის იგნორირების მოთხოვნა"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"საშუალებას მისცემს აპს, მოითხოვოს მასთან დაკავშირებული ბატარეის ოპტიმიზაციის იგნორირების ნებართვა."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"ყველა პაკეტის მოთხოვნა"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"საშუალებას აძლევს აპს, ნახოს ყველა ინსტალირებული პაკეტი."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"მასშტაბის ცვლილებისთვის შეეხეთ ორჯერ"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ვერ დაემატა ვიჯეტი."</string>
<string name="ime_action_go" msgid="5536744546326495436">"გადასვლა"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 6d3429e..da0b1d9 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Қолданбаға статус жолағын жаюға емесе тасалауға рұқсат береді."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"құлыпталған құрылғыда хабарландыруларды толық экрандағы әрекеттер түрінде көрсету"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Қолданбаның құлыпталған құрылғыда хабарландыруларды толық экрандағы әрекеттер түрінде көрсетуіне рұқсат береді."</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"төте пернелерді орнату"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"таңбаша орнату"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Қолданбаға Негізгі экранның төте пернелерін пайдаланушының қатысуынсыз қосу мүмкіндігін береді."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"төте пернелерді алып тастау"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Қолданбаға Негізгі экранның төте пернелерін пайдаланушының қатысуынсыз алып тастау мүмкіндігін береді."</string>
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Басқа саусақ ізін байқап көріңіз."</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Тым жарық."</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Дұрыстап қойып көріңіз."</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Саусағыңыздың қалпын аздап өзгертіп тұрыңыз."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Саусақ ізі аутентификацияланды"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Саусақ іздері тіркелмеген."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Бұл құрылғыда саусақ ізін оқу сканері жоқ."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчик уақытша өшірулі."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Датчикті калибрлеу қажет."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>-саусақ"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Саусақ ізін пайдалану"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Саусақ ізін немесе экран құлпын пайдалану"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Саусақ ізі белгішесі"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Бет тану"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Бетті қайта тіркеу"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Құрылғы жүзіңізді жақсырақ тануы үшін, бетіңізді қайта тіркеңіз."</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Бет тану функциясын реттеу"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Телефоныңызға қарап, оның құлпын ашыңыз."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Құлыпты ашудың басқа тәсілдерін реттеу"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Қолданбаның пакеттерді жоюға рұқсат сұрауына мүмкіндік береді."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"батареяны оңтайландыру әрекетін елемеуді сұрау"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Қолданба батареяны оңтайландыру әрекетін елемеуді сұрай алады."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"барлық бумаға сұрау жасау"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Қолданба барлық орнатылған буманы көре алады."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Масштабтау параметрін басқару үшін екі рет түртіңіз"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Виджетті қосу."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Өту"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 19aa889..4f90d03 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"សាកល្បងប្រើស្នាមម្រាមដៃផ្សេងទៀត"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ភ្លឺពេក"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"សាកល្បងកែតម្រូវ"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ប្ដូរទីតាំងម្រាមដៃរបស់អ្នកតិចៗគ្រប់ពេល"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"បានផ្ទៀងផ្ទាត់ស្នាមម្រាមដៃ"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"មិនមានការចុះឈ្មោះស្នាមម្រាមដៃទេ។"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ឧបករណ៍នេះមិនមានឧបករណ៍ចាប់ស្នាមម្រាមដៃទេ។"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"បានបិទឧបករណ៍ចាប់សញ្ញាជាបណ្តោះអាសន្ន។"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"ឧបករណ៍ចាប់សញ្ញាត្រូវការកែសម្រួល"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ម្រាមដៃ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ប្រើស្នាមម្រាមដៃ"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ប្រើស្នាមម្រាមដៃ ឬការចាក់សោអេក្រង់"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"រូបស្នាមម្រាមដៃ"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ដោះសោតាមទម្រង់មុខ"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"ស្កេនបញ្ចូលមុខរបស់អ្នកម្ដងទៀត"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"ដើម្បីធ្វើឱ្យការសម្គាល់មុខប្រសើរជាងមុន សូមស្កេនបញ្ចូលមុខរបស់អ្នកម្ដងទៀត"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"រៀបចំការដោះសោតាមទម្រង់មុខ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ដោះសោទូរសព្ទរបស់អ្នកដោយសម្លឹងមើលវា"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"រៀបចំវិធីច្រើនទៀតដើម្បីដោះសោ"</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"បានចម្លងអត្ថបទទៅក្ដារតម្បៀតខ្ទាស់។"</string>
<string name="copied" msgid="4675902854553014676">"បានចម្លង"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> បានដាក់ចូលពី <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> បានដាក់ចូលពីឃ្លីបបតរបស់អ្នក"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"បានដាក់ចូល <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ពីឃ្លីបបតរបស់អ្នក"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> បានដាក់ចូលអត្ថបទដែលអ្នកបានចម្លង"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> បានដាក់ចូលរូបភាពដែលអ្នកបានចម្លង"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> បានដាក់ចូលខ្លឹមសារដែលអ្នកបានចម្លង"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"អនុញ្ញាតឲ្យកម្មវិធីស្នើសុំលុបកញ្ចប់។"</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ស្នើឲ្យមិនអើពើចំពោះការបង្កើនប្រសិទ្ធភាពថ្ម"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"អនុញ្ញាតឲ្យកម្មវិធីស្នើសុំការអនុញ្ញាត ដើម្បីមិនអើពើចំពោះការបង្កើនប្រសិទ្ធភាពថ្ម។"</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"សួរសំណួរអំពីកញ្ចប់ទាំងអស់"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"អនុញ្ញាតឱ្យកម្មវិធីមើលកញ្ចប់ដែលបានដំឡើងទាំងអស់។"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ប៉ះ ពីរដងដើម្បីពិនិត្យការពង្រីក"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"មិនអាចបន្ថែមធាតុក្រាហ្វិក។"</string>
<string name="ime_action_go" msgid="5536744546326495436">"ទៅ"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index a3e0ca5..cc8b5bf 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"ಮತ್ತೊಂದು ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಪ್ರಯತ್ನಿಸಿ"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ತುಂಬಾ ಪ್ರಕಾಶಮಾನವಾಗಿದೆ"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ಹೊಂದಿಸಲು ಪ್ರಯತ್ನಿಸಿ"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ಪ್ರತಿ ಬಾರಿಯೂ ನಿಮ್ಮ ಬೆರಳಿನ ಸ್ಥಾನವನ್ನು ಸ್ವಲ್ಪ ಮಟ್ಟಿಗೆ ಬದಲಾಯಿಸಿ"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ಪ್ರಮಾಣೀಕರಣ ಮಾಡಲಾಗಿದೆ"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ಯಾವುದೇ ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ನೋಂದಣಿ ಮಾಡಿಲ್ಲ."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ಈ ಸಾಧನವು ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಅನ್ನು ಹೊಂದಿಲ್ಲ."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ಸೆನ್ಸಾರ್ ಅನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"ಸೆನ್ಸರ್ಗೆ ಕ್ಯಾಲಿಬ್ರೇಶನ್ನ ಅಗತ್ಯವಿದೆ"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ಫಿಂಗರ್ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ಬಳಸಿ"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ಅಥವಾ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಬಳಸಿ"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಐಕಾನ್"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ಫೇಸ್ ಅನ್ಲಾಕ್"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"ನಿಮ್ಮ ಮುಖವನ್ನು ಮರುನೋಂದಣಿ ಮಾಡಿ"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"ಗುರುತಿಸುವಿಕೆಯನ್ನು ಹೆಚ್ಚಿಸಲು ನಿಮ್ಮ ಮುಖವನ್ನು ಮರುನೋಂದಣಿ ಮಾಡಿ"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"ಫೇಸ್ ಅನ್ಲಾಕ್ ಅನ್ನು ಸೆಟಪ್ ಮಾಡಿ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ಫೋನ್ ಅನ್ನು ನೋಡುವ ಮೂಲಕ ಅನ್ಲಾಕ್ ಮಾಡಿ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ಅನ್ಲಾಕ್ ಮಾಡಲು ಹೆಚ್ಚಿನ ಮಾರ್ಗಗಳನ್ನು ಹೊಂದಿಸಿ"</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"ಪಠ್ಯವನ್ನು ಕ್ಲಿಪ್ಬೋರ್ಡ್ಗೆ ನಕಲಿಸಲಾಗಿದೆ."</string>
<string name="copied" msgid="4675902854553014676">"ನಕಲಿಸಲಾಗಿದೆ"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ಅನ್ನು <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> ನಿಂದ ಅಂಟಿಸಲಾಗಿದೆ"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ಅನ್ನು ನಿಮ್ಮ ಕ್ಲಿಪ್ಬೋರ್ಡ್ನಿಂದ ಅಂಟಿಸಲಾಗಿದೆ"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>, ನಿಮ್ಮ ಕ್ಲಿಪ್ಬೋರ್ಡ್ನಲ್ಲಿನ ಡೇಟಾವನ್ನು ಅಂಟಿಸಿದೆ"</string>
<string name="pasted_text" msgid="4298871641549173733">"ನೀವು ನಕಲಿಸಿರುವ ಪಠ್ಯವನ್ನು <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ನಿಂದ ಅಂಟಿಸಲಾಗಿದೆ"</string>
<string name="pasted_image" msgid="4729097394781491022">"ನೀವು ನಕಲಿಸಿರುವ ಚಿತ್ರವನ್ನು <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ನಿಂದ ಅಂಟಿಸಲಾಗಿದೆ"</string>
<string name="pasted_content" msgid="646276353060777131">"ನೀವು ನಕಲಿಸಿರುವ ವಿಷಯವನ್ನು <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ನಿಂದ ಅಂಟಿಸಲಾಗಿದೆ"</string>
@@ -1393,7 +1397,7 @@
<string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"ಭಾಷೆ ಮತ್ತು ವಿನ್ಯಾಸವನ್ನು ಆಯ್ಕೆ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
- <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"ಇತರ ಅಪ್ಲಿಕೇಶನ್ ಮೇಲೆ ಡಿಸ್ಪ್ಲೇ"</string>
+ <string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"ಇತರ ಆ್ಯಪ್ಗಳ ಮೇಲೆ ಪ್ರದರ್ಶಿಸುವಿಕೆ"</string>
<string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"<xliff:g id="NAME">%s</xliff:g> ಇತರೆ ಆ್ಯಪ್ಗಳ ಮೇಲೆ ಡಿಸ್ಪ್ಲೇ ಆಗುತ್ತದೆ"</string>
<string name="alert_windows_notification_title" msgid="6331662751095228536">"<xliff:g id="NAME">%s</xliff:g> ಇತರೆ ಆ್ಯಪ್ಗಳ ಮೇಲೆ ಡಿಸ್ಪ್ಲೇ ಆಗುತ್ತದೆ"</string>
<string name="alert_windows_notification_message" msgid="6538171456970725333">"<xliff:g id="NAME">%s</xliff:g> ಈ ವೈಶಿಷ್ಟ್ಯ ಬಳಸುವುದನ್ನು ನೀವು ಬಯಸದಿದ್ದರೆ, ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ತೆರೆಯಲು ಮತ್ತು ಅದನ್ನು ಆಫ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ಪ್ಯಾಕೇಜ್ಗಳನ್ನು ಅಳಿಸುವುದಕ್ಕಾಗಿ ವಿನಂತಿ ಮಾಡಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ಬ್ಯಾಟರಿ ಆಪ್ಟಿಮೈಸೇಶನ್ಗಳನ್ನು ಕಡೆಗಣಿಸಲು ಕೇಳಿ"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ಈ ಅಪ್ಲಿಕೇಶನ್ಗೆ ಬ್ಯಾಟರಿ ಆಪ್ಟಿಮೈಸೇಶನ್ಗಳನ್ನು ಕಡೆಗಣಿಸುವುದಕ್ಕೆ ಅನುಮತಿಯನ್ನು ಕೇಳಲು ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಅನುಮತಿಸುತ್ತದೆ."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"ಎಲ್ಲಾ ಪ್ಯಾಕೇಜ್ಗಳ ಕುರಿತಾದ ಮಾಹಿತಿಯನ್ನು ಕೇಳಿ"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ಇನ್ಸ್ಟಾಲ್ ಮಾಡಿದ ಎಲ್ಲಾ ಪ್ಯಾಕೇಜ್ಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ಝೂಮ್ ನಿಯಂತ್ರಿಸಲು ಎರಡು ಬಾರಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ವಿಜೆಟ್ ಸೇರಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ."</string>
<string name="ime_action_go" msgid="5536744546326495436">"ಹೋಗು"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 4c1691b..c4892ba 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"다른 지문으로 시도"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"너무 밝음"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"조정 시도"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"지문을 등록할 때마다 손가락을 조금씩 이동하세요."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"지문이 인증됨"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"등록된 지문이 없습니다."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"기기에 지문 센서가 없습니다."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"센서가 일시적으로 사용 중지되었습니다."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"센서 보정 필요"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"손가락 <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"지문 사용"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"지문 또는 화면 잠금 사용"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"지문 아이콘"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"얼굴 인식 잠금 해제"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"얼굴 재등록 필요"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"인식률을 개선하려면 얼굴을 다시 등록하세요."</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"얼굴 인식 잠금 해제 설정"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"휴대전화의 화면을 응시하여 잠금 해제할 수 있습니다."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"다른 잠금 해제 방법 설정"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"애플리케이션이 패키지 삭제를 요청하도록 허용합니다."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"배터리 최적화를 무시하도록 요청"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"앱에서 배터리 최적화를 무시할 수 있는 권한을 요청할 수 있도록 허용합니다."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"모든 패키지 쿼리"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"앱이 설치된 패키지를 모두 볼 수 있도록 허용합니다."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"확대/축소하려면 두 번 탭하세요."</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"위젯을 추가할 수 없습니다."</string>
<string name="ime_action_go" msgid="5536744546326495436">"이동"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 66ec9aa..9cf1964 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Колдонмого абал тилкесин жайып көрсөтүү же жыйнап коюу мүмкүнчүлүгүн берет."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"билдирмелерди кулпуланган түзмөктүн толук экранында көрсөтүү"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Колдонмого билдирмелерди кулпуланган түзмөктүн толук экранында көрсөтүүгө уруксат берет"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"тез чакырма орнотуу"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Ыкчам баскыч түзүү"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Колдонмого үй экранга колдонуучунун катышуусусуз тез чакырма кошууга мүмкүнчүлүк берет."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"тез чакыргычтарды жок кылуу"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Колдонмого колдонуучунун катышуусусуз үй экранынын тез чакырмаларын жок кылуу мүмкүнчүлүгүн берет."</string>
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Башка манжа изин байкап көрүңүз"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Өтө жарык"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Тууралап көрүңүз"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Манжаңыздын абалын ар жолкусунда бир аз өзгөртүп туруңуз"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Манжа изи текшерилди"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Бир да манжа изи катталган эмес."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Бул түзмөктө манжа изинин сенсору жок."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сенсор убактылуу өчүрүлгөн."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Сенсорду тууралоо керек"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>-манжа"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Манжа изин колдонуу"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Манжа изин же экрандын кулпусун колдонуу"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Манжа изинин сүрөтчөсү"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Жүзүнөн таанып ачуу"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Жүзүңүздү кайра таанытыңыз."</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Мыкты таануу үчүн, жүзүңүздү кайра таанытыңыз"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Жүзүнөн таанып ачууну жөндөө"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Телефонуңузду карап туруп эле кулпусун ачып алыңыз"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Кулпусун ачуунун көбүрөөк жолдорун жөндөңүз"</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"Текст алмашуу буферине көчүрүлдү."</string>
<string name="copied" msgid="4675902854553014676">"Көчүрүлдү"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> колдонмосунан чапталды"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> алмашуу буферинен чапталды"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"Алмашуу буфериндеги нерселер <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> колдонмосуна жайгаштырылды"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>: көчүрүлгөн текст чапталды"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>: көчүрүлгөн сүрөт чапталды"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>: көчүрүлгөн мазмун чапталды"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Колдонмо топтомдорду жок кылууга уруксат сурай алат."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"батареянын кубатын көп керектей берсин"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Колдонмо батареянын кубатын керектегенден мурун уруксат суралсын."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"бардык топтомдор боюнча сурам жөнөтүү"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Колдонмо бардык орнотулган топтомдорду көрөт."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Масштабдын параметрлерин өзгөртүү үчүн бул жерди эки жолу басыңыз."</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Виджетти кошуу мүмкүн болбоду."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Өтүү"</string>
@@ -1870,8 +1872,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Администраторуңуз жаңыртып койгон"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Администраторуңуз жок кылып салган"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ЖАРАЙТ"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Батареяны үнөмдөгүч режиминде Караңгы тема күйгүзүлүп, фондогу аракеттер, айрым визуалдык эффекттер, белгилүү бир функциялар жана айрым тармакка туташуулар чектелип же өчүрүлөт."</string>
- <string name="battery_saver_description" msgid="8518809702138617167">"Батареяны үнөмдөгүч режиминде Караңгы тема күйгүзүлүп, фондогу аракеттер, айрым визуалдык эффекттер, белгилүү бир функциялар жана айрым тармакка туташуулар чектелип же өчүрүлөт."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Батареяны үнөмдөөчү режимде Караңгы тема күйгүзүлүп, фондогу аракеттер, айрым визуалдык эффекттер, белгилүү бир функциялар жана айрым тармакка туташуулар чектелип же өчүрүлөт."</string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Батареяны үнөмдөөчү режимде Караңгы тема күйгүзүлүп, фондогу аракеттер, айрым визуалдык эффекттер, белгилүү бир функциялар жана айрым тармакка туташуулар чектелип же өчүрүлөт."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Трафикти үнөмдөө режиминде айрым колдонмолор маалыматтарды фондо өткөрө алышпайт. Учурда сиз пайдаланып жаткан колдонмо маалыматтарды жөнөтүп/ала алат, бирок адаттагыдан азыраак өткөргөндүктөн, анын айрым функциялары талаптагыдай иштебей коюшу мүмкүн. Мисалы, сүрөттөр басылмайынча жүктөлбөйт."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Трафикти үнөмдөө режимин иштетесизби?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Күйгүзүү"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index fd83eda..c75d6b6 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"ລອງໃຊ້ລາຍນິ້ວມືອື່ນ"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ແຈ້ງເກີນໄປ"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ກະລຸນາລອງປັບແກ້"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ປ່ຽນຕຳແໜ່ງຂອງນິ້ວມືຂອງທ່ານເລັກນ້ອຍໃນແຕ່ລະເທື່ອ"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ພິສູດຢືນຢັນລາຍນິ້ວມືແລ້ວ"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ບໍ່ມີການລົງທະບຽນລາຍນິ້ວມື."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ອຸປະກອນນີ້ບໍ່ມີເຊັນເຊີລາຍນິ້ວມື."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ປິດການເຮັດວຽກຂອງເຊັນເຊີໄວ້ຊົ່ວຄາວແລ້ວ."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"ຕ້ອງປັບທຽບມາດຕະຖານເຊັນເຊີ"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ນີ້ວມື <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ໃຊ້ລາຍນິ້ວມື"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ໃຊ້ລາຍນິ້ວມື ຫຼື ການລັອກໜ້າຈໍ"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ໄອຄອນລາຍນິ້ວມື"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ປົດລັອກດ້ວຍໜ້າ"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"ລົງທະບຽນໃບໜ້າຂອງທ່ານຄືນໃໝ່"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"ເພື່ອປັບປຸງການຈຳແນກ, ກະລຸນາລົງທະບຽນໃບໜ້າຂອງທ່ານຄືນໃໝ່."</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"ຕັ້ງຄ່າການປົດລັອກດ້ວຍໜ້າ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ປົດລັອກໂທລະສັບຂອງທ່ານໂດຍການເບິ່ງມັນ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ຕັ້ງຄ່າວິທີເພີ່ມເຕີມເພື່ອປົດລັອກ"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນຮ້ອງຂໍການລຶບແພັກເກດ."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ຖາມເພື່ອໃຫ້ເພີກເສີຍການປັບແຕ່ງແບັດເຕີຣີ"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ອະນຸຍາດໃຫ້ແອັບຖາມສິດອະນຸຍາດເພື່ອເພີກເສີຍຕໍ່ການປັບແຕ່ງແບັດເຕີຣີສຳລັບແອັບນັ້ນ."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"ຊອກຫາແພັກເກດທັງໝົດ"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ອະນຸຍາດໃຫ້ແອັບເບິ່ງເຫັນແພັກເກດທີ່ຕິດຕັ້ງແລ້ວທັງໝົດໄດ້."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ແຕະສອງເທື່ອເພື່ອຄວບຄຸມການຊູມ"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ບໍ່ສາມາດເພີ່ມວິດເຈັດໄດ້."</string>
<string name="ime_action_go" msgid="5536744546326495436">"ໄປ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index ca43ce7..cd0bbe3 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -591,6 +591,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Pabandykite kitą kontrolinį kodą"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Per šviesu"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Pabandykite koreguoti"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Kaskart šiek tiek pakeiskite piršto poziciją"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Piršto antspaudas autentifikuotas"</string>
@@ -607,6 +608,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Neužregistruota jokių kontrolinių kodų."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šiame įrenginyje nėra kontrolinio kodo jutiklio."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Jutiklis laikinai išjungtas."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Reikia sukalibruoti jutiklį"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> pirštas"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Naudoti kontrolinį kodą"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Naudoti kontrolinį kodą arba ekrano užraktą"</string>
@@ -616,8 +618,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Piršto antspaudo piktograma"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Atrakinimas pagal veidą"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Pakartotinis veido registravimas"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Kad patobulintumėte atpažinimą, iš naujo užregistruokite veidą"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Atrakinimo pagal veidą nustatymas"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Atrakinkite telefoną pažiūrėję į jį"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Daugiau atrakinimo metodų nustatymas"</string>
@@ -1498,10 +1502,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Programai leidžiama pateikti užklausą dėl paketų ištrynimo."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"prašyti nepaisyti akumuliatoriaus optimizavimo nustatymų"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Programai leidžiama prašyti leidimo nepaisyti tai programai skirto akumuliatoriaus optimizavimo nustatymų."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"Teikti visų paketų užklausą"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Programai leidžiama peržiūrėti visus įdiegtus paketus."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Bakstelėkite du kartus, kad valdytumėte mastelio keitimą"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nepavyko pridėti."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Pradėti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 47094a4..90de707 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -588,6 +588,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Izmēģiniet citu pirksta nospiedumu"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Pārāk spilgts"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Mēģiniet mainīt pozīciju"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Katru reizi mazliet mainiet pirksta pozīciju."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Pirksta nospiedums tika autentificēts."</string>
@@ -604,6 +605,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nav reģistrēts neviens pirksta nospiedums."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šajā ierīcē nav pirksta nospieduma sensora."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensors ir īslaicīgi atspējots."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Nepieciešama sensora kalibrēšana."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. pirksts"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Pirksta nospieduma izmantošana"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Pirksta nospieduma vai ekrāna bloķēšanas metodes izmantošana"</string>
@@ -613,8 +615,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Pirksta nospieduma ikona"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Autorizācija pēc sejas"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Atkārtoti reģistrējiet seju"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Lai uzlabotu atpazīšanu, lūdzu, atkārtoti reģistrējiet savu seju"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Autorizācijas pēc sejas iestatīšana"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Atbloķējiet tālruni, skatoties uz to"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Citi atbloķēšanas veidi"</string>
@@ -1478,10 +1482,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Atļauj lietojumprogrammai pieprasīt pakotņu dzēšanu."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"Lūgt akumulatora optimizācijas ignorēšanu"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Ļauj lietotnei lūgt atļauju ignorēt akumulatora optimizāciju šai lietotnei."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"pieprasīt atļauju skatīt visas pakotnes"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Ļauj lietotnei skatīt visas instalētās pakotnes."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Pieskarieties divreiz, lai kontrolētu tālummaiņu."</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nevarēja pievienot logrīku."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Doties uz"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 5a9486e..663a3ad9 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Пробајте со друг отпечаток"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Премногу светло"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Пробајте да го приспособите прстот"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Менувајте ја положбата на прстот по малку секој пат"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечатокот е проверен"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Не се запишани отпечатоци."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Уредов нема сензор за отпечатоци."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензорот е привремено оневозможен."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Сензорот треба да се калибрира"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Прст <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Користи отпечаток"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Користи отпечаток или заклучување екран"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Икона за отпечатоци"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Отклучување со лик"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Повторно регистрирајте го ликот"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"За да се подобри препознавањето, повторно регистрирајте го ликот"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Поставете „Отклучување со лик“"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Отклучете го телефонот со гледање во него"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Поставете уште начини за отклучување"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Дозволува апликацијата да бара бришење на пакетите."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"прашај дали да се игнорираат оптимизациите на батеријата"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Овозможува апликацијата да побара дозвола за игнорирање на оптимизациите на батеријата за таа апликација."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"пребарување на сите пакети"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Дозволува апликацијата да ги гледа сите инсталирани пакети."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Допрете двапати за контрола на зумот"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Не може да се додаде виџет."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Оди"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 9ca392a..a7d6ded 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"നില ബാർ വിപുലീകരിക്കുന്നതിനോ ചുരുക്കുന്നതിനോ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"ലോക്ക് ചെയ്ത ഒരു ഉപകരണത്തിൽ പൂർണ്ണ സ്ക്രീൻ ആക്റ്റിവിറ്റികളായി അറിയിപ്പുകൾ പ്രദർശിപ്പിക്കുക"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"ലോക്ക് ചെയ്ത ഒരു ഉപകരണത്തിൽ പൂർണ്ണ സ്ക്രീൻ ആക്റ്റിവിറ്റികളായി അറിയിപ്പുകൾ പ്രദർശിപ്പിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"കുറുക്കുവഴികൾ ഇൻസ്റ്റാളുചെയ്യുക"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"കുറുക്കുവഴികൾ ഇൻസ്റ്റാൾ ചെയ്യുക"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"ഉപയോക്തൃ ഇടപെടലില്ലാതെ ഹോംസ്ക്രീൻ കുറുക്കുവഴികൾ ചേർക്കാൻ ഒരു അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"കുറുക്കുവഴികൾ അൺഇൻസ്റ്റാളുചെയ്യുക"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"ഉപയോക്തൃ ഇടപെടലില്ലാതെ ഹോംസ്ക്രീൻ കുറുക്കുവഴികൾ നീക്കംചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"മറ്റൊരു ഫിംഗർപ്രിന്റ് ഉപയോഗിച്ച് നോക്കുക"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"തെളിച്ചം വളരെയധികമാണ്"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"അൽപ്പം നീക്കി നോക്കൂ"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ഓരോ തവണയും നിങ്ങളുടെ വിരലിന്റെ സ്ഥാനം ചെറുതായി മാറ്റുക"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ഫിംഗർപ്രിന്റ് പരിശോധിച്ചുറപ്പിച്ചു"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"വിരലടയാളങ്ങൾ എൻറോൾ ചെയ്തിട്ടില്ല."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ഈ ഉപകരണത്തിൽ ഫിംഗർപ്രിന്റ് സെൻസറില്ല."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"സെൻസർ താൽക്കാലികമായി പ്രവർത്തനരഹിതമാക്കി."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"സെൻസറിന് കാലിബ്രേഷൻ ആവശ്യമാണ്"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ഫിംഗർ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കുക"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ഫിംഗർപ്രിന്റ് അല്ലെങ്കിൽ സ്ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ഫിംഗർപ്രിന്റ് ഐക്കൺ"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ഫെയ്സ് അൺലോക്ക്"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"നിങ്ങളുടെ മുഖം വീണ്ടും എൻറോൾ ചെയ്യൂ"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"തിരിച്ചറിയൽ മെച്ചപ്പെടുത്താൻ, നിങ്ങളുടെ മുഖം ദയവായി വീണ്ടും എൻറോൾ ചെയ്യൂ"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"ഫെയ്സ് അൺലോക്ക് സജ്ജീകരിക്കുക"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ഫോണിലേക്ക് നോക്കി അത് അൺലോക്ക് ചെയ്യുക"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"അൺലോക്ക് ചെയ്യുന്നതിനുള്ള കൂടുതൽ വഴികൾ സജ്ജീകരിക്കുക"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"പാക്കേജുകളെ ഇല്ലാതാക്കാനുള്ള അഭ്യർത്ഥന നടത്താൻ ആപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ബാറ്ററി ഒപ്റ്റിമൈസേഷനുകൾ അവഗണിക്കാൻ ആവശ്യപ്പെടുക"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ആപ്പിന് വേണ്ടിയുള്ള ബാറ്ററി ഒപ്റ്റിമൈസേഷനുകളെ അവഗണിക്കാനുള്ള അനുമതി ചോദിക്കുന്നതിന് ആപ്പിനെ അനുവദിക്കുന്നു."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"എല്ലാ പാക്കേജുകളും നോക്കുക"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ഇൻസ്റ്റാൾ ചെയ്ത എല്ലാ പാക്കേജുകളും കാണാൻ ഒരു ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"സൂം നിയന്ത്രണം ലഭിക്കാൻ രണ്ടുതവണ ടാപ്പുചെയ്യുക"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"വിജറ്റ് ചേർക്കാനായില്ല."</string>
<string name="ime_action_go" msgid="5536744546326495436">"പോവുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 4c01871d..f075050 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Өөр хурууны хээ туршина уу"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Хэт гэрэлтэй байна"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Тохируулж үзнэ үү"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Хурууныхаа байрлалыг тухай бүрд бага зэрэг өөрчилнө үү"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Хурууны хээг нотолсон"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Бүртгүүлсэн хурууны хээ алга."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Энэ төхөөрөмжид хурууны хээ мэдрэгч алга."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Мэдрэгчийг түр хугацаанд идэвхгүй болгосон."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Мэдрэгчид тохируулга шаардлагатай"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Хурууны хээ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Хурууны хээ ашиглах"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Хурууны хээ эсвэл дэлгэцийн түгжээ ашиглах"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Хурууны хээний дүрс"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Царайгаар түгжээ тайлах"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Царайгаа дахин бүртгүүлнэ үү"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Танилтыг сайжруулахын тулд царайгаа дахин бүртгүүлнэ үү"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Царайгаар түгжээ тайлахыг тохируулах"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Утас руугаа харж түгжээг нь тайлна уу"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Түгжээ тайлах илүү олон арга тохируулна уу"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Апп-д багц устгах хүсэлт тавихыг зөвшөөрнө."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"батерейны оновчлол алгасахыг асуух"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Тухайн аппaaс батерейны оновчлол алгасах зөвшөөрөл асуухыг зөвшөөрдөг."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"бүх багцыг лавлах"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Аппап бүх суулгасан багцыг харахыг зөвшөөрнө."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Өсгөх контрол дээр хоёр удаа товшино уу"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Виджет нэмж чадсангүй."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Очих"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 1dfa5de..22905d4 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -248,15 +248,15 @@
<string name="global_action_power_options" msgid="1185286119330160073">"पॉवर"</string>
<string name="global_action_restart" msgid="4678451019561687074">"रीस्टार्ट करा"</string>
<string name="global_action_emergency" msgid="1387617624177105088">"आणीबाणी"</string>
- <string name="global_action_bug_report" msgid="5127867163044170003">"बग रीपोर्ट"</string>
+ <string name="global_action_bug_report" msgid="5127867163044170003">"बग रिपोर्ट"</string>
<string name="global_action_logout" msgid="6093581310002476511">"सेशन समाप्त करा"</string>
<string name="global_action_screenshot" msgid="2610053466156478564">"स्क्रीनशॉट"</string>
<string name="bugreport_title" msgid="8549990811777373050">"बग रिपोर्ट"</string>
- <string name="bugreport_message" msgid="5212529146119624326">"ई-मेल मेसेज म्हणून पाठविण्यासाठी, हे तुमच्या सद्य डिव्हाइस स्थितीविषयी माहिती संकलित करेल. बग रीपोर्ट सुरू करण्यापासून तो पाठविण्यापर्यंत थोडा वेळ लागेल; कृपया धीर धरा."</string>
+ <string name="bugreport_message" msgid="5212529146119624326">"ईमेल मेसेज म्हणून पाठविण्यासाठी, हे तुमच्या सध्याच्या डिव्हाइस स्थितीविषयी माहिती संकलित करेल. बग रिपोर्ट सुरू करण्यापासून तो पाठवण्यापर्यंत थोडा वेळ लागेल; कृपया धीर धरा."</string>
<string name="bugreport_option_interactive_title" msgid="7968287837902871289">"परस्परसंवादी अहवाल"</string>
<string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"बहुतांश प्रसंगांमध्ये याचा वापर करा. ते तुम्हाला अहवालाच्या प्रगतीचा मागोवा घेण्याची, समस्येविषयी आणखी तपाशील एंटर करण्याची आणि स्क्रीनशॉट घेण्याची अनुमती देते. ते कदाचित अहवाल देण्यासाठी बराच वेळ घेणारे कमी-वापरलेले विभाग वगळू शकते."</string>
<string name="bugreport_option_full_title" msgid="7681035745950045690">"संपूर्ण अहवाल"</string>
- <string name="bugreport_option_full_summary" msgid="1975130009258435885">"तुमचे डिव्हाइस प्रतिसाद देत नाही किंवा खूप धीमे असते किंवा तुम्हाला सर्व अहवाल विभागांची आवश्यकता असते तेव्हा कमीतकमी सिस्टम हस्तक्षेपासाठी या पर्यायाचा वापर करा. तुम्हाला आणखी तपशील एंटर करण्याची किंवा अतिरिक्त स्क्रीनशॉट घेण्याची अनुमती देत नाही."</string>
+ <string name="bugreport_option_full_summary" msgid="1975130009258435885">"तुमचे डिव्हाइस प्रतिसाद देत नाही किंवा खूप धीमे असते अथवा तुम्हाला सर्व अहवाल विभागांची आवश्यकता असते तेव्हा कमीतकमी सिस्टम हस्तक्षेपासाठी या पर्यायाचा वापर करा. तुम्हाला आणखी तपशील एंटर करण्याची किंवा अतिरिक्त स्क्रीनशॉट घेण्याची अनुमती देत नाही."</string>
<plurals name="bugreport_countdown" formatted="false" msgid="3906120379260059206">
<item quantity="other">दोष अहवालासाठी <xliff:g id="NUMBER_1">%d</xliff:g> सेकंदांमध्ये स्क्रीनशॉट घेत आहे.</item>
<item quantity="one">दोष अहवालासाठी <xliff:g id="NUMBER_0">%d</xliff:g> सेकंदामध्ये स्क्रीनशॉट घेत आहे.</item>
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"स्टेटस बार विस्तृत करण्यासाठी किंवा संक्षिप्त करण्यासाठी अॅप ला अनुमती देते."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"लॉक केलेल्या डिव्हाइसवर फुल स्क्रीन अॅक्टिव्हिटी म्हणून सूचना प्रदर्शित करणे"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"लॉक केलेल्या डिव्हाइसवर फुल स्क्रीन अॅक्टिव्हिटी म्हणून सूचना प्रदर्शित करण्यासाठी ॲपला अनुमती द्या"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"शॉर्टकट स्थापित करा"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"शॉर्टकट इंस्टॉल करा"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"अनुप्रयोगाला वापरकर्ता हस्तक्षेपाशिवाय मुख्यस्क्रीन शॉर्टकट जोडण्याची अनुमती देते."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"शॉर्टकट विस्थापित करा"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"अनुप्रयोगाला वापरकर्ता हस्तक्षेपाशिवाय मुख्यस्क्रीन शॉर्टकट काढण्याची अनुमती देते."</string>
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"दुसरी फिंगरप्रिंट वापरून पाहा"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"खूप प्रखर"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"अॅडजस्ट करण्याचा प्रयत्न करा"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"तुमच्या बोटाची स्थिती प्रत्येक वेळी थोडीशी बदला"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"फिंगरप्रिंट ऑथेंटिकेट केली आहे"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कोणत्याही फिंगरप्रिंटची नोंद झाली नाही"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"या डिव्हाइसमध्ये फिंगरप्रिंट सेन्सर नाही."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"सेन्सर तात्पुरता बंद केला आहे."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"सेन्सरला कॅलिब्रेट करण्याची आवश्यकता आहे"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g> बोट"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"फिंगरप्रिंट वापरा"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"फिंगरप्रिंट किंवा स्क्रीन लॉक वापरा"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"फिंगरप्रिंट आयकन"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"फेस अनलॉक"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"तुमच्या चेहऱ्याची पुन्हा नोंदणी करा"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"ओळखण्यामध्ये सुधारणा करण्यासाठी, कृपया तुमच्या चेहऱ्याची पुन्हा नोंदणी करा"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"फेस अनलॉक सेट करा"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"तुमच्या फोनकडे पाहून तो अनलॉक करा"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"अनलॉक करण्याच्या आणखी पद्धती सेट करा"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"अनुप्रयोगास पॅकेज हटवण्यासाठी विनंती करण्याची अनुमती देते."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"बॅटरी ऑप्टिमायझेशन दुर्लक्षित करण्यास सांगा"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"त्या ॲपसाठी बॅटरी ऑप्टिमायझेशन दुर्लक्षित करण्यासाठी ॲपला परवानगी मागण्याची अनुमती देते."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"सर्व पॅकेजविषयी क्वेरी करा"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ॲपला इंस्टॉल केलेले सर्व पॅकेज पाहण्याची अनुमती देते."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"झूम नियंत्रणासाठी दोनदा टॅप करा"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"विजेट जोडू शकलो नाही."</string>
<string name="ime_action_go" msgid="5536744546326495436">"जा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 2960ca2..3c105fd 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Cuba cap jari lain"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Terlalu terang"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Cuba selaraskan"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Tukar sedikit kedudukan jari anda setiap kali pergerakan dilakukan"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Cap jari disahkan"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Tiada cap jari didaftarkan."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Peranti ini tiada penderia cap jari."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Penderia dilumpuhkan sementara."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Penderia memerlukan penentukuran"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Jari <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gunakan cap jari"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Gunakan cap jari atau kunci skrin"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon cap jari"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Buka Kunci Wajah"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Daftarkan semula wajah anda"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Untuk meningkatkan pengecaman, sila daftarkan semula wajah anda"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Sediakan Buka Kunci Wajah"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Buka kunci telefon anda dengan melihat telefon anda"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Sediakan lebih banyak cara untuk membuka kunci"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Membenarkan aplikasi meminta pemadaman pakej."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"minta kebenaran untuk mengabaikan pengoptimuman bateri"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Membenarkan apl meminta kebenaran untuk mengabaikan pengoptimuman bateri untuk apl itu."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"buat pertanyaan untuk semua pakej"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Membenarkan apl melihat semua pakej yang dipasang."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Ketik dua kali untuk mendapatkan kawalan zum"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Tidak dapat menambahkan widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Pergi"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 2c0ed7b..01a2157 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"အက်ပ်အား အခြေအနေပြ ဘားကို ချဲ့ခွင့် သို့မဟုတ် ခေါက်သိမ်းခွင့် ပြုသည်။"</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"လော့ခ်ချထားသော စက်ပစ္စည်းပေါ်တွင် အကြောင်းကြားချက်များကို ဖန်သားပြင်အပြည့် လုပ်ဆောင်ချက်များအဖြစ် ပြခြင်း"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"လော့ခ်ချထားသော စက်ပစ္စည်းပေါ်တွင် အကြောင်းကြားချက်များကို ဖန်သားပြင်အပြည့် လုပ်ဆောင်ချက်များအဖြစ် ပြရန် အက်ပ်ကို ခွင့်ပြုသည်"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"အတိုကောက်များအား ထည့်သွင်းခြင်း"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"ဖြတ်လမ်းလင့်ခ်များ ထည့်သွင်းခြင်း"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"အပလီကေးရှင်းအား အသုံးပြုသူ လုပ်ဆောင်ခြင်း မပါပဲ ပင်မ မြင်ကွင်းအား ပြောင်းလဲခွင့် ပေးခြင်း"</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"အတိုကောက်များ ဖယ်ထုတ်ခြင်း"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"အပလီကေးရှင်းအား အသုံးပြုသူ လုပ်ဆောင်ခြင်း မပါပဲ ပင်မ မြင်ကွင်းအား ဖယ်ရှားခွင့် ပေးခြင်း"</string>
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"အခြားလက်ဗွေဖြင့် စမ်းကြည့်ပါ"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"အလွန် လင်းသည်"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ပြင်ဆင်ကြည့်ပါ"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"အကြိမ်တိုင်း သင့်လက်ချောင်း၏တည်နေရာကို အနည်းငယ်ပြောင်းပါ"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"လက်ဗွေကို အထောက်အထား စိစစ်ပြီးပါပြီ"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"မည်သည့် လက်ဗွေကိုမျှ ထည့်သွင်းမထားပါ။"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ဤစက်တွင် လက်ဗွေအာရုံခံကိရိယာ မရှိပါ။"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"အာရုံခံကိရိယာကို ယာယီပိတ်ထားသည်။"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"အာရုံခံကိရိယာက စံကိုက်ချိန်ညှိခြင်း လိုအပ်သည်"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"လက်ချောင်း <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"လက်ဗွေ သုံးခြင်း"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"လက်ဗွေ (သို့) ဖန်သားပြင်လော့ခ်ချခြင်းကို သုံးခြင်း"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"လက်ဗွေ သင်္ကေတ"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်း"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"သင့်မျက်နှာကို စာရင်းပြန်သွင်းပါ"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"ပိုမှတ်မိစေရန် သင့်မျက်နှာကို စာရင်းပြန်သွင်းပါ"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်းကို ထည့်သွင်းပါ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"သင့်ဖုန်းကိုကြည့်၍ သော့ဖွင့်ပါ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"သော့ဖွင့်ရန် နောက်ထပ်နည်းလမ်းများကို စနစ်ထည့်သွင်းပါ"</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"clipboardထံ စာသားအားကူးယူမည်"</string>
<string name="copied" msgid="4675902854553014676">"မိတ္တူကူးပြီးပါပြီ"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> မှ <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> သို့ ကူးထည့်ထားသည်"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> က ဒေတာကို သင့်ကလစ်ဘုတ်မှ ကူးထည့်ထားသည်"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> က သင့်ကလစ်ဘုတ်မှ ကူးထည့်ထားသည်"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> က သင်မိတ္တူကူးထားသော စာသားကို ထည့်လိုက်သည်"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> က သင်မိတ္တူကူးထားသော ပုံကို ထည့်လိုက်သည်"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> က သင်မိတ္တူကူးထားသော အကြောင်းအရာကို ထည့်လိုက်သည်"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"အပလီကေးရှင်းတစ်ခုအား ပက်ကေ့ဂျ်များကို ဖျက်ရန် တောင်းဆိုခွင့်ပေးပါ။"</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ဘက်ထရီ ပိုမိုကောင်းမွန်အောင် ပြုလုပ်ခြင်းကို လျစ်လျူရှုရန် တောင်းဆိုပါ"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ဘက်ထရီ ပိုမိုကောင်းမွန်အောင် ပြုလုပ်ခြင်းကို လျစ်လျူရှုရန်အတွက် ခွင့်ပြုချက်တောင်းရန် အက်ပ်ကို ခွင့်ပြုပါ။"</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"ပက်ကေ့ဂျ်အားလုံးကို မေးမြန်းခြင်း"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ထည့်သွင်းထားသော ပက်ကေ့ဂျ်အားလုံး ကြည့်ရန် အက်ပ်ကို ခွင့်ပြုပါ။"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ဇူးမ်အသုံးပြုရန် နှစ်ချက်တို့ပါ"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ဝဒ်ဂျက်ထည့်လို့ မရပါ"</string>
<string name="ime_action_go" msgid="5536744546326495436">"သွားပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 0ceebf1..a788879 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prøv et annet fingeravtrykk"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"For lyst"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Prøv å justere"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Endre posisjonen til fingeren litt hver gang"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeravtrykket er godkjent"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ingen fingeravtrykk er registrert."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enheten har ikke fingeravtrykkssensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensoren er midlertidig slått av."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Sensoren må kalibreres"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Bruk fingeravtrykk"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Bruk fingeravtrykk eller skjermlås"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon for fingeravtrykk"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Ansiktslås"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Registrer ansiktet ditt på nytt"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"For å forbedre gjenkjennelse, registrer ansiktet ditt på nytt"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfigurer ansiktslås"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Lås opp telefonen ved å se på den"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfigurer flere måter å låse opp på"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Lar apper be om sletting av pakker."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"be om å ignorere batterioptimaliseringer"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Gjør det mulig for apper å be om tillatelse til å ignorere batterioptimaliseringer for disse appene."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"søk i alle pakker"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Lar en app se alle installerte pakker."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Trykk to ganger for zoomkontroll"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Kunne ikke legge til modulen."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Utfør"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 8e7ca71..1adeaad 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"एपलाई स्थिति पट्टि विस्तार वा संकुचन गर्न अनुमति दिन्छ।"</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"लक गरिएको डिभाइसमा स्क्रिनभरि देखिने सूचनाहरू देखाइयोस्"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"यो अनुमति दिइएमा एपले लक गरिएको डिभाइसमा स्क्रिनभरि देखिने सूचनाहरू देखाउन सक्छ"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"सर्टकट स्थापना गर्नुहोस्"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"सर्टकट इन्स्टल गर्ने"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा एपलाई सर्टकटमा थप्नको लागि अनुमति दिन्छ।"</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"सर्टकटहरूको स्थापन रद्द गर्नुहोस्"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा एपलाई सर्टकटमा हटाउनको लागि अनुमति दिन्छ।"</string>
@@ -438,9 +438,9 @@
<string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"अधिक स्थान प्रदायक आदेशहरू पहुँच गर्नुहोस्"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"एपलाई अतिरिक्त स्थान प्रदायक आदेशहरू पहुँच गर्न अनुमति दिन्छ। यो एपलाई GPS वा अन्य स्थान स्रोतहरूको संचालन साथै हस्तक्षेप गर्न अनुमति दिन सक्छ।"</string>
<string name="permlab_accessFineLocation" msgid="6426318438195622966">"अग्रभूमिमा मात्र सटीक स्थानमाथि पहुँच राख्नुहोस्"</string>
- <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"यो एप चलाएका बेला यसले लोकेसनमा आधारित सेवाहरूबाट तपाईंको स्थानको सटीक जानकारी प्राप्त गर्न सक्छ। तपाईंको डिभाइसमा लोकेसनमा आधारित सेवाहरू सक्रिय गरिएको छ भने मात्र यो एपले स्थानको जानकारी प्राप्त गर्न सक्छ। यसले ब्याट्रीको उपयोग बढाउन सक्छ।"</string>
+ <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"यो एप चलाएका बेला यसले लोकेसन सर्भिसबाट तपाईंको स्थानको सटीक जानकारी प्राप्त गर्न सक्छ। तपाईंको डिभाइसमा लोकेसन सर्भिस सक्रिय गरिएको छ भने मात्र यो एपले स्थानको जानकारी प्राप्त गर्न सक्छ। यसले ब्याट्रीको उपयोग बढाउन सक्छ।"</string>
<string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"अग्रभागमा मात्र अनुमानित स्थानमाथि पहुँच राख्नुहोस्"</string>
- <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"यो एप चलाएका बेला यसले लोकेसनमा आधारित सेवाहरूबाट तपाईंको स्थानको अनुमानित जानकारी प्राप्त गर्न सक्छ। तपाईंको डिभाइसमा लोकेसनमा आधारित सेवाहरू सक्रिय गरिएको छ भने मात्र यो एपले स्थानको जानकारी प्राप्त गर्न सक्छ।"</string>
+ <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"यो एप चलाएका बेला यसले लोकेसन सर्भिसबाट तपाईंको स्थानको अनुमानित जानकारी प्राप्त गर्न सक्छ। तपाईंको डिभाइसमा लोकेसन सर्भिस सक्रिय गरिएको छ भने मात्र यो एपले स्थानको जानकारी प्राप्त गर्न सक्छ।"</string>
<string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"पृष्ठभूमिमा स्थानसम्बन्धी पहुँच"</string>
<string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"यो एपले जुनसुकै बेला (एप नचलाएका बेलामा पनि) स्थानमाथि पहुँच राख्न सक्छ।"</string>
<string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"तपाईँका अडियो सेटिङहरू परिवर्तन गर्नुहोस्"</string>
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"अर्को फिंगरप्रिन्ट प्रयोग गरी हेर्नुहोस्"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ज्यादै उज्यालो छ"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"सेन्सरमा सही तरिकाले औँला राखेर हेर्नुहोस्"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"हरेक पटक आफ्नो औँला थोरै यताउता सार्नुहोस्"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"फिंगरप्रिन्ट प्रमाणीकरण गरियो"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कुनै पनि फिंगरप्रिन्ट दर्ता गरिएको छैन।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"यो डिभाइसमा कुनै पनि फिंगरप्रिन्ट सेन्सर छैन।"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"केही समयका लागि सेन्सर असक्षम पारियो।"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"सेन्सर क्यालिब्रेट गर्नु पर्ने हुन्छ"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"औंला <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"फिंगरप्रिन्ट प्रयोग गर्नुहोस्"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"फिंगरप्रिन्ट वा स्क्रिन लक प्रयोग गर्नुहोस्"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"फिंगरप्रिन्ट आइकन"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"फेस अनलक"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"आफ्नो अनुहार पुनः दर्ता गर्नुहोस्"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"अनुहार पहिचानको गुणस्तर सुधार गर्न कृपया आफ्नो अनुहार पुनः दर्ता गर्नुहोस्"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"फेस अनलक सेटअप गर्नुहोस्"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"फोनमा हेरेकै भरमा फोन अनलक गर्नुहोस्"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"अनलक गर्ने अन्य तरिकाहरू सेटअप गर्नुहोस्"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"एपलाई प्याकेजहरू मेटाउने अनुरोध गर्न अनुमति दिन्छ।"</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ब्याट्री सम्बन्धी अनुकूलनहरूलाई बेवास्ता गर्न सोध्नुहोस्"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"कुनै एपलाई त्यसका ब्याट्री सम्बन्धी अनुकूलनहरूलाई बेवास्ता गर्नाका लागि अनुमति माग्न दिन्छ।"</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"सबै प्याकेजहरू खोज्नुहोस्"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"यसले यस एपलाई इन्स्टल गरिएका सबै प्याकेजहरू हेर्ने अनुमति दिन्छ।"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"जुम नियन्त्रणको लागि दुई चोटि ट्याप गर्नुहोस्"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"विजेट थप गर्न सकिँदैन।"</string>
<string name="ime_action_go" msgid="5536744546326495436">"जानुहोस्"</string>
@@ -2002,7 +2004,7 @@
<string name="app_category_game" msgid="4534216074910244790">"खेलहरू"</string>
<string name="app_category_audio" msgid="8296029904794676222">"सङ्गीत तथा अडियो"</string>
<string name="app_category_video" msgid="2590183854839565814">"चलचित्र तथा भिडियो"</string>
- <string name="app_category_image" msgid="7307840291864213007">"फोटो तथा छविहरू"</string>
+ <string name="app_category_image" msgid="7307840291864213007">"फोटो तथा फोटो"</string>
<string name="app_category_social" msgid="2278269325488344054">"सामाजिक तथा सञ्चार"</string>
<string name="app_category_news" msgid="1172762719574964544">"समाचार तथा पत्रिकाहरू"</string>
<string name="app_category_maps" msgid="6395725487922533156">"नक्सा तथा नेभिगेसन"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 9700939..6900921 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Probeer een andere vingerafdruk"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Te veel licht"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Verplaats je vinger"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Verander de positie van je vinger steeds een beetje"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Vingerafdruk geverifieerd"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Geen vingerafdrukken geregistreerd."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dit apparaat heeft geen vingerafdruksensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor staat tijdelijk uit."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Sensor moet worden gekalibreerd"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Vinger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Vingerafdruk gebruiken"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Vingerafdruk of schermvergrendeling gebruiken"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Vingerafdruk-icoon"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Ontgrendelen via gezicht"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Je gezicht opnieuw registreren"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Registreer je gezicht opnieuw om de herkenning te verbeteren"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Ontgrendeling via gezichtsherkenning instellen"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ontgrendel je telefoon door ernaar te kijken"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Stel meer manieren in om te ontgrendelen"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Hiermee kan een app verwijdering van pakketten aanvragen."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"vragen om batterijoptimalisatie te negeren"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Hiermee kan een app rechten vragen om batterijoptimalisatie voor die app te negeren."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"alle pakketten opvragen"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Hiermee kan een app alle geïnstalleerde pakketten zien."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tik twee keer voor zoomregeling"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Kan widget niet toevoegen."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Ga"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index d6b1e1e..7bf760d 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"ଷ୍ଟାଟସ୍ ବାର୍କୁ ବଡ଼ କିମ୍ବା ଛୋଟ କରିବା ପାଇଁ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"ଏକ ଲକ୍ ହୋଇଥିବା ଡିଭାଇସରେ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ୍ କାର୍ଯ୍ୟକଳାପ ଭାବେ ଦେଖାନ୍ତୁ"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"ଏକ ଲକ୍ ହୋଇଥିବା ଡିଭାଇସରେ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନ୍ କାର୍ଯ୍ୟକଳାପ ଭାବେ ଦେଖାଇବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଏ"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"ଶର୍ଟକଟ୍ ଇନଷ୍ଟଲ୍ କରନ୍ତୁ"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"ସର୍ଟକଟ୍ ଇନଷ୍ଟଲ୍ କରନ୍ତୁ"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"ୟୁଜର୍ଙ୍କ ବିନା ବାଧାରେ ହୋମ୍ସ୍କ୍ରୀନ୍ ସର୍ଟକଟ୍ ଯୋଡ଼ିବାକୁ ଏକ ଆପ୍ଲିକେଶନ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"ଶର୍ଟକଟ୍ ଅନଇନଷ୍ଟଲ୍ କରନ୍ତୁ"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"ୟୁଜର୍ଙ୍କ ବିନା ବାଧାରେ ହୋମ୍ସ୍କ୍ରୀନ୍ର ଶର୍ଟକଟ୍ ବାହାର କରିବା ପାଇଁ ଗୋଟିଏ ଆପ୍ଲିକେଶନ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
@@ -585,6 +585,8 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"ଅନ୍ୟ ଏକ ଟିପଚିହ୍ନ ବ୍ୟବହାର କରି ଦେଖନ୍ତୁ"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ବହୁତ ଉଜ୍ଜ୍ୱଳ"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ଆଡଜଷ୍ଟ କରି ଦେଖନ୍ତୁ"</string>
+ <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) -->
+ <skip />
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ଟିପଚିହ୍ନ ପ୍ରମାଣିତ ହେଲା"</string>
@@ -601,6 +603,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"କୌଣସି ଆଙ୍ଗୁଠି ଚିହ୍ନ ପଞ୍ଜୀକୃତ ହୋଇନାହିଁ।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ଏହି ଡିଭାଇସ୍ରେ ଟିପଚିହ୍ନ ସେନ୍ସର୍ ନାହିଁ।"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ସେନ୍ସରକୁ ଅସ୍ଥାୟୀ ଭାବେ ଅକ୍ଷମ କରାଯାଇଛି।"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"ସେନ୍ସରକୁ କାଲିବ୍ରେଟ୍ କରିବା ଆବଶ୍ୟକ"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ଆଙ୍ଗୁଠି <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ଟିପଚିହ୍ନ ବା ସ୍କ୍ରିନ୍ ଲକ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
@@ -610,8 +613,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ଟିପଚିହ୍ନ ଆଇକନ୍"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ଫେସ୍ ଅନଲକ୍"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"ଆପଣଙ୍କର ମୁହଁ ପୁଣି-ଏନ୍ରୋଲ୍ କରନ୍ତୁ"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"ଚିହ୍ନଟକରଣକୁ ଉନ୍ନତ କରିବା ପାଇଁ, ଦୟାକରି ଆପଣଙ୍କର ମୁହଁ ପୁଣି-ଏନ୍ରୋଲ୍ କରନ୍ତୁ।"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"ଫେସ୍ ଅନଲକ୍ ସେଟ୍ ଅପ୍ କରନ୍ତୁ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ଫୋନକୁ ଦେଖି ଏହାକୁ ଅନଲକ୍ କରନ୍ତୁ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ଅନଲକ୍ କରିବା ପାଇଁ ଆହୁରି ଅଧିକ ଉପାୟ ସେଟ୍ ଅପ୍ କରନ୍ତୁ"</string>
@@ -1239,7 +1244,7 @@
<string name="unsupported_display_size_show" msgid="980129850974919375">"ସର୍ବଦା ଦେଖାନ୍ତୁ"</string>
<string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଏକ କମ୍ପାଟିବଲ୍ ନଥିବା Android OSରେ ତିଆରି ହୋଇଛି ଏବଂ ଆକସ୍ମିକ ଗତିବିଧି ଦେଖାଦେଇପାରେ। ଆପ୍ର ଏକ ଅପଡେଟ୍ ଭର୍ସନ୍ ଉପଲବ୍ଧ ରହିପାରେ।"</string>
<string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"ସର୍ବଦା ଦେଖାନ୍ତୁ"</string>
- <string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"କୌଣସି ଅପଡେଟ୍ ଅଛି କି ନାହିଁ ଦେଖନ୍ତୁ"</string>
+ <string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"ଅପଡେଟ୍ ପାଇଁ ଯାଞ୍ଚ କରନ୍ତୁ"</string>
<string name="smv_application" msgid="3775183542777792638">"ଆପ୍ <xliff:g id="APPLICATION">%1$s</xliff:g> (ପ୍ରକ୍ରିୟା <xliff:g id="PROCESS">%2$s</xliff:g>) ଏହାର ସ୍ୱ-ଲାଗୁ କରାଯାଇଥିବା ଷ୍ଟ୍ରିକ୍ଟ-ମୋଡ୍ ପଲିସୀ ଉଲ୍ଲଂଘନ କରିଛି।"</string>
<string name="smv_process" msgid="1398801497130695446">"ଏହି {0/PROCESS<xliff:g id="PROCESS">%1$s</xliff:g> ନିଜ ଦ୍ୱାରା ଲାଗୁ କରାଯାଇଥିବା ଷ୍ଟ୍ରିକ୍ଟମୋଡ୍ ପଲିସୀକୁ ଉଲ୍ଲଂଘନ କରିଛି।"</string>
<string name="android_upgrading_title" product="default" msgid="7279077384220829683">"ଫୋନ୍ ଅପଡେଟ୍ ହେଉଛି…"</string>
@@ -1458,10 +1463,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ପ୍ୟାକେଜ୍ଗୁଡ଼ିକ ଡିଲିଟ୍ କରିବା ପାଇଁ ଅନୁରୋଧ କରିବାକୁ ଏକ ଆପ୍ଲିକେଶନକୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ବ୍ୟାଟେରୀ ଅନୁକୂଳନ ଏଡ଼ାଇବା ପାଇଁ ପଚାରନ୍ତୁ"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ଆପ୍ ପାଇଁ ବ୍ୟାଟେରୀ ଅନୁକୂଳନ ଏଡ଼ାଇବାର ଅନୁମତି ମାଗିବା ନିମନ୍ତେ ଆପ୍କୁ ଅନୁମତି ଦେଇଥାଏ।"</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"ସବୁ ପ୍ୟାକେଜ୍ ବିଷୟରେ କ୍ୱେରୀ କରନ୍ତୁ"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ଇନଷ୍ଟଲ୍ କରାଯାଇଥିବା ସମସ୍ତ ପ୍ୟାକେଜକୁ ଦେଖିବା ପାଇଁ ଏକ ଆପକୁ ଅନୁମତି ଦିଏ।"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ଜୁମ୍ ନିୟନ୍ତ୍ରଣ ପାଇଁ ଦୁଇଥର ଟାପ୍ କରନ୍ତୁ"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ୱିଜେଟ୍ ଯୋଡ଼ିପାରିବ ନାହିଁ।"</string>
<string name="ime_action_go" msgid="5536744546326495436">"ଯାଆନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 9a73f9b..469686c 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"ਕੋਈ ਹੋਰ ਫਿੰਗਰਪ੍ਰਿੰਟ ਵਰਤ ਕੇ ਦੇਖੋ"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ਬਹੁਤ ਜ਼ਿਆਦਾ ਚਮਕ"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ਵਿਵਸਥਿਤ ਕਰਕੇ ਦੇਖੋ"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ਹਰ ਵਾਰ ਆਪਣੀ ਉਂਗਲ ਨੂੰ ਥੋੜ੍ਹਾ ਹਿਲਾਓ"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਪ੍ਰਮਾਣਿਤ ਹੋਇਆ"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ਕੋਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਰਜ ਨਹੀਂ ਕੀਤੇ ਗਏ।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨਹੀਂ ਹੈ।"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ਸੈਂਸਰ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"ਸੈਂਸਰ ਨੂੰ ਕੈਲੀਬਰੇਸ਼ਨ ਦੀ ਲੋੜ ਹੈ"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ਉਂਗਲ <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਜਾਂ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਪ੍ਰਤੀਕ"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ਫ਼ੇਸ ਅਣਲਾਕ"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"ਆਪਣਾ ਚਿਹਰਾ ਮੁੜ-ਦਰਜ ਕਰੋ"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"ਪਛਾਣ ਨੂੰ ਬਿਹਤਰ ਬਣਾਉਣ ਲਈ, ਕਿਰਪਾ ਕਰਕੇ ਆਪਣੇ ਚਿਹਰੇ ਨੂੰ ਮੁੜ-ਦਰਜ ਕਰੋ"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"ਫ਼ੇਸ ਅਣਲਾਕ ਦਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ਆਪਣੇ ਫ਼ੋਨ ਵੱਲ ਦੇਖ ਕੇ ਇਸਨੂੰ ਅਣਲਾਕ ਕਰੋ"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ਅਣਲਾਕ ਕਰਨ ਦੇ ਹੋਰ ਤਰੀਕਿਆਂ ਦਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ਕਿਸੇ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਪੈਕੇਜਾਂ ਨੂੰ ਮਿਟਾਉਣ ਦੀ ਬੇਨਤੀ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦੀ ਹੈ।"</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ਬੈਟਰੀ ਸੁਯੋਗਤਾਵਾਂ ਨੂੰ ਅਣਡਿੱਠ ਕਰਨ ਲਈ ਪੁੱਛੋ"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ਕਿਸੇ ਐਪ ਨੂੰ ਉਸ ਵਾਸਤੇ ਬੈਟਰੀ ਸੁਯੋਗਤਾਵਾਂ ਨੂੰ ਅਣਡਿੱਠ ਕਰਨ ਲਈ ਇਜਾਜ਼ਤ ਵਾਸਤੇ ਪੁੱਛਣ ਲਈ ਆਗਿਆ ਦਿੰਦੀ ਹੈ।"</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"ਸਾਰੇ ਪੈਕੇਜਾਂ ਬਾਰੇ ਪੁੱਛਗਿੱਛ"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ਇਸ ਨਾਲ ਐਪ ਨੂੰ ਸਥਾਪਤ ਕੀਤੇ ਸਾਰੇ ਪੈਕੇਜ ਦੇਖਣ ਦੀ ਇਜਾਜ਼ਤ ਮਿਲਦੀ ਹੈ।"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"ਜ਼ੂਮ ਕੰਟਰੋਲ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ਵਿਜੇਟ ਸ਼ਾਮਲ ਨਹੀਂ ਹੋ ਸਕਿਆ।"</string>
<string name="ime_action_go" msgid="5536744546326495436">"ਜਾਓ"</string>
@@ -2152,7 +2154,7 @@
<string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"ਗੁਰੱਪ ਗੱਲਬਾਤ"</string>
<string name="unread_convo_overflow" msgid="920517615597353833">"<xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>+"</string>
<string name="resolver_personal_tab" msgid="2051260504014442073">"ਨਿੱਜੀ"</string>
- <string name="resolver_work_tab" msgid="2690019516263167035">"ਕੰਮ"</string>
+ <string name="resolver_work_tab" msgid="2690019516263167035">"ਕੰਮ ਸੰਬੰਧੀ"</string>
<string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"ਵਿਅਕਤੀਗਤ ਦ੍ਰਿਸ਼"</string>
<string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"ਕਾਰਜ ਦ੍ਰਿਸ਼"</string>
<string name="resolver_cross_profile_blocked" msgid="3014597376026044840">"ਤੁਹਾਡੇ ਆਈ.ਟੀ. ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਬਲਾਕ ਕੀਤਾ ਗਿਆ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 2781df7..eb51d8c 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -355,7 +355,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Pozwala aplikacji na rozwijanie lub zwijanie paska stanu."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"wyświetlaj powiadomienia w trybie pełnoekranowym na zablokowanym urządzeniu"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Zezwala na wyświetlanie przez aplikacje powiadomień w trybie pełnoekranowym na zablokowanym urządzeniu"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"instalowanie skrótów"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Instalowanie skrótów"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Pozwala aplikacji dodawać skróty na ekranie głównym bez interwencji użytkownika."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"odinstalowywanie skrótów"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Pozwala aplikacji usuwać skróty z ekranu głównego bez interwencji użytkownika."</string>
@@ -591,6 +591,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Użyj odcisku innego palca"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Zbyt jasno"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Popraw"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Za każdym razem lekko zmieniaj ułożenie palca"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Uwierzytelniono odciskiem palca"</string>
@@ -607,6 +608,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nie zarejestrowano odcisków palców."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"To urządzenie nie jest wyposażone w czytnik linii papilarnych."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Czujnik jest tymczasowo wyłączony."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Czujnik wymaga kalibracji"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Odcisk palca <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Używaj odcisku palca"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Używaj odcisku palca lub blokady ekranu"</string>
@@ -616,8 +618,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona odcisku palca"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Rozpoznawanie twarzy"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Zarejestruj swoją twarz ponownie"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Aby poprawić rozpoznawanie, ponownie zarejestruj swoją twarz"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Skonfiguruj rozpoznawanie twarzy"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Popatrz na ekran telefonu, aby go odblokować"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Skonfiguruj więcej sposobów odblokowywania"</string>
@@ -1025,7 +1029,7 @@
<string name="text_copied" msgid="2531420577879738860">"Tekst został skopiowany do schowka."</string>
<string name="copied" msgid="4675902854553014676">"Skopiowano"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"Aplikacja <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> wkleiła dane z aplikacji <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"Aplikacja <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> skopiowała dane ze schowka"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"Aplikacja <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> wkleiła dane ze schowka"</string>
<string name="pasted_text" msgid="4298871641549173733">"Aplikacja <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> wkleiła skopiowany tekst"</string>
<string name="pasted_image" msgid="4729097394781491022">"Aplikacja <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> wkleiła skopiowany obraz"</string>
<string name="pasted_content" msgid="646276353060777131">"Aplikacja <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> wkleiła skopiowane treści"</string>
@@ -1498,10 +1502,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Zezwala aplikacji na żądanie usunięcia pakietów."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"Prośba o ignorowanie optymalizacji wykorzystania baterii"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Zezwala aplikacji na proszenie o uprawnienia do ignorowania optymalizacji wykorzystania baterii w przypadku danej aplikacji."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"zapytanie o wszystkie pakiety"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Pozwala aplikacji wyświetlać wszystkie zainstalowane pakiety."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dotknij dwukrotnie, aby sterować powiększeniem"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nie można dodać widżetu."</string>
<string name="ime_action_go" msgid="5536744546326495436">"OK"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index a60cb23..c1237ea 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -305,7 +305,7 @@
<string name="managed_profile_label" msgid="7316778766973512382">"Perfil de trabalho"</string>
<string name="permgrouplab_contacts" msgid="4254143639307316920">"Contatos"</string>
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"acesse seus contatos"</string>
- <string name="permgrouplab_location" msgid="1858277002233964394">"Local"</string>
+ <string name="permgrouplab_location" msgid="1858277002233964394">"Localização"</string>
<string name="permgroupdesc_location" msgid="1995955142118450685">"acesse o local do dispositivo"</string>
<string name="permgrouplab_calendar" msgid="6426860926123033230">"Agenda"</string>
<string name="permgroupdesc_calendar" msgid="6762751063361489379">"acesse sua agenda"</string>
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Permite que o app expanda ou recolha a barra de status."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"exibir notificações como atividades em tela cheia em um dispositivo bloqueado"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Permite que o app exiba notificações como atividades em tela cheia em um dispositivo bloqueado"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"instalar atalhos"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Instalar atalhos"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Permite que um app adicione atalhos da tela inicial sem a intervenção do usuário."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"desinstalar atalhos"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Permite que o app remova atalhos da tela inicial sem a intervenção do usuário."</string>
@@ -438,9 +438,9 @@
<string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"acessar comandos extras do provedor de localização"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Permite que o app acesse comandos do provedor não relacionados à localização. Isso pode permitir que o app interfira no funcionamento do GPS ou de outras fontes de localização."</string>
<string name="permlab_accessFineLocation" msgid="6426318438195622966">"acessar localização precisa apenas em primeiro plano"</string>
- <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Esse app poderá acessar seu local exato por meio dos Serviços de localização enquanto estiver sendo usado. Os Serviços de localização do dispositivo precisam estar ativados para que o app possa acessar o local. Isso pode aumentar o uso da bateria."</string>
+ <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Esse app poderá acessar sua localização exata com os Serviços de localização enquanto estiver sendo usado. Os Serviços de localização do dispositivo precisam estar ativados para que o app possa acessar o local. Isso pode aumentar o uso da bateria."</string>
<string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"acessar local aproximado apenas em primeiro plano"</string>
- <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Esse app poderá acessar seu local aproximado por meio dos Serviços de localização enquanto estiver sendo usado. Os Serviços de localização do dispositivo precisam estar ativados para que o app possa acessar o local."</string>
+ <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Esse app poderá acessar sua localização aproximada com os Serviços de localização enquanto estiver sendo usado. Os Serviços de localização do dispositivo precisam estar ativados para que o app possa acessar o local."</string>
<string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"acessar a localização em segundo plano"</string>
<string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Esse app poderá acessar o local a qualquer momento, mesmo quando não estiver sendo usado."</string>
<string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"alterar as suas configurações de áudio"</string>
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Use outra impressão digital"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Claro demais"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Ajuste a posição do dedo"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Mude a posição do dedo ligeiramente a cada momento"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Impressão digital autenticada"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registrada."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor desativado temporariamente."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"O sensor precisa ser calibrado"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar impressão digital"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usar impressão digital ou bloqueio de tela"</string>
@@ -610,8 +612,8 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ícone de impressão digital"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Desbloqueio facial"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Registre seu rosto novamente"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Para melhorar o reconhecimento, registre seu rosto novamente"</string>
+ <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problema com o desbloqueio facial"</string>
+ <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toque para excluir seu modelo de rosto e crie um novo"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurar o desbloqueio facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloqueie o smartphone olhando para ele"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configure mais formas de desbloquear a tela"</string>
@@ -1458,10 +1460,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permite que um app solicite a exclusão de pacotes."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"solicitar que as otimizações de bateria sejam ignoradas"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que um app peça permissão para ignorar as otimizações de bateria para esse app."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos os pacotes"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que um app veja todos os pacotes instalados."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Toque duas vezes para ter controle do zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Não foi possível adicionar widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Ir"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 5f10462..8a46c12 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Experimente outra impressão digital"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Está demasiado claro"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Experimente ajustar"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Altere a posição do seu dedo ligeiramente de cada vez"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"A impressão digital foi autenticada."</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registada."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem sensor de impressões digitais."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporariamente desativado."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"O sensor necessita de calibração"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Utilizar a impressão digital"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Utilizar o bloqueio de ecrã ou a impressão digital"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ícone de impressão digital"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Desbloqueio facial"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Volte a inscrever o seu rosto"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Para melhorar o reconhecimento, volte a inscrever o seu rosto."</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configure o Desbloqueio facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloqueie o telemóvel ao olhar para ele"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configure mais formas de desbloquear"</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"Texto copiado para a área de transferência."</string>
<string name="copied" msgid="4675902854553014676">"Copiado"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"A app <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> colou da app <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> colou a partir da área de transferência"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"A app <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> colou a partir da área de transferência"</string>
<string name="pasted_text" msgid="4298871641549173733">"A app <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> colou o texto que copiou"</string>
<string name="pasted_image" msgid="4729097394781491022">"A app <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> colou uma imagem que copiou"</string>
<string name="pasted_content" msgid="646276353060777131">"A app <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> colou o conteúdo que copiou"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permite que uma app solicite a eliminação de pacotes."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"pedir para ignorar as otimizações da bateria"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que uma app solicite autorização para ignorar as otimizações da bateria para a mesma."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos os pacotes"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite a uma app ver todos os pacotes instalados."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tocar duas vezes para controlar o zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Não foi possível adicionar widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Ir"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index a60cb23..c1237ea 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -305,7 +305,7 @@
<string name="managed_profile_label" msgid="7316778766973512382">"Perfil de trabalho"</string>
<string name="permgrouplab_contacts" msgid="4254143639307316920">"Contatos"</string>
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"acesse seus contatos"</string>
- <string name="permgrouplab_location" msgid="1858277002233964394">"Local"</string>
+ <string name="permgrouplab_location" msgid="1858277002233964394">"Localização"</string>
<string name="permgroupdesc_location" msgid="1995955142118450685">"acesse o local do dispositivo"</string>
<string name="permgrouplab_calendar" msgid="6426860926123033230">"Agenda"</string>
<string name="permgroupdesc_calendar" msgid="6762751063361489379">"acesse sua agenda"</string>
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Permite que o app expanda ou recolha a barra de status."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"exibir notificações como atividades em tela cheia em um dispositivo bloqueado"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Permite que o app exiba notificações como atividades em tela cheia em um dispositivo bloqueado"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"instalar atalhos"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Instalar atalhos"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Permite que um app adicione atalhos da tela inicial sem a intervenção do usuário."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"desinstalar atalhos"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Permite que o app remova atalhos da tela inicial sem a intervenção do usuário."</string>
@@ -438,9 +438,9 @@
<string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"acessar comandos extras do provedor de localização"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"Permite que o app acesse comandos do provedor não relacionados à localização. Isso pode permitir que o app interfira no funcionamento do GPS ou de outras fontes de localização."</string>
<string name="permlab_accessFineLocation" msgid="6426318438195622966">"acessar localização precisa apenas em primeiro plano"</string>
- <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Esse app poderá acessar seu local exato por meio dos Serviços de localização enquanto estiver sendo usado. Os Serviços de localização do dispositivo precisam estar ativados para que o app possa acessar o local. Isso pode aumentar o uso da bateria."</string>
+ <string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Esse app poderá acessar sua localização exata com os Serviços de localização enquanto estiver sendo usado. Os Serviços de localização do dispositivo precisam estar ativados para que o app possa acessar o local. Isso pode aumentar o uso da bateria."</string>
<string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"acessar local aproximado apenas em primeiro plano"</string>
- <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Esse app poderá acessar seu local aproximado por meio dos Serviços de localização enquanto estiver sendo usado. Os Serviços de localização do dispositivo precisam estar ativados para que o app possa acessar o local."</string>
+ <string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Esse app poderá acessar sua localização aproximada com os Serviços de localização enquanto estiver sendo usado. Os Serviços de localização do dispositivo precisam estar ativados para que o app possa acessar o local."</string>
<string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"acessar a localização em segundo plano"</string>
<string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Esse app poderá acessar o local a qualquer momento, mesmo quando não estiver sendo usado."</string>
<string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"alterar as suas configurações de áudio"</string>
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Use outra impressão digital"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Claro demais"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Ajuste a posição do dedo"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Mude a posição do dedo ligeiramente a cada momento"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Impressão digital autenticada"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registrada."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor desativado temporariamente."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"O sensor precisa ser calibrado"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Dedo <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Usar impressão digital"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Usar impressão digital ou bloqueio de tela"</string>
@@ -610,8 +612,8 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ícone de impressão digital"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Desbloqueio facial"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Registre seu rosto novamente"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Para melhorar o reconhecimento, registre seu rosto novamente"</string>
+ <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problema com o desbloqueio facial"</string>
+ <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toque para excluir seu modelo de rosto e crie um novo"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurar o desbloqueio facial"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloqueie o smartphone olhando para ele"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configure mais formas de desbloquear a tela"</string>
@@ -1458,10 +1460,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permite que um app solicite a exclusão de pacotes."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"solicitar que as otimizações de bateria sejam ignoradas"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite que um app peça permissão para ignorar as otimizações de bateria para esse app."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"consultar todos os pacotes"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite que um app veja todos os pacotes instalados."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Toque duas vezes para ter controle do zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Não foi possível adicionar widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Ir"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index dc351a2..71ea137 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -352,7 +352,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Permite aplicației să extindă sau să restrângă bara de stare."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"să afișeze notificări ca activități pe ecran complet pe un dispozitiv blocat"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Permite aplicației să afișeze notificări ca activități pe ecran complet pe un dispozitiv blocat"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"instalează comenzi rapide"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Instalarea de comenzi rapide"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Permite unei aplicații să adauge comenzi rapide pe ecranul de pornire, fără intervenția utilizatorului."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"dezinstalează comenzi rapide"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Permite aplicației să elimine comenzi rapide de pe ecranul de pornire, fără intervenția utilizatorului."</string>
@@ -588,6 +588,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Încercați altă amprentă"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Prea luminos"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Încercați să ajustați"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Schimbați ușor poziția degetului de fiecare dată"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Amprentă autentificată"</string>
@@ -604,6 +605,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nu au fost înregistrate amprente."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dispozitivul nu are senzor de amprentă."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzorul este dezactivat temporar."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Senzorul necesită calibrare"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Degetul <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Folosiți amprenta"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Folosiți amprenta sau blocarea ecranului"</string>
@@ -613,8 +615,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Pictograma amprentă"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Deblocare facială"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Reînregistrați-vă chipul"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Pentru a îmbunătăți recunoașterea, reînregistrați-vă chipul"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Configurați Deblocarea facială"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Deblocați-vă telefonul uitându-vă la acesta"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurați mai multe moduri de deblocare"</string>
@@ -1478,10 +1482,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Permite unei aplicații să solicite ștergerea pachetelor."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"să solicite ignorarea optimizărilor bateriei"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite unei aplicații să solicite permisiunea de a ignora optimizările bateriei pentru aplicația respectivă."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"să interogheze toate pachetele"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite unei aplicații să vadă toate pachetele instalate."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Apăsați de două ori pentru a controla mărirea/micșorarea"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nu s-a putut adăuga widgetul."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Accesați"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index ea9e292..c83c2e4 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -447,7 +447,7 @@
<string name="permdesc_accessFineLocation" msgid="6732174080240016335">"Приложение сможет получать сведения о вашем точном местоположении, только когда используется. Для этого на устройстве должна быть включена геолокация. Учтите, что при этом заряд батареи может расходоваться быстрее."</string>
<string name="permlab_accessCoarseLocation" msgid="1561042925407799741">"Доступ к приблизительному местоположению только в активном режиме"</string>
<string name="permdesc_accessCoarseLocation" msgid="778521847873199160">"Приложение сможет получать сведения о вашем приблизительном местоположении, только когда используется. Для этого на устройстве должна быть включена геолокация."</string>
- <string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"доступ к геоданным в фоновом режиме"</string>
+ <string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"Доступ к геоданным в фоновом режиме"</string>
<string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Приложение сможет получать доступ к сведениям о местоположении, даже когда не используется."</string>
<string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"Изменение настроек аудио"</string>
<string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Приложение сможет изменять системные настройки звука, например уровень громкости и активный динамик."</string>
@@ -457,7 +457,7 @@
<string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Приложение может в любое время записывать аудио с помощью микрофона."</string>
<string name="permlab_sim_communication" msgid="176788115994050692">"Отправка команд SIM-карте"</string>
<string name="permdesc_sim_communication" msgid="4179799296415957960">"Приложение сможет отправлять команды SIM-карте (данное разрешение представляет большую угрозу)."</string>
- <string name="permlab_activityRecognition" msgid="1782303296053990884">"распознавать физическую активность"</string>
+ <string name="permlab_activityRecognition" msgid="1782303296053990884">"Распознавать физическую активность"</string>
<string name="permdesc_activityRecognition" msgid="8667484762991357519">"Приложение может распознавать физическую активность."</string>
<string name="permlab_camera" msgid="6320282492904119413">"Фото- и видеосъемка"</string>
<string name="permdesc_camera" msgid="5240801376168647151">"Когда приложение используется, оно может делать фотографии и снимать видео с помощью камеры."</string>
@@ -512,8 +512,8 @@
<string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"Приложение сможет получить список всех используемых на устройстве аккаунтов, в том числе созданных установленными приложениями."</string>
<string name="permlab_accessNetworkState" msgid="2349126720783633918">"Просмотр сетевых подключений"</string>
<string name="permdesc_accessNetworkState" msgid="4394564702881662849">"Приложение сможет просматривать информацию о сетевых подключениях, например о том, какие сети доступны и к каким из них вы подключены."</string>
- <string name="permlab_createNetworkSockets" msgid="3224420491603590541">"Неограниченный доступ к Интернету"</string>
- <string name="permdesc_createNetworkSockets" msgid="7722020828749535988">"Приложение сможет создавать сетевые сокеты и использовать различные сетевые протоколы. Так как браузер и другие приложения обеспечивают средства для отправки данных в Интернет, это разрешение предоставлять не обязательно."</string>
+ <string name="permlab_createNetworkSockets" msgid="3224420491603590541">"Неограниченный доступ к интернету"</string>
+ <string name="permdesc_createNetworkSockets" msgid="7722020828749535988">"Приложение сможет создавать сетевые сокеты и использовать различные сетевые протоколы. Так как браузер и другие приложения обеспечивают средства для отправки данных в интернет, это разрешение предоставлять не обязательно."</string>
<string name="permlab_changeNetworkState" msgid="8945711637530425586">"Изменение сетевых настроек"</string>
<string name="permdesc_changeNetworkState" msgid="649341947816898736">"Приложение сможет изменять состояние подключения к сети."</string>
<string name="permlab_changeTetherState" msgid="9079611809931863861">"Изменение подключения к компьютеру"</string>
@@ -540,9 +540,9 @@
<string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Приложение сможет просматривать конфигурацию Bluetooth на планшетном ПК, а также запрашивать и подтверждать соединение с другими устройствами."</string>
<string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Приложение сможет просматривать конфигурацию Bluetooth на устройстве Android TV, а также запрашивать и подтверждать соединение с другими устройствами."</string>
<string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Приложение сможет просматривать конфигурацию Bluetooth на телефоне, а также запрашивать и подтверждать соединение с другими устройствами."</string>
- <string name="permlab_bluetooth_scan" msgid="5402587142833124594">"находить устройства Bluetooth поблизости и подключаться к ним"</string>
+ <string name="permlab_bluetooth_scan" msgid="5402587142833124594">"Находить устройства Bluetooth и подключаться к ним"</string>
<string name="permdesc_bluetooth_scan" product="default" msgid="6540723536925289276">"Приложение сможет находить устройства Bluetooth поблизости и подключаться к ним."</string>
- <string name="permlab_bluetooth_connect" msgid="6657463246355003528">"доступ к подключенным устройствам Bluetooth"</string>
+ <string name="permlab_bluetooth_connect" msgid="6657463246355003528">"Доступ к подключенным устройствам Bluetooth"</string>
<string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"У приложения будет доступ к подключенным устройствам Bluetooth."</string>
<string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"Передача рекламы на устройства Bluetooth рядом"</string>
<string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Приложение сможет передавать рекламу на устройства Bluetooth поблизости."</string>
@@ -568,7 +568,7 @@
<string name="permdesc_videoWrite" msgid="6124731210613317051">"Приложение сможет вносить изменения в вашу видеоколлекцию."</string>
<string name="permlab_imagesWrite" msgid="1774555086984985578">"изменение фотоколлекции"</string>
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Приложение сможет вносить изменения в вашу фотоколлекцию."</string>
- <string name="permlab_mediaLocation" msgid="7368098373378598066">"доступ к геоданным в медиаколлекции"</string>
+ <string name="permlab_mediaLocation" msgid="7368098373378598066">"Доступ к геоданным в медиаколлекции"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Приложение получит доступ к геоданным в вашей медиаколлекции."</string>
<string name="biometric_app_setting_name" msgid="3339209978734534457">"Использовать биометрию"</string>
<string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Использовать биометрию или блокировку экрана"</string>
@@ -591,6 +591,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Попробуйте сохранить отпечаток другого пальца."</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Слишком светло."</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Попробуйте изменить положение пальца."</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Каждый раз немного меняйте положение пальца."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечаток пальца проверен"</string>
@@ -607,6 +608,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Нет отсканированных отпечатков пальцев"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На этом устройстве нет сканера отпечатков пальцев."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сканер отпечатков пальцев временно отключен."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Требуется калибровка датчика."</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Отпечаток <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Использовать отпечаток пальца"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Использовать отпечаток пальца или блокировку экрана"</string>
@@ -616,8 +618,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Значок отпечатка пальца"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Фейсконтроль"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Зарегистрируйте лицо ещё раз"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Чтобы улучшить распознавание лица, зарегистрируйте его ещё раз"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Настройка фейсконтроля"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Вы сможете разблокировать телефон, просто посмотрев на него."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Настройте другие способы разблокировки"</string>
@@ -1498,10 +1502,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Приложение сможет запрашивать разрешения на удаление пакетов."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"Без ограничения расхода батареи"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Разрешает приложению игнорировать ограничение на расход заряда батареи."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"Запрос информации обо всех пакетах"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Приложение сможет просматривать все установленные пакеты."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Нажмите дважды для изменения масштаба"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Не удалось добавить виджет."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Выбрать"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 6064103..72848f1 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"තවත් ඇඟිලි සලකුණක් උත්සාහ කරන්න"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"දීප්තිය වැඩියි"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"සීරුමාරු කිරීම උත්සාහ කරන්න"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"එක් එක් අවස්ථාවේ ඔබගේ ඇඟිල්ලේ පිහිටීම මදක් වෙනස් කරන්න"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ඇඟිලි සලකුණ සත්යාපනය කරන ලදී"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ඇඟිලි සලකුණු ඇතුළත් කර නොමැත."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"මෙම උපාංගයේ ඇඟිලි සලකුණු සංවේදකයක් නොමැත."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"සංවේදකය තාවකාලිකව අබල කර ඇත."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"සංවේදකයට ක්රමාංකනය අවශ්යයි"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"ඇඟිලි <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ඇඟිලි සලකුණ භාවිත කරන්න"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ඇඟිලි සලකුණ හෝ තිර අගුල භාවිත කරන්න"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ඇඟිලි සලකුණු නිරූපකය"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"මුහුණෙන් අගුළු ඇරීම"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"ඔබේ මුහුණ යළි ලියාපදිංචි කරන්න"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"හඳුනා ගැනීම වැඩිදියුණු කිරීමට, ඔබේ මුහුණ යළි-ලියාපදිංචි කරන්න"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"මුහුණෙන් අගුළු ඇරීම පිහිටුවන්න"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ඔබගේ දුරකථනය දෙස බැලීමෙන් එහි අගුලු හරින්න"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"අගුලු හැරීමට තවත් ක්රම සකසන්න"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ස්ථාපන පැකේජ මැකීමට යෙදුමකට ඉඩ දීම."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"බැටරි ප්රශස්තකරණ නොසලකා හැරීමට ඉල්ලන්න"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"යෙදුමකට එම යෙදුම සඳහා බැටරි ප්රශස්තකරණ නොසලකා හැරීමට අවසර ඉල්ලීමට ඉඩ දෙයි."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"සියලු පැකේජ විමසන්න"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ස්ථාපනය කර ඇති සියලු පැකේජ බැලීමට යෙදුමකට ඉඩ දෙයි."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"විශාලන පාලක සඳහා දෙවතාවක් තට්ටු කරන්න"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"විජටය එකතු කිරීමට නොහැකි විය."</string>
<string name="ime_action_go" msgid="5536744546326495436">"යන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index e42cc01..534ce4d 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -591,6 +591,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Vyskúšajte iný odtlačok prsta"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Príliš jasno"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Vyskúšajte upraviť"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Zakaždým trocha zmeňte pozíciu prsta"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Odtlačok prsta bol overený"</string>
@@ -607,6 +608,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Neregistrovali ste žiadne odtlačky prstov."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zariadenie nemá senzor odtlačkov prstov."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je dočasne vypnutý."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Senzor vyžaduje kalibráciu"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst: <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Použiť odtlačok prsta"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Použiť odtlačok prsta alebo zámku obrazovky"</string>
@@ -616,8 +618,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona odtlačku prsta"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Odomknutie tvárou"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Znova zaregistrujte svoju tvár"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Znova zaregistrujte svoju tvár, aby sa zlepšilo rozpoznávanie"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Nastavte odomknutie tvárou"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Odomykajte telefón tak, že sa naň pozriete"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Nastavte viac spôsobov odomknutia"</string>
@@ -1025,7 +1029,7 @@
<string name="text_copied" msgid="2531420577879738860">"Text bol skopírovaný do schránky."</string>
<string name="copied" msgid="4675902854553014676">"Skopírované"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"Aplikácia <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> prilepila údaje z aplikácie <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"Aplikácia <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> bola prilepená zo schránky"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"Aplikácia <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> vložila obsah zo schránky"</string>
<string name="pasted_text" msgid="4298871641549173733">"Aplikácia <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> prilepila text, ktorý ste skopírovali"</string>
<string name="pasted_image" msgid="4729097394781491022">"Aplik. <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> prilepila obrázok, ktorý ste skopírovali"</string>
<string name="pasted_content" msgid="646276353060777131">"Aplikácia <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> prilepila obsah, ktorý ste skopírovali"</string>
@@ -1498,10 +1502,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Umožňuje aplikácii vyžiadať odstránenie balíkov."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"požiadať o ignorovanie optimalizácií výdrže batérie"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Umožňuje aplikácii požiadať o povolenie ignorovať optimalizácie výdrže batérie pre danú aplikáciu."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"dopytovať všetky balíky"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Povoľuje aplikácii čítať všetky nainštalované balíky."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Dvojitým klepnutím môžete ovládať priblíženie"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Miniaplikáciu sa nepodarilo pridať."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Hľadať"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 506c066..6d1deb1 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -591,6 +591,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Poskusite z drugim prstnim odtisom."</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Presvetlo je."</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Poskusite popraviti položaj prsta."</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Vsakič nekoliko spremenite položaj prsta."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Pristnost prstnega odtisa je preverjena"</string>
@@ -607,6 +608,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ni registriranih prstnih odtisov."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ta naprava nima tipala prstnih odtisov."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Tipalo je začasno onemogočeno."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Tipalo je treba umeriti"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Prst <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Uporaba prstnega odtisa"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Uporaba prstnega odtisa ali odklepanja s poverilnico"</string>
@@ -616,8 +618,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona prstnih odtisov"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Odklepanje z obrazom"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Znova registrirajte obraz"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Za izboljšanje prepoznavanja znova registrirajte svoj obraz"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Nastavitev odklepanja z obrazom"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Odklenite telefon tako, da ga pogledate."</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Nastavite več načinov odklepanja"</string>
@@ -1498,10 +1502,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Omogoča aplikaciji, da zahteva brisanje paketov."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"Dovoljenje za prezrtje optimizacij baterije"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Aplikaciji dovoljuje, da vpraša za dovoljenje, ali naj prezre optimizacije baterije."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"poizvedovanje po vseh paketih"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Aplikaciji dovoli, da vidi vse nameščene pakete."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tapnite dvakrat za nadzor povečave/pomanjšave"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Pripomočka ni bilo mogoče dodati."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Pojdi"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index ed6a616..ca175bb 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Lejon aplikacionin të zgjerojë ose shpalosë shiritin e statusit."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"shfaq njoftimet si aktivitete në ekran të plotë në një pajisje të kyçur"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Lejon që aplikacioni t\'i shfaqë njoftimet si aktivitete në ekran të plotë në një pajisje të kyçur"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"instalo shkurtore"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"instalimi i shkurtoreve"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Lejon një aplikacion për të shtuar shkurtore në ekranin bazë pa ndërhyrjen e përdoruesit."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"çinstalo shkurtore"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Lejon aplikacionin të heqë shkurtore në ekranin bazë, pa ndërhyrjen e përdoruesit."</string>
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Provo një gjurmë gishti tjetër"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Me shumë ndriçim"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Provo ta rregullosh"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Ndrysho pak pozicionin e gishtit çdo herë"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Gjurma e gishtit u vërtetua"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nuk ka asnjë gjurmë gishti të regjistruar."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kjo pajisje nuk ka sensor të gjurmës së gishtit."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensori është çaktivizuar përkohësisht."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Sensori ka nevojë për kalibrim"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Gishti <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Përdor gjurmën e gishtit"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Përdor gjurmën e gishtit ose kyçjen e ekranit"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona e gjurmës së gishtit"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Shkyçja me fytyrë"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Regjistro përsëri fytyrën tënde"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Për të përmirësuar njohjen, regjistro përsëri fytyrën tënde"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfiguro \"Shkyçjen me fytyrë\""</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Shkyçe telefonin duke parë tek ai"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfiguri më shumë mënyra për të shkyçur"</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"Teksti u kopjua në kujtesën e fragmenteve."</string>
<string name="copied" msgid="4675902854553014676">"U kopjua"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> u ngjit nga <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ngjitur nga kujtesa jote e fragmenteve"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ngjiti përmbajtje nga kujtesa jote e fragmenteve"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ngjiti një tekst që kopjove"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ngjiti një imazh që kopjove"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ngjiti një përmbajtje që kopjove"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Lejon që një aplikacion të kërkojë fshirjen e paketave."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"kërko të shpërfillësh optimizimet e baterisë"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Lejon që një aplikacion të kërkojë leje për të shpërfillur optimizimet e baterisë për atë aplikacion."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"kërko të gjitha paketat"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Lejon një aplikacion të shikojë të gjitha paketat e instaluara."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Trokit dy herë për të kontrolluar zmadhimin"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nuk mundi të shtonte miniaplikacion."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Shko"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 14f5673..2b356be 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -352,7 +352,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Дозвољава апликацији да проширује или скупља статусну траку."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"приказује обавештења као активности преко целог екрана на закључаном уређају"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Омогућава апликацији да на закључаном уређају приказује обавештења као активности преко целог екрана."</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"инсталирање пречица"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Инсталирање пречица"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Омогућава апликацији да додаје пречице на почетни екран без интервенције корисника."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"деинсталирање пречица"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Омогућава апликацији да уклања пречице са почетног екрана без интервенције корисника."</string>
@@ -365,11 +365,11 @@
<string name="permlab_receiveMms" msgid="4000650116674380275">"пријем текстуалних порука (MMS)"</string>
<string name="permdesc_receiveMms" msgid="958102423732219710">"Дозвољава апликацији да прима и обрађује MMS поруке. То значи да апликација може да надгледа или брише поруке које се шаљу уређају, а да вам их не прикаже."</string>
<string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"Прослеђивање порука за мобилне уређаје на локалитету"</string>
- <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Дозвољава апликацији да се везује за модул порука за мобилне уређаје на локалитету да би прослеђивала поруке за мобилне уређаје на локалитету онако како су примљене. Обавештења порука за мобилне уређаје на локалитету се на неким локацијама примају као упозорења на хитне случајеве. Злонамерне апликације могу да утичу на учинак или ометају рад уређаја када се прими порука о хитном случају за мобилне уређаје на локалитету."</string>
+ <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Дозвољава апликацији да се везује за модул порука за мобилне уређаје на локалитету да би прослеђивала поруке за мобилне уређаје на локалитету онако како су примљене. Обавештења порука за мобилне уређаје на локалитету се на неким локацијама примају као упозорења на хитне случајеве. Злонамерне апликације могу да утичу на перформансе или ометају рад уређаја када се прими порука о хитном случају за мобилне уређаје на локалитету."</string>
<string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Управљање одлазним позивима"</string>
<string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Омогућава апликацији да види детаље о одлазним позивима на уређају и да контролише те позиве."</string>
<string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"читање порука инфо сервиса"</string>
- <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Омогућава апликацији да чита поруке инфо сервиса које уређај прима. Упозорења инфо сервиса се на неким локацијама примају као упозорења на хитне случајеве. Злонамерне апликације могу да утичу на учинак или ометају функционисање уређаја када се прими порука инфо сервиса о хитном случају."</string>
+ <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Омогућава апликацији да чита поруке инфо сервиса које уређај прима. Упозорења инфо сервиса се на неким локацијама примају као упозорења на хитне случајеве. Злонамерне апликације могу да утичу на перформансе или ометају функционисање уређаја када се прими порука инфо сервиса о хитном случају."</string>
<string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"читање пријављених фидова"</string>
<string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"Дозвољава апликацији да преузима детаље о тренутно синхронизованим фидовима."</string>
<string name="permlab_sendSms" msgid="7757368721742014252">"шаље и прегледа SMS поруке"</string>
@@ -588,6 +588,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Пробајте са другим отиском прста"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Превише је светло"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Пробајте да прилагодите"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Сваки пут лагано промените положај прста"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Отисак прста је потврђен"</string>
@@ -604,6 +605,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Није регистрован ниједан отисак прста."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Овај уређај нема сензор за отисак прста."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензор је привремено онемогућен."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Сензор треба да се калибрише"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Прст <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Користите отисак прста"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Користите отисак прста или закључавање екрана"</string>
@@ -613,8 +615,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Икона отиска прста"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Откључавање лицем"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Поново региструјте лице"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Да бисте побољшали препознавање, поново региструјте лице"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Подесите откључавање лицем"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Откључајте телефон тако што ћете га погледати"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Подесите још начина за откључавање"</string>
@@ -1022,7 +1026,7 @@
<string name="text_copied" msgid="2531420577879738860">"Текст је копиран у привремену меморију."</string>
<string name="copied" msgid="4675902854553014676">"Копирано је"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"Апликација<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> је налепила податке из апликације <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"Садржај апликације <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> је налепљен у привр. меморију"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> је прелепио/ла из привремене меморије"</string>
<string name="pasted_text" msgid="4298871641549173733">"Апликација<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> је налепила текст који сте копирали"</string>
<string name="pasted_image" msgid="4729097394781491022">"Апликација<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> је налепила слику коју сте копирали"</string>
<string name="pasted_content" msgid="646276353060777131">"Апликација<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> је налепила садржај који сте копирали"</string>
@@ -1283,7 +1287,7 @@
<string name="heavy_weight_notification" msgid="8382784283600329576">"Апликација <xliff:g id="APP">%1$s</xliff:g> је покренута"</string>
<string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Додирните да бисте се вратили у игру"</string>
<string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Одаберите игру"</string>
- <string name="heavy_weight_switcher_text" msgid="6814316627367160126">"Да би учинак био бољи, можете да отворите само једну од ових игара одједном."</string>
+ <string name="heavy_weight_switcher_text" msgid="6814316627367160126">"Да би перформансе биле боље, може да буде отворена само једна од ових игара."</string>
<string name="old_app_action" msgid="725331621042848590">"Назад на <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
<string name="new_app_action" msgid="547772182913269801">"Отвори <xliff:g id="NEW_APP">%1$s</xliff:g>"</string>
<string name="new_app_description" msgid="1958903080400806644">"<xliff:g id="OLD_APP">%1$s</xliff:g> ће се затворити без чувања"</string>
@@ -1395,7 +1399,7 @@
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Омогућен је режим пробног коришћења"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Обавите ресетовање на фабричка подешавања да бисте онемогућили режим пробног коришћења."</string>
<string name="console_running_notification_title" msgid="6087888939261635904">"Серијска конзола је омогућена"</string>
- <string name="console_running_notification_message" msgid="7892751888125174039">"Учинак је смањен. Да бисте онемогући конзолу, проверите покретачки програм."</string>
+ <string name="console_running_notification_message" msgid="7892751888125174039">"Перформансе су смањене. Да бисте онемогући конзолу, проверите покретачки програм."</string>
<string name="usb_contaminant_detected_title" msgid="4359048603069159678">"Течност или нечистоћа у USB порту"</string>
<string name="usb_contaminant_detected_message" msgid="7346100585390795743">"USB порт је аутоматски искључен. Додирните да бисте сазнали више."</string>
<string name="usb_contaminant_not_detected_title" msgid="2651167729563264053">"Коришћење USB порта је дозвољено"</string>
@@ -1478,10 +1482,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Омогућава да апликација захтева брисање пакета."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"тражење дозволе за игнорисање оптимизација батерије"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Дозвољава апликацији да тражи дозволу за игнорисање оптимизација батерије за ту апликацију."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"слање упита за све пакете"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Дозвољава апликацији да види све инсталиране пакете."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Додирните двапут за контролу зумирања"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Није могуће додати виџет."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Иди"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index f5e1f81..332075f 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Testa ett annat fingeravtryck"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Det är för ljust"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Testa att justera fingeravtrycket"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Flytta fingret lite varje gång"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeravtrycket har autentiserats"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Inga fingeravtryck har registrerats."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Enheten har ingen fingeravtryckssensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensorn har tillfälligt inaktiverats."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Sensorn måste kalibreras"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Finger <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Använd ditt fingeravtryck"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Använd ditt fingeravtryck eller skärmlåset"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon för fingeravtryck"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Ansiktslås"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Registrera ansiktet på nytt"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Gör om registreringen av ansiktet så att ansiktsigenkänningen ska fungera bättre"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Konfigurera ansiktslås"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Lås upp telefonen genom att titta på den"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfigurera fler sätt att låsa upp"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Tillåter att en app begär paketborttagning."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"får be om tillstånd att ignorera batterioptimering"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Appen får be om tillstånd att ignorera batterioptimering."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"fråga alla paket"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Tillåter att en app ser alla installerade paket."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Peka två gånger för zoomkontroll"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Det gick inte att lägga till widgeten."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Kör"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index c05f866..7119aa7 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Jaribu alama nyingine ya kidole"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Inang\'aa mno"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Jaribu kurekebisha"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Badilisha mkao wa kidole chako kiasi kila wakati"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Imethibitisha alama ya kidole"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Hakuna alama za vidole zilizojumuishwa."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kifaa hiki hakina kitambua alama ya kidole."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Kitambuzi kimezimwa kwa muda."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Kitambuzi kinahitaji kurekebishwa"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Kidole cha <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Tumia alama ya kidole"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Tumia alama ya kidole au mbinu ya kufunga skrini"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Aikoni ya alama ya kidole"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Kufungua kwa uso"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Sajili uso wako tena"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Ili kuimarisha utambuzi, tafadhali sajili uso wako tena"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Weka mipangilio ya Kufungua kwa uso"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Fungua simu yako kwa kuiangalia"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Weka mipangilio ya mbinu zaidi za kufungua"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Huruhusu programu kuomba idhini ya kufuta vifurushi."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"omba kupuuza uimarishji wa betri"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Huruhusu programu kuomba ruhusa ya kupuuza uimarishaji wa betri katika programu yako."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"kutuma hoja kwa vifurushi vyote"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Huruhusu programu kuona vifurushi vyote vilivyosakinishwa."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Gusa mara mbili kwa udhibiti wa kuza"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Haikuweza kuongeza wijeti."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Nenda"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 9be38c9..1d40c02 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"வேறு கைரேகையை முயலவும்"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"அதிக வெளிச்சமாக உள்ளது"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"விரலைச் சரியாக வைக்கவும்"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ஒவ்வொரு முறையும் விரலின் நிலையைச் சிறிதளவு மாற்றுங்கள்"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"கைரேகை அங்கீகரிக்கப்பட்டது"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"கைரேகைப் பதிவுகள் எதுவும் இல்லை."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"இந்தச் சாதனத்தில் கைரேகை சென்சார் இல்லை."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"சென்சார் தற்காலிகமாக முடக்கப்பட்டுள்ளது."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"சென்சாரைச் சீரமைக்க வேண்டும்"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"கைரேகை <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"கைரேகையைப் பயன்படுத்து"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"கைரேகையையோ திரைப் பூட்டையோ பயன்படுத்து"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"கைரேகை ஐகான்"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"முகம் காட்டித் திறத்தல்"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"முகத்தை மீண்டும் பதிவுசெய்யவும்"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"அடையாளத்தை மேம்படுத்த முகத்தை மீண்டும் பதிவுசெய்யவும்"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"\'முகம் காட்டித் திறத்தல்\' அம்சத்தை அமைத்தல்"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"மொபைலைப் பார்ப்பதன் மூலம் அதைத் திறக்கலாம்"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"திறக்க, மேலும் பல வழிகளை அமையுங்கள்"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"தொகுப்புகளை நீக்க கோர, ஆப்ஸை அனுமதிக்கும்."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"பேட்டரி மேம்படுத்தல்களைப் புறக்கணிப்பதற்கான அனுமதியைக் கோரு"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"பயன்பாட்டிற்கான பேட்டரி மேம்படுத்தல்களைப் புறக்கணிப்பதற்கான அனுமதியைக் கோர, ஆப்ஸை அனுமதிக்கும்."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"அனைத்துப் பேக்கேஜ்களையும் பார்க்க அனுமதித்தல்"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"நிறுவப்பட்டுள்ள அனைத்துப் பேக்கேஜ்களையும் பார்ப்பதற்கு ஆப்ஸை அனுமதிக்கும்."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"அளவை மாற்றுவதற்கான கட்டுப்பாட்டிற்கு, இருமுறை தட்டவும்"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"விட்ஜெட்டைச் சேர்க்க முடியவில்லை."</string>
<string name="ime_action_go" msgid="5536744546326495436">"செல்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index d910224..5efa33c 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"మరొక వేలిముద్రను ట్రై చేయండి"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"వెలుతురు అధికంగా ఉంది"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"సర్దుబాటు చేయడానికి ట్రై చేయండి"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ప్రతిసారీ మీ వేళ్ల స్థానాన్ని కొద్దిగా మార్చండి"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"వేలిముద్ర ప్రమాణీకరించబడింది"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"వేలిముద్రలు నమోదు చేయబడలేదు."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ఈ పరికరంలో వేలిముద్ర సెన్సార్ ఎంపిక లేదు."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"సెన్సార్ తాత్కాలికంగా డిజేబుల్ చేయబడింది."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"సెన్సార్కు కాలిబ్రేషన్ అవసరం"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"వేలు <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"వేలిముద్రను ఉపయోగించండి"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"వేలిముద్ర లేదా స్క్రీన్ లాక్ను ఉపయోగించండి"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"వేలిముద్ర చిహ్నం"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ఫేస్ అన్లాక్"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"మీ ముఖాన్ని తిరిగి నమోదు చేయండి"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"గుర్తింపును మెరుగుపరచడానికి, దయచేసి మీ ముఖంను తిరిగి నమోదు చేసుకోండి"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"ఫేస్ అన్లాక్ను సెటప్ చేయండి"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"మీ ఫోన్ను చూడటం ద్వారా దాన్ని అన్లాక్ చేయండి"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"అన్లాక్ చేయడానికి మరిన్ని మార్గాలను సెటప్ చేయండి"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ప్యాకేజీల తొలగింపును అభ్యర్థించడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"బ్యాటరీ అనుకూలీకరణలను విస్మరించడానికి అడగాలి"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"ఆ యాప్ కోసం బ్యాటరీ అనుకూలీకరణలు విస్మరించేలా అనుమతి కోరడానికి యాప్ను అనుమతిస్తుంది."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"అన్ని ప్యాకేజీలను క్వెరీ చేయండి"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ఇన్స్టాల్ చేసిన అన్ని ప్యాకేజీలను చూడటానికి యాప్ను అనుమతించండి."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"జూమ్ నియంత్రణ కోసం రెండుసార్లు నొక్కండి"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"విడ్జెట్ను జోడించడం సాధ్యపడలేదు."</string>
<string name="ime_action_go" msgid="5536744546326495436">"వెళ్లు"</string>
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 3ecb1dd..55e5685 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -42,4 +42,8 @@
<!-- Allow SystemUI to show the shutdown dialog -->
<bool name="config_showSysuiShutdown">true</bool>
+
+ <!-- Component name of the activity used to inform a user about a sensory being blocked because
+ of privacy settings. -->
+ <string name="config_sensorUseStartedActivity">com.android.systemui/com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity</string>
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index b6c6ed5c2..742e838 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"ลองลายนิ้วมืออื่น"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"สว่างเกินไป"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ลองปรับการวางนิ้ว"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"เปลี่ยนตำแหน่งของนิ้วเล็กน้อยไปเรื่อยๆ ทุกครั้ง"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"ตรวจสอบสิทธิ์ลายนิ้วมือแล้ว"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ไม่มีลายนิ้วมือที่ลงทะเบียน"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"อุปกรณ์นี้ไม่มีเซ็นเซอร์ลายนิ้วมือ"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ปิดใช้เซ็นเซอร์ชั่วคราวแล้ว"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"ต้องปรับเทียบเซ็นเซอร์"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"นิ้ว <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"ใช้ลายนิ้วมือ"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"ใช้ลายนิ้วมือหรือการล็อกหน้าจอ"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ไอคอนลายนิ้วมือ"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"การปลดล็อกด้วยใบหน้า"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"ลงทะเบียนใบหน้าอีกครั้ง"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"โปรดลงทะเบียนใบหน้าอีกครั้งเพื่อปรับปรุงการจดจำ"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"ตั้งค่าการปลดล็อกด้วยใบหน้า"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"ปลดล็อกโทรศัพท์โดยมองไปที่โทรศัพท์"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ตั้งค่าการปลดล็อกด้วยวิธีอื่น"</string>
@@ -658,7 +662,7 @@
<string-array name="face_error_vendor">
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"ไอคอนใบหน้า"</string>
- <string name="permlab_readSyncSettings" msgid="6250532864893156277">"อ่านการตั้งค่าการซิงค์แล้ว"</string>
+ <string name="permlab_readSyncSettings" msgid="6250532864893156277">"อ่านการตั้งค่าการซิงค์"</string>
<string name="permdesc_readSyncSettings" msgid="1325658466358779298">"อนุญาตให้แอปพลิเคชันอ่านการตั้งค่าการซิงค์ของบัญชี ตัวอย่างเช่น การอนุญาตนี้สามารถระบุได้ว่าแอปพลิเคชัน People ซิงค์กับบัญชีหรือไม่"</string>
<string name="permlab_writeSyncSettings" msgid="6583154300780427399">"สลับระหว่างเปิดและปิดการซิงค์"</string>
<string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"อนุญาตให้แอปพลิเคชันเปลี่ยนแปลงการตั้งค่าการซิงค์ของบัญชี ตัวอย่างเช่น สามารถใช้การอนุญาตเปิดใช้งานการซิงค์แอปพลิเคชัน People กับบัญชี"</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"คัดลอกข้อความไปยังคลิปบอร์ด"</string>
<string name="copied" msgid="4675902854553014676">"คัดลอกแล้ว"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"วาง <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> จาก <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> แล้ว"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"วาง <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> จากคลิปบอร์ดแล้ว"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> ได้วางข้อมูลจากคลิปบอร์ดแล้ว"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> วางข้อความที่คุณคัดลอก"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> วางรูปภาพที่คุณคัดลอก"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> วางเนื้อหาที่คุณคัดลอก"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"อนุญาตให้แอปพลิเคชันขอการลบแพ็กเกจ"</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"ขอเพิกเฉยต่อการเพิ่มประสิทธิภาพแบตเตอรี่"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"อนุญาตให้แอปขอสิทธิ์เพิกเฉยต่อการเพิ่มประสิทธิภาพแบตเตอรี่สำหรับแอปนั้น"</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"ค้นหาแพ็กเกจทั้งหมด"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"อนุญาตให้แอปดูแพ็กเกจที่ติดตั้งไว้ทั้งหมด"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"แตะสองครั้งเพื่อควบคุมการซูม"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ไม่สามารถเพิ่มวิดเจ็ต"</string>
<string name="ime_action_go" msgid="5536744546326495436">"ไป"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 4ea1da9..c39ecf0 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Sumubok ng ibang fingerprint"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Masyadong maliwanag"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Subukang isaayos"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Bahagyang baguhin ang posisyon ng iyong daliri sa bawat pagkakataon"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Na-authenticate ang fingerprint"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Walang naka-enroll na fingerprint."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Walang sensor ng fingerprint ang device na ito."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Pansamantalang na-disable ang sensor."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Kailangang i-calibrate ang sensor"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Daliri <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Gumamit ng fingerprint"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Gumamit ng fingerprint o lock ng screen"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icon ng fingerprint"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Pag-unlock Gamit ang Mukha"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"I-enroll ulit ang iyong mukha"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Para mapahusay ang pagkilala, paki-enroll ulit ang iyong mukha"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"I-set up ang Pag-unlock Gamit ang Mukha"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"I-unlock ang iyong telepono sa pamamagitan ng pagtingin dito"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Mag-set up ng higit pang paraan para mag-unlock"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Pinapayagan ang isang application na humiling ng pag-delete ng mga package."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"hilingin na balewalain ang mga pag-optimize ng baterya"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Pinapayagang humingi ng pahintulot ang isang app na balewalain ang mga pag-optimize ng baterya para sa app na iyon."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"i-query ang lahat ng package"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Nagbibigay-daan sa isang app na makita ang lahat ng naka-install na package."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Tapikin ng dalawang beses para sa pagkontrol ng zoom"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Hindi maidagdag ang widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Pumunta"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 306fc82..b74e7f0 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Başka bir parmak izi deneyin"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Çok parlak"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Ayarlamayı deneyin"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Her defasında parmağınızın konumunu biraz değiştirin"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Parmak izi kimlik doğrulaması yapıldı"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Parmak izi kaydedilmedi."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda parmak izi sensörü yok."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensör geçici olarak devre dışı bırakıldı."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Sensörün kalibre edilmesi gerekiyor"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"<xliff:g id="FINGERID">%d</xliff:g>. parmak"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Parmak izi kullan"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Parmak izi veya ekran kilidi kullan"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Parmak izi simgesi"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Yüz Tanıma Kilidi"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Yüzünüzü yeniden kaydedin"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Daha iyi tanınmasını sağlamak için lütfen yüzünüzü yeniden kaydedin"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Yüz Tanıma Kilidi\'ni kurma"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefonunuza bakarak kilidini açın"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Kilidi açmak için daha fazla yöntem ayarlayın"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Uygulamaya, paketleri silme isteğinde bulunma izni verir."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"pil optimizasyonlarını göz ardı etme izni iste"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Bir uygulamanın, kendisi için pil optimizasyonlarını göz ardı etme izni istemesine olanak sağlar."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"tüm paketleri sorgulama"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Uygulamaya tüm yüklü paketleri görme izni verir."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Zum denetimi için iki kez dokun"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Widget eklenemedi."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Git"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index baf4a44..3d51bda 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -591,6 +591,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Спробуйте інший відбиток пальця"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Надто яскраво"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Спробуйте відкоригувати відбиток пальця"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Щоразу трохи змінюйте положення пальця"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Відбиток пальця автентифіковано"</string>
@@ -607,6 +608,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Відбитки пальців не зареєстровано."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На цьому пристрої немає сканера відбитків пальців."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчик тимчасово вимкнено."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Потрібно відкалібрувати датчик"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Відбиток пальця <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Доступ за відбитком пальця"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Використовувати відбиток пальця або дані для розблокування екрана"</string>
@@ -616,8 +618,8 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Значок відбитка пальця"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Фейсконтроль"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Повторно проскануйте обличчя"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Повторно проскануйте обличчя для ефективнішого розпізнавання"</string>
+ <string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Сталася помилка з фейсконтролем"</string>
+ <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Натисніть, щоб видалити свою модель обличчя, а потім знову додайте її"</string>
<string name="face_setup_notification_title" msgid="8843461561970741790">"Налаштування фейсконтролю"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Ви зможете розблоковувати телефон, подивившись на нього"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Налаштуйте більше способів розблокування"</string>
@@ -1498,10 +1500,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Додаток зможе надсилати запити на видалення пакетів."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"запитувати дозвіл ігнорувати оптимізацію використання заряду акумулятора"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Додаток зможе запитувати дозвіл ігнорувати оптимізацію використання заряду акумулятора."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"подавати запити на всі пакети"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Дозволяє додатку переглядати всі встановлені пакети."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Двічі натис. для кер. масшт."</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Не вдалося додати віджет."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Йти"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 8318021..f957a30 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"دوسرا فنگر پرنٹ آزمائیں"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"کافی روشنی ہے"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ایڈجسٹ کرنے کی کوشش کریں"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ہر بار اپنی انگلی کی پوزیشن کو تھوڑا تبدیل کریں"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"فنگر پرنٹ کی تصدیق ہو گئی"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"کوئی فنگر پرنٹ مندرج شدہ نہیں ہے۔"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"اس آلہ میں فنگر پرنٹ سینسر نہیں ہے۔"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"سینسر عارضی طور غیر فعال ہے۔"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"سینسر کو کیلیبریشن کی ضرورت ہے"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"انگلی <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"فنگر پرنٹ استعمال کریں"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"فنگر پرنٹ یا اسکرین لاک استعمال کریں"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"فنگر پرنٹ آئیکن"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"فیس اَنلاک"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"اپنے چہرے کو دوبارہ مندرج کریں"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"شناخت کو بہتر بنانے کے لیے براہ کرم اپنے چہرے کو دوبارہ مندرج کریں"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"فیس اَنلاک سیٹ اپ کریں"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"اپنے فون کی طرف دیکھ کر اسے غیر مقفل کریں"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"غیر مقفل کرنے کے مزید طریقے سیٹ اپ کریں"</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"متن کو کلپ بورڈ پر کاپی کیا گیا۔"</string>
<string name="copied" msgid="4675902854553014676">"کاپی ہو گیا"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> سے <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> میں پیسٹ کیا گیا"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> نے آپ کے کلپ بورڈ سے پپیسٹ کر دیا"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> نے آپ کے کلپ بورڈ سے پپیسٹ کر دیا"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> نے آپ کا کاپی کردہ ٹیکسٹ پیسٹ کر دیا"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> نے آپ کی کاپی کردہ ایک تصویر پیسٹ کر دی"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> نے آپ کا کاپی کردہ مواد پیسٹ کر دیا"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"ایپلیکیشن کو پیکجز حذف کرنے کیلئے درخواست کرنے کی اجازت ہے۔"</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"بیٹری کی بہتریاں نظر انداز کرنے کا پوچھیں"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"اس ایپ کیلئے ایک ایپ کو بیٹری کی کارکردگی بہتر بنانے کو نظر انداز کرنے کی اجازت دیں۔"</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"سبھی پیکیجز سے متعلق استفسار کریں"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"ایپ کو سبھی انسٹال کردہ پیکیجز دیکھنے کی اجازت دیتا ہے۔"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"زوم کنٹرول کیلئے دوبار تھپتھپائیں"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"ویجٹس کو شامل نہیں کرسکا۔"</string>
<string name="ime_action_go" msgid="5536744546326495436">"جائیں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 4c30b54..29c2199 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Boshqa barmoq izi bilan urining"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Juda yorqin"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Moslashga urining"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Barmoqni har safar biroz surib joylang"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Barmoq izi tekshirildi"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Hech qanday barmoq izi qayd qilinmagan."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu qurilmada barmoq izi skaneri mavjud emas."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor vaqtincha faol emas."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Sensorni sozlash kerak"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Barmoq izi <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Barmoq izi ishlatish"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Barmoq izi yoki ekran qulfi"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Barmoq izi belgisi"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Yuz bilan ochish"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Yuzingizni yana qayd qiling"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Yuzingiz yanada yaxshiroq aniqlanishi uchun uni yana bir marta qayd qiling"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Yuz bilan ochishni sozlash"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Telefoningizni yuz tekshiruvi yordamida qulfdan chiqaring"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Qulfdan chiqarishning boshqa usullarini sozlang"</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"Matn klipboardga nusxa olindi."</string>
<string name="copied" msgid="4675902854553014676">"Nusxalandi"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> ilovasidan <xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> joylandi"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> vaqtinchalik xotiradan joylandi"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> vaqtinchalik xotiradan joyladi"</string>
<string name="pasted_text" msgid="4298871641549173733">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> siz nusxa olgan matnni joyladi"</string>
<string name="pasted_image" msgid="4729097394781491022">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> siz nusxa olgan rasmni joyladi"</string>
<string name="pasted_content" msgid="646276353060777131">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> siz nusxa olgan kontentni joyladi"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Ilovaga paketlarni o‘chirib tashlash so‘rovini yuborish imkonini beradi."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"batareya quvvatidan xohlagancha foydalanishni so‘rash"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Ilovaga batareya quvvatidan xohlagancha foydalanish uchun ruxsat so‘rashga imkon beradi."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"barcha paketlarni chiqarish"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Ilova oʻrnatilgan barcha paketlarni koʻrishiga ruxsat beradi"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Ko‘lamini o‘zgartirish uchun ikki marta bosing"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Vidjet qo‘shilmadi."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Tanlash"</string>
@@ -1990,8 +1992,8 @@
<string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Fayllarni ko‘rish uchun bosing"</string>
<string name="pin_target" msgid="8036028973110156895">"Qadash"</string>
<string name="pin_specific_target" msgid="7824671240625957415">"Mahkamlash: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="unpin_target" msgid="3963318576590204447">"Olib tashlash"</string>
- <string name="unpin_specific_target" msgid="3859828252160908146">"Olib tashlash: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="unpin_target" msgid="3963318576590204447">"Yechib olish"</string>
+ <string name="unpin_specific_target" msgid="3859828252160908146">"Yechib olish: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="app_info" msgid="6113278084877079851">"Ilova haqida"</string>
<string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="demo_starting_message" msgid="6577581216125805905">"Demo boshlanmoqda…"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 61aba14..08dbdde 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -349,7 +349,7 @@
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"Cho phép ứng dụng mở rộng hoặc thu gọn thanh trạng thái."</string>
<string name="permlab_fullScreenIntent" msgid="4310888199502509104">"hiển thị thông báo dưới dạng các hoạt động ở chế độ toàn màn hình trên thiết bị ở trạng thái khóa"</string>
<string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"Cho phép ứng dụng hiển thị thông báo dưới dạng các hoạt động ở chế độ toàn màn hình trên thiết bị ở trạng thái khóa"</string>
- <string name="permlab_install_shortcut" msgid="7451554307502256221">"cài đặt lối tắt"</string>
+ <string name="permlab_install_shortcut" msgid="7451554307502256221">"Cài đặt lối tắt"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"Cho phép ứng dụng thêm lối tắt trên Màn hình chính mà không cần sự can thiệp của người dùng."</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"gỡ cài đặt lối tắt"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Cho phép ứng dụng xóa lối tắt trên Màn hình chính mà không cần sự can thiệp của người dùng."</string>
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Hãy thử một vân tay khác"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Quá sáng"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Hãy thử điều chỉnh"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Mỗi lần, hãy thay đổi vị trí ngón tay một chút"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Đã xác thực vân tay"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Chưa đăng ký vân tay."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Thiết bị này không có cảm biến vân tay."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Đã tạm thời tắt cảm biến."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Cảm biến cần hiệu chỉnh"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Ngón tay <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Dùng vân tay"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Dùng vân tay hoặc phương thức khóa màn hình"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Biểu tượng vân tay"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Mở khóa bằng khuôn mặt"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Đăng ký lại khuôn mặt của bạn"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Để cải thiện khả năng nhận dạng, hãy đăng ký lại khuôn mặt của bạn"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Thiết lập tính năng Mở khóa bằng khuôn mặt"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Mở khóa điện thoại bằng cách nhìn vào điện thoại"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Thiết lập thêm những cách mở khóa khác"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Cho phép ứng dụng yêu cầu xóa gói."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"hỏi để bỏ qua tối ưu hóa pin"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Cho phép ứng dụng hỏi quyền để bỏ qua tối ưu hóa pin cho ứng dụng đó."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"truy vấn tất cả các gói"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Cho phép một ứng dụng xem tất cả các gói đã cài đặt."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Nhấn hai lần để kiểm soát thu phóng"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Không thể thêm tiện ích."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Đến"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 7fefe74..e453b04 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"请试试其他指纹"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"光线太亮"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"请尝试调整指纹"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"请在每次放手指时略微更改手指的位置"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"已验证指纹"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未注册任何指纹。"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"此设备没有指纹传感器。"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"传感器已暂时停用。"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"传感器需要校准"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"使用指纹"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"使用指纹或屏幕锁定凭据"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"指纹图标"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"人脸解锁"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"重新注册您的面孔"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"要提升识别精确度,请重新注册您的面孔"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"设置人脸解锁"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"脸部对准手机即可将其解锁"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"设置更多解锁方式"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"允许应用请求删除文件包。"</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"请求忽略电池优化"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"允许应用请求相应的权限,以便忽略针对该应用的电池优化。"</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"查询所有软件包"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"允许应用查看所有已安装的软件包。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"双击可以进行缩放控制"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"无法添加微件。"</string>
<string name="ime_action_go" msgid="5536744546326495436">"开始"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 457f0d5..02cd207 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"改用其他指紋"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"太亮"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"嘗試調整"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"每次掃瞄時請稍微變更手指的位置"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"驗證咗指紋"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未註冊任何指紋"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"此裝置沒有指紋感應器。"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"感應器已暫時停用。"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"需要校正感應器"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"使用指紋鎖定"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"使用指紋或螢幕鎖定"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"指紋圖示"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"面孔解鎖"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"重新註冊臉孔"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"如要提高識別能力,請重新註冊您的臉孔"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"設定「面孔解鎖」"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"直望手機即可解鎖"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"設定更多解鎖方法"</string>
@@ -1019,7 +1023,7 @@
<string name="text_copied" msgid="2531420577879738860">"文字已複製到剪貼簿。"</string>
<string name="copied" msgid="4675902854553014676">"已複製"</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> 已貼上從 <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g> 複製的資料"</string>
- <string name="pasted_from_clipboard" msgid="7355790625710831847">"已將剪貼簿內容貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string>
+ <string name="pasted_from_clipboard" msgid="7355790625710831847">"「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」已貼上剪貼簿內容"</string>
<string name="pasted_text" msgid="4298871641549173733">"您複製的文字已貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string>
<string name="pasted_image" msgid="4729097394781491022">"您複製的圖片已貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string>
<string name="pasted_content" msgid="646276353060777131">"您複製的內容已貼到「<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g>」"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"允許應用程式要求刪除套件。"</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"要求忽略電池優化"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"允許應用程式要求就該應用程式忽略電池優化。"</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"查詢所有套件"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"允許應用程式查看所有已安裝的套件。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"輕觸兩下控制縮放"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"無法新增小工具。"</string>
<string name="ime_action_go" msgid="5536744546326495436">"開始"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 82be318..f396067 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"改用其他指紋"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"太亮"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"請試著調整"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"每次掃描時請稍微變更手指的位置"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"指紋驗證成功"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未登錄任何指紋。"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"這個裝置沒有指紋感應器。"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"感應器已暫時停用。"</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"必須校正感應器"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"手指 <xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"使用指紋"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"使用指紋或螢幕鎖定功能"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"指紋圖示"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"人臉解鎖"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"請重新註冊你的臉孔"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"為提升辨識精準度,請重新註冊你的臉孔"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"設定人臉解鎖功能"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"看著手機就能解鎖"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"設定更多解鎖方式"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"允許應用程式要求刪除套件。"</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"要求忽略電池效能最佳化設定"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"允許應用程式要求權限,以便忽略針對該應用程式的電池效能最佳化設定。"</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"查詢所有套件"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"允許應用程式查看所有已安裝的套件。"</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"點兩下以進行縮放控制"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"無法新增小工具。"</string>
<string name="ime_action_go" msgid="5536744546326495436">"開始"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 85cf88c..12b5ccb 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -585,6 +585,7 @@
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Zama ezinye izigxivizo zeminwe"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Kukhanya kakhulu"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Zama ukulungisa"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Shintsha indawo yomunwe wakho kancane isikhathi ngasinye"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Izigxivizo zeminwe zigunyaziwe"</string>
@@ -601,6 +602,7 @@
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Azikho izigxivizo zeminwe ezibhalisiwe."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Le divayisi ayinayo inzwa yezigxivizo zeminwe."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Inzwa ikhutshazwe okwesikhashana."</string>
+ <string name="fingerprint_error_bad_calibration" msgid="374406495079531135">"Inzwa idinga ukulinganisa"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Umunwe ongu-<xliff:g id="FINGERID">%d</xliff:g>"</string>
<string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Sebenzisa izigxivizo zeminwe"</string>
<string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Sebenzisa izigxivizo zeminwe noma ukukhiya isikrini"</string>
@@ -610,8 +612,10 @@
</string-array>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Isithonjana sezigxivizo zeminwe"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Ukuvula ubuso"</string>
- <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"Phinda ubhalise ubuso bakho"</string>
- <string name="face_recalibrate_notification_content" msgid="892757485125249962">"Ukuze uthuthukise ukubonwa, sicela uphinde ubhalise ubuso bakho"</string>
+ <!-- no translation found for face_recalibrate_notification_title (2524791952735579082) -->
+ <skip />
+ <!-- no translation found for face_recalibrate_notification_content (3064513770251355594) -->
+ <skip />
<string name="face_setup_notification_title" msgid="8843461561970741790">"Setha Ukuvula ngobuso"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Vula ifoni yakho ngokuyibheka"</string>
<string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Setha izindlela eziningi zokuvula"</string>
@@ -1458,10 +1462,8 @@
<string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"Ivumela uhlelo lokusebenza ukuthi lucele ukususwa kwamaphakheji."</string>
<string name="permlab_requestIgnoreBatteryOptimizations" msgid="7646611326036631439">"cela ukuziba ukulungiselelwa kwebhethri"</string>
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Ivumela uhlelo lokusebenza ukuthi licele imvume yokuziba ukulungiselela ibhethri yalolo hlelo lokusebenza."</string>
- <!-- no translation found for permlab_queryAllPackages (2928450604653281650) -->
- <skip />
- <!-- no translation found for permdesc_queryAllPackages (5339069855520996010) -->
- <skip />
+ <string name="permlab_queryAllPackages" msgid="2928450604653281650">"buza wonke amaphakheji"</string>
+ <string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Ivumela i-app ibone wonke amaphakheji afakiwe."</string>
<string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Thepha kabili ukuthola ukulawula ukusondeza"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Yehlulekile ukwengeza i-widget."</string>
<string name="ime_action_go" msgid="5536744546326495436">"Iya"</string>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index d94bcfb..55ed83b 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -140,7 +140,7 @@
<color name="notification_secondary_text_color_light">@color/primary_text_default_material_light</color>
<item name="notification_secondary_text_disabled_alpha" format="float" type="dimen">0.38</item>
<color name="notification_secondary_text_color_dark">@color/primary_text_default_material_dark</color>
- <color name="notification_default_color_dark">@color/primary_text_default_material_light</color>
+ <color name="notification_default_color_dark">#ddffffff</color>
<color name="notification_default_color_light">#a3202124</color>
<color name="notification_primary_text_color_current">@color/notification_primary_text_color_light</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ee33d48..646fbd3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1706,6 +1706,10 @@
config_enableFusedLocationOverlay is false. -->
<string name="config_fusedLocationProviderPackageName" translatable="false">com.android.location.fused</string>
+ <!-- Default value for the ADAS GNSS Location Enabled setting if this setting has never been
+ set before. -->
+ <bool name="config_defaultAdasGnssLocationEnabled" translatable="false">false</bool>
+
<string-array name="config_locationExtraPackageNames" translatable="false"></string-array>
<!-- The package name of the default network recommendation app.
@@ -1890,6 +1894,9 @@
STREAM_MUSIC as if it's on TV platform. -->
<bool name="config_single_volume">false</bool>
+ <!-- Flag indicating whether the volume panel should show remote sessions. -->
+ <bool name="config_volumeShowRemoteSessions">true</bool>
+
<!-- Flag indicating that an outbound call must have a call capable phone account
that has declared it can process the call's handle. -->
<bool name="config_requireCallCapableAccountForHandle">false</bool>
@@ -3350,6 +3357,10 @@
<!-- The default vibration strength, must be between 1 and 255 inclusive. -->
<integer name="config_defaultVibrationAmplitude">255</integer>
+ <!-- The max vibration strength allowed in audio haptic channels, must be positive or zero if
+ limit is unknown. -->
+ <item name="config_hapticChannelMaxVibrationAmplitude" format="float" type="dimen">0</item>
+
<!-- If the device should still vibrate even in low power mode, for certain priority vibrations
(e.g. accessibility, alarms). This is mainly for Wear devices that don't have speakers. -->
<bool name="config_allowPriorityVibrationsInLowPowerMode">false</bool>
@@ -3529,6 +3540,12 @@
<!-- If true, all guest users created on the device will be ephemeral. -->
<bool name="config_guestUserEphemeral">false</bool>
+ <!-- Whether device should always have a guest user available. If true, guest user will be
+ created on boot, and a new guest user will be created in the background anytime the current
+ guest user is removed. Instead of showing "Add guest" and "Remove guest", the UI will show
+ "Guest" and "Reset guest". -->
+ <bool name="config_guestUserAutoCreated">false</bool>
+
<!-- Enforce strong auth on boot. Setting this to false represents a security risk and should
not be ordinarily done. The only case in which this might be permissible is in a car head
unit where there are hardware mechanisms to protect the device (physical keys) and not
@@ -3570,13 +3587,21 @@
-->
<integer name="config_respectsActivityMinWidthHeightMultiWindow">0</integer>
- <!-- This value is only used when the device checks activity min width/height to determine if it
+ <!-- This value is only used when the device checks activity min height to determine if it
can be shown in multi windowing modes.
- If the activity min width/height is greater than this percentage of the display smallest
- width, it will not be allowed to be shown in multi windowing modes.
+ If the activity min height is greater than this percentage of the display height in
+ portrait, it will not be allowed to be shown in multi windowing modes.
The value should be between [0 - 1].
-->
- <item name="config_minPercentageMultiWindowSupportWidth" format="float" type="dimen">0.3</item>
+ <item name="config_minPercentageMultiWindowSupportHeight" format="float" type="dimen">0.3</item>
+
+ <!-- This value is only used when the device checks activity min width to determine if it
+ can be shown in multi windowing modes.
+ If the activity min width is greater than this percentage of the display width in
+ landscape, it will not be allowed to be shown in multi windowing modes.
+ The value should be between [0 - 1].
+ -->
+ <item name="config_minPercentageMultiWindowSupportWidth" format="float" type="dimen">0.5</item>
<!-- If the display smallest screen width is greater or equal to this value, we will treat it
as a large screen device, which will have some multi window features enabled by default.
@@ -4521,6 +4546,9 @@
-->
</integer-array>
+ <!-- How long it takes for the HW to start illuminating after the illumination is requested. -->
+ <integer name="config_udfps_illumination_transition_ms">50</integer>
+
<!-- Indicates whether device has a power button fingerprint sensor. -->
<bool name="config_is_powerbutton_fps" translatable="false" >false</bool>
@@ -4615,6 +4643,15 @@
<!-- The package name for the default bug report handler app from power menu short press. This app must be allowlisted. -->
<string name="config_defaultBugReportHandlerApp" translatable="false"></string>
+ <!-- When true, enables the allowlisted app to upload profcollect reports. -->
+ <bool name="config_profcollectReportUploaderEnabled">false</bool>
+
+ <!-- The package name for the default profcollect report uploader app. This app must be allowlisted. -->
+ <string name="config_defaultProfcollectReportUploaderApp" translatable="false"></string>
+
+ <!-- The action name for the default profcollect report uploader app. -->
+ <string name="config_defaultProfcollectReportUploaderAction" translatable="false"></string>
+
<!-- The default value used for RawContacts.ACCOUNT_NAME when contacts are inserted without this
column set. These contacts are stored locally on the device and will not be removed even
if no android.account.Account with this name exists. A null string will be used if the
@@ -4994,6 +5031,10 @@
<!-- Default value for Settings.ASSIST_TOUCH_GESTURE_ENABLED -->
<bool name="config_assistTouchGestureEnabledDefault">true</bool>
+ <!-- The maximum byte size of the information contained in the bundle of
+ HotwordDetectedResult. -->
+ <integer translatable="false" name="config_hotwordDetectedResultMaxBundleSize">0</integer>
+
<!-- The amount of dimming to apply to wallpapers with mid range luminance. 0 displays
the wallpaper at full brightness. 1 displays the wallpaper as fully black. -->
<item name="config_wallpaperDimAmount" format="float" type="dimen">0.05</item>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index bdeff893..d1a5cc4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1603,6 +1603,8 @@
<string name="fingerprint_acquired_too_bright">Too bright</string>
<!-- Message shown during fingerprint acquisition when a fingerprint must be adjusted.[CHAR LIMIT=50] -->
<string name="fingerprint_acquired_try_adjusting">Try adjusting</string>
+ <!-- Message shown during fingerprint acquisition when a fingeprint area has already been captured during enrollment [CHAR LIMIT=100] -->
+ <string name="fingerprint_acquired_immobile">Change the position of your finger slightly each time</string>
<!-- Array containing custom messages shown during fingerprint acquisision from vendor. Vendor is expected to add and translate these strings -->
<string-array name="fingerprint_acquired_vendor">
</string-array>
@@ -1636,6 +1638,8 @@
<string name="fingerprint_error_hw_not_present">This device does not have a fingerprint sensor.</string>
<!-- Generic error message shown when fingerprint is not available due to a security vulnerability. [CHAR LIMIT=50] -->
<string name="fingerprint_error_security_update_required">Sensor temporarily disabled.</string>
+ <!-- Generic error message shown when fingerprint needs calibration [CHAR LIMIT=150] -->
+ <string name="fingerprint_error_bad_calibration">Can\u2019t use fingerprint sensor. Visit a repair provider</string>
<!-- Template to be used to name enrolled fingerprints by default. -->
<string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string>
@@ -1659,9 +1663,9 @@
<!-- Notification name shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
<string name="face_recalibrate_notification_name">Face Unlock</string>
<!-- Notification title shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
- <string name="face_recalibrate_notification_title">Re-enroll your face</string>
+ <string name="face_recalibrate_notification_title">Issue with Face Unlock</string>
<!-- Notification content shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] -->
- <string name="face_recalibrate_notification_content">To improve recognition, please re-enroll your face</string>
+ <string name="face_recalibrate_notification_content">Tap to delete your face model, then add your face again</string>
<!-- Title of a notification that directs the user to set up Face Unlock by enrolling their face. [CHAR LIMIT=NONE] -->
<string name="face_setup_notification_title">Set up Face Unlock</string>
<!-- Contents of a notification that directs the user to set up face unlock by enrolling their face. [CHAR LIMIT=NONE] -->
@@ -1671,6 +1675,13 @@
<!-- Contents of a notification that directs the user to enroll a fingerprint. [CHAR LIMIT=NONE] -->
<string name="fingerprint_setup_notification_content">Tap to add a fingerprint</string>
+ <!-- Notification name shown when the system requires the user to re-calibrate their fingerprint. [CHAR LIMIT=NONE] -->
+ <string name="fingerprint_recalibrate_notification_name">Fingerprint Unlock</string>
+ <!-- Notification title shown when the system requires the user to re-calibrate their fingerprint. [CHAR LIMIT=NONE] -->
+ <string name="fingerprint_recalibrate_notification_title">Can\u2019t use fingerprint sensor</string>
+ <!-- Notification content shown when the system requires the user to re-calibrate their fingerprint. [CHAR LIMIT=NONE] -->
+ <string name="fingerprint_recalibrate_notification_content">Visit a repair provider.</string>
+
<!-- Message shown during face acquisition when the face cannot be recognized [CHAR LIMIT=50] -->
<string name="face_acquired_insufficient">Couldn\u2019t capture accurate face data. Try again.</string>
<!-- Message shown during face acquisition when the image is too bright [CHAR LIMIT=50] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7d685a2..c05adfd 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -395,6 +395,7 @@
<java-symbol type="bool" name="config_supportsMultiDisplay" />
<java-symbol type="integer" name="config_supportsNonResizableMultiWindow" />
<java-symbol type="integer" name="config_respectsActivityMinWidthHeightMultiWindow" />
+ <java-symbol type="dimen" name="config_minPercentageMultiWindowSupportHeight" />
<java-symbol type="dimen" name="config_minPercentageMultiWindowSupportWidth" />
<java-symbol type="integer" name="config_largeScreenSmallestScreenWidthDp" />
<java-symbol type="bool" name="config_useLegacySplit" />
@@ -402,6 +403,7 @@
<java-symbol type="bool" name="config_supportsSystemDecorsOnSecondaryDisplays" />
<java-symbol type="bool" name="config_supportsInsecureLockScreen" />
<java-symbol type="bool" name="config_guestUserEphemeral" />
+ <java-symbol type="bool" name="config_guestUserAutoCreated" />
<java-symbol type="bool" name="config_localDisplaysMirrorContent" />
<java-symbol type="array" name="config_localPrivateDisplayPorts" />
<java-symbol type="integer" name="config_defaultDisplayDefaultColorMode" />
@@ -1917,6 +1919,7 @@
<java-symbol type="bool" name="config_tintNotificationActionButtons" />
<java-symbol type="bool" name="config_dozeAfterScreenOffByDefault" />
<java-symbol type="bool" name="config_enableActivityRecognitionHardwareOverlay" />
+ <java-symbol type="bool" name="config_defaultAdasGnssLocationEnabled" />
<java-symbol type="bool" name="config_enableFusedLocationOverlay" />
<java-symbol type="bool" name="config_enableGeocoderOverlay" />
<java-symbol type="bool" name="config_enableGeofenceOverlay" />
@@ -2012,6 +2015,7 @@
<java-symbol type="integer" name="config_notificationServiceArchiveSize" />
<java-symbol type="integer" name="config_previousVibrationsDumpLimit" />
<java-symbol type="integer" name="config_defaultVibrationAmplitude" />
+ <java-symbol type="dimen" name="config_hapticChannelMaxVibrationAmplitude" />
<java-symbol type="integer" name="config_vibrationWaveformRampStepDuration" />
<java-symbol type="integer" name="config_vibrationWaveformRampDownDuration" />
<java-symbol type="integer" name="config_radioScanningTimeout" />
@@ -2536,6 +2540,11 @@
<java-symbol type="string" name="fingerprint_error_no_fingerprints" />
<java-symbol type="string" name="fingerprint_error_hw_not_present" />
<java-symbol type="string" name="fingerprint_error_security_update_required" />
+ <java-symbol type="string" name="fingerprint_error_bad_calibration" />
+ <java-symbol type="string" name="fingerprint_acquired_immobile" />
+ <java-symbol type="string" name="fingerprint_recalibrate_notification_name" />
+ <java-symbol type="string" name="fingerprint_recalibrate_notification_title" />
+ <java-symbol type="string" name="fingerprint_recalibrate_notification_content" />
<!-- Fingerprint config -->
<java-symbol type="integer" name="config_fingerprintMaxTemplatesPerUser"/>
@@ -2590,6 +2599,7 @@
<java-symbol type="array" name="config_biometric_sensors" />
<java-symbol type="bool" name="allow_test_udfps" />
<java-symbol type="array" name="config_udfps_sensor_props" />
+ <java-symbol type="integer" name="config_udfps_illumination_transition_ms" />
<java-symbol type="bool" name="config_is_powerbutton_fps" />
<java-symbol type="array" name="config_face_acquire_enroll_ignorelist" />
@@ -4013,6 +4023,11 @@
<java-symbol type="bool" name="config_bugReportHandlerEnabled" />
<java-symbol type="string" name="config_defaultBugReportHandlerApp" />
+ <!-- For profcollect report uploader -->
+ <java-symbol type="bool" name="config_profcollectReportUploaderEnabled" />
+ <java-symbol type="string" name="config_defaultProfcollectReportUploaderApp" />
+ <java-symbol type="string" name="config_defaultProfcollectReportUploaderAction" />
+
<java-symbol type="string" name="usb_device_resolve_prompt_warn" />
<!-- For Accessibility system actions -->
@@ -4399,5 +4414,9 @@
<java-symbol type="bool" name="config_assistLongPressHomeEnabledDefault" />
<java-symbol type="bool" name="config_assistTouchGestureEnabledDefault" />
+ <java-symbol type="integer" name="config_hotwordDetectedResultMaxBundleSize" />
+
<java-symbol type="dimen" name="config_wallpaperDimAmount" />
+
+ <java-symbol type="bool" name="config_volumeShowRemoteSessions" />
</resources>
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 93e4a29..8fae80d 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -121,6 +121,8 @@
":FrameworksCoreTests_keyset_splat_api",
":FrameworksCoreTests_locales",
":FrameworksCoreTests_overlay_config",
+ ":FrameworksCoreTests_res_version_after",
+ ":FrameworksCoreTests_res_version_before",
":FrameworksCoreTests_version_1",
":FrameworksCoreTests_version_1_diff",
":FrameworksCoreTests_version_1_nosys",
diff --git a/core/tests/coretests/apks/res_upgrade/Android.bp b/core/tests/coretests/apks/res_upgrade/Android.bp
new file mode 100644
index 0000000..c58614f
--- /dev/null
+++ b/core/tests/coretests/apks/res_upgrade/Android.bp
@@ -0,0 +1,22 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "FrameworksCoreTests_res_version_before",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ resource_dirs: ["res_before"],
+ certificate: ":FrameworksCoreTests_unit_test_cert",
+}
+
+android_test_helper_app {
+ name: "FrameworksCoreTests_res_version_after",
+ defaults: ["FrameworksCoreTests_apks_defaults"],
+ resource_dirs: ["res_after"],
+ certificate: ":FrameworksCoreTests_unit_test_cert",
+}
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/core/tests/coretests/apks/res_upgrade/AndroidManifest.xml
similarity index 73%
copy from packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
copy to core/tests/coretests/apks/res_upgrade/AndroidManifest.xml
index a2bbd2b..1c607c9 100644
--- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
+++ b/core/tests/coretests/apks/res_upgrade/AndroidManifest.xml
@@ -12,8 +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.
-->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.coretests.res_version">
+ <application android:hasCode="false"/>
+</manifest>
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/core/tests/coretests/apks/res_upgrade/res_after/values/values.xml
similarity index 74%
copy from packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
copy to core/tests/coretests/apks/res_upgrade/res_after/values/values.xml
index a2bbd2b..db4fd54 100644
--- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
+++ b/core/tests/coretests/apks/res_upgrade/res_after/values/values.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+
<!--
~ Copyright (C) 2021 The Android Open Source Project
~
@@ -12,8 +13,10 @@
~ 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.
-->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
+<resources>
+ <string name="version">after</string>
+ <public type="string" name="version" id="0x7f010000"/>
+</resources>
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/core/tests/coretests/apks/res_upgrade/res_before/values/values.xml
similarity index 74%
copy from packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
copy to core/tests/coretests/apks/res_upgrade/res_before/values/values.xml
index a2bbd2b..63fc790 100644
--- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
+++ b/core/tests/coretests/apks/res_upgrade/res_before/values/values.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+
<!--
~ Copyright (C) 2021 The Android Open Source Project
~
@@ -12,8 +13,10 @@
~ 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.
-->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
+<resources>
+ <string name="version">before</string>
+ <public type="string" name="version" id="0x7f010000"/>
+</resources>
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
new file mode 100644
index 0000000..8c05978
--- /dev/null
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Test for verifying the behavior of {@link PropertyInvalidatedCache}. This test does
+ * not use any actual binder calls - it is entirely self-contained.
+ * <p>
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:PropertyInvalidatedCacheTests
+ */
+@SmallTest
+public class PropertyInvalidatedCacheTests {
+
+ static final String CACHE_PROPERTY = "cache_key.cache_test_a";
+
+ // This class is a proxy for binder calls. It contains a counter that increments
+ // every time the class is queried.
+ private static class ServerProxy {
+ // The number of times this class was queried.
+ private int mCount = 0;
+
+ // A single query. The key behavior is that the query count is incremented.
+ boolean query(int x) {
+ mCount++;
+ return value(x);
+ }
+
+ // Return the expected value of an input, without incrementing the query count.
+ boolean value(int x) {
+ return x % 3 == 0;
+ }
+
+ // Verify the count.
+ void verify(int x) {
+ assertEquals(x, mCount);
+ }
+ }
+
+ @Test
+ public void testDisableCache1() {
+
+ // A stand-in for the binder. The test verifies that calls are passed through to
+ // this class properly.
+ ServerProxy tester = new ServerProxy();
+
+ // Three caches, all using the same system property but one uses a different name.
+ PropertyInvalidatedCache<Integer, Boolean> cache1 =
+ new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
+ @Override
+ protected Boolean recompute(Integer x) {
+ return tester.query(x);
+ }
+ };
+ PropertyInvalidatedCache<Integer, Boolean> cache2 =
+ new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
+ @Override
+ protected Boolean recompute(Integer x) {
+ return tester.query(x);
+ }
+ };
+ PropertyInvalidatedCache<Integer, Boolean> cache3 =
+ new PropertyInvalidatedCache<>(4, CACHE_PROPERTY, "cache3") {
+ @Override
+ protected Boolean recompute(Integer x) {
+ return tester.query(x);
+ }
+ };
+
+ // Caches are enabled upon creation.
+ assertEquals(false, cache1.getDisabledState());
+ assertEquals(false, cache2.getDisabledState());
+ assertEquals(false, cache3.getDisabledState());
+
+ // Disable the cache1 instance. Only cache1 is disabled
+ cache1.disableInstance();
+ assertEquals(true, cache1.getDisabledState());
+ assertEquals(false, cache2.getDisabledState());
+ assertEquals(false, cache3.getDisabledState());
+
+ // Disable cache1. This will disable cache1 and cache2 because they share the
+ // same name. cache3 has a different name and will not be disabled.
+ cache1.disableLocal();
+ assertEquals(true, cache1.getDisabledState());
+ assertEquals(true, cache2.getDisabledState());
+ assertEquals(false, cache3.getDisabledState());
+
+ // Create a new cache1. Verify that the new instance is disabled.
+ cache1 = new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
+ @Override
+ protected Boolean recompute(Integer x) {
+ return tester.query(x);
+ }
+ };
+ assertEquals(true, cache1.getDisabledState());
+ }
+}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java
index de0670b..228a061 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java
@@ -18,7 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.testng.Assert.expectThrows;
+import static org.junit.Assert.assertThrows;
import org.junit.Test;
@@ -26,7 +26,7 @@
@Test
public void testMapNullPointerException() {
NullPointerException e =
- expectThrows(
+ assertThrows(
NullPointerException.class,
() -> {
Object o = null;
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
deleted file mode 100644
index 6fad4b8d..0000000
--- a/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.appsearch;
-
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.android.server.appsearch.testing.AppSearchEmail;
-
-import com.google.common.collect.ImmutableSet;
-
-import org.junit.Test;
-
-import java.util.Set;
-
-public class PutDocumentsRequestTest {
-
- @Test
- public void addGenericDocument_byCollection() {
- Set<AppSearchEmail> emails =
- ImmutableSet.of(
- new AppSearchEmail.Builder("namespace", "test1").build(),
- new AppSearchEmail.Builder("namespace", "test2").build());
- PutDocumentsRequest request =
- new PutDocumentsRequest.Builder().addGenericDocuments(emails).build();
-
- assertThat(request.getGenericDocuments().get(0).getId()).isEqualTo("test1");
- assertThat(request.getGenericDocuments().get(1).getId()).isEqualTo("test2");
- }
-}
diff --git a/core/tests/coretests/src/android/app/appsearch/external/util/IndentingStringBuilderTest.java b/core/tests/coretests/src/android/app/appsearch/external/util/IndentingStringBuilderTest.java
new file mode 100644
index 0000000..057ecbd
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/external/util/IndentingStringBuilderTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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 android.app.appsearch.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import org.junit.Test;
+
+public class IndentingStringBuilderTest {
+ @Test
+ public void testAppendIndentedStrings() {
+ IndentingStringBuilder stringBuilder = new IndentingStringBuilder();
+ stringBuilder
+ .increaseIndentLevel()
+ .append("\nIndentLevel1\nIndentLevel1\n")
+ .decreaseIndentLevel()
+ .append("IndentLevel0,\n");
+
+ String str = stringBuilder.toString();
+ String expectedString = "\n IndentLevel1\n IndentLevel1\nIndentLevel0,\n";
+
+ assertThat(str).isEqualTo(expectedString);
+ }
+
+ @Test
+ public void testDecreaseIndentLevel_throwsException() {
+ IndentingStringBuilder stringBuilder = new IndentingStringBuilder();
+
+ Exception e =
+ assertThrows(
+ IllegalStateException.class, () -> stringBuilder.decreaseIndentLevel());
+ assertThat(e).hasMessageThat().contains("Cannot set indent level below 0.");
+ }
+
+ @Test
+ public void testAppendIndentedObjects() {
+ IndentingStringBuilder stringBuilder = new IndentingStringBuilder();
+ Object stringProperty = "String";
+ Object longProperty = 1L;
+ Object booleanProperty = true;
+
+ stringBuilder
+ .append(stringProperty)
+ .append("\n")
+ .increaseIndentLevel()
+ .append(longProperty)
+ .append("\n")
+ .decreaseIndentLevel()
+ .append(booleanProperty);
+
+ String str = stringBuilder.toString();
+ String expectedString = "String\n 1\ntrue";
+
+ assertThat(str).isEqualTo(expectedString);
+ }
+
+ @Test
+ public void testAppendIndentedStrings_doesNotIndentLineBreak() {
+ IndentingStringBuilder stringBuilder = new IndentingStringBuilder();
+
+ stringBuilder
+ .append("\n")
+ .increaseIndentLevel()
+ .append("\n\n")
+ .decreaseIndentLevel()
+ .append("\n");
+
+ String str = stringBuilder.toString();
+ String expectedString = "\n\n\n\n";
+
+ assertThat(str).isEqualTo(expectedString);
+ }
+}
diff --git a/core/tests/coretests/src/android/content/ContextTest.java b/core/tests/coretests/src/android/content/ContextTest.java
index d1776fb..8488a84 100644
--- a/core/tests/coretests/src/android/content/ContextTest.java
+++ b/core/tests/coretests/src/android/content/ContextTest.java
@@ -25,15 +25,23 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.app.ActivityThread;
+import android.app.PendingIntent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.inputmethodservice.InputMethodService;
import android.media.ImageReader;
+import android.os.FileUtils;
import android.os.UserHandle;
import android.view.Display;
@@ -42,12 +50,20 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.frameworks.coretests.R;
+
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
/**
- * Build/Install/Run:
- * atest FrameworksCoreTests:ContextTest
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:ContextTest
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -216,6 +232,132 @@
assertFalse(context.isUiContext());
}
+ private static class TestReceiver extends BroadcastReceiver implements AutoCloseable {
+ private static final String INTENT_ACTION = "com.android.server.pm.test.test_app.action";
+ private final ArrayBlockingQueue<Intent> mResults = new ArrayBlockingQueue<>(1);
+
+ public IntentSender makeIntentSender() {
+ return PendingIntent.getBroadcast(getContext(), 0, new Intent(INTENT_ACTION),
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED)
+ .getIntentSender();
+ }
+
+ public void waitForIntent() throws InterruptedException {
+ assertNotNull(mResults.poll(30, TimeUnit.SECONDS));
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mResults.add(intent);
+ }
+
+ public void register() {
+ getContext().registerReceiver(this, new IntentFilter(INTENT_ACTION));
+ }
+
+ @Override
+ public void close() throws Exception {
+ getContext().unregisterReceiver(this);
+ }
+
+ private Context getContext() {
+ return InstrumentationRegistry.getInstrumentation().getContext();
+ }
+ }
+
+ @Test
+ public void applicationContextBeforeAndAfterUpgrade() throws Exception {
+ final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ final String testPackageName = "com.android.frameworks.coretests.res_version";
+ try {
+ final PackageManager pm = context.getPackageManager();
+ final int versionRes = 0x7f010000;
+
+ final Context appContext = ApplicationProvider.getApplicationContext();
+ installApk(appContext, R.raw.res_version_before);
+
+ ApplicationInfo info = pm.getApplicationInfo(testPackageName, 0);
+ final Context beforeContext = appContext.createApplicationContext(info, 0);
+ assertEquals("before", beforeContext.getResources().getString(versionRes));
+
+ installApk(appContext, R.raw.res_version_after);
+
+ info = pm.getApplicationInfo(testPackageName, 0);
+ final Context afterContext = appContext.createApplicationContext(info, 0);
+ assertEquals("before", beforeContext.createConfigurationContext(Configuration.EMPTY)
+ .getResources().getString(versionRes));
+ assertEquals("after", afterContext.createConfigurationContext(Configuration.EMPTY)
+ .getResources().getString(versionRes));
+ assertNotEquals(beforeContext.getPackageResourcePath(),
+ afterContext.getPackageResourcePath());
+ } finally {
+ uninstallPackage(context, testPackageName);
+ }
+ }
+
+ @Test
+ public void packageContextBeforeAndAfterUpgrade() throws Exception {
+ final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ final String testPackageName = "com.android.frameworks.coretests.res_version";
+ try {
+ final int versionRes = 0x7f010000;
+ final Context appContext = ApplicationProvider.getApplicationContext();
+ installApk(appContext, R.raw.res_version_before);
+
+ final Context beforeContext = appContext.createPackageContext(testPackageName, 0);
+ assertEquals("before", beforeContext.getResources().getString(versionRes));
+
+ installApk(appContext, R.raw.res_version_after);
+
+ final Context afterContext = appContext.createPackageContext(testPackageName, 0);
+ assertEquals("before", beforeContext.createConfigurationContext(Configuration.EMPTY)
+ .getResources().getString(versionRes));
+ assertEquals("after", afterContext.createConfigurationContext(Configuration.EMPTY)
+ .getResources().getString(versionRes));
+ assertNotEquals(beforeContext.getPackageResourcePath(),
+ afterContext.getPackageResourcePath());
+ } finally {
+ uninstallPackage(context, testPackageName);
+ }
+ }
+
+ private void installApk(Context context, int rawApkResId) throws Exception {
+ final PackageManager pm = context.getPackageManager();
+ final PackageInstaller pi = pm.getPackageInstaller();
+ final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ final int sessionId = pi.createSession(params);
+
+ try (PackageInstaller.Session session = pi.openSession(sessionId)) {
+ // Copy the apk to the install session.
+ final Resources resources = context.getResources();
+ try (InputStream is = resources.openRawResource(rawApkResId);
+ OutputStream sessionOs = session.openWrite("base", 0, -1)) {
+ FileUtils.copy(is, sessionOs);
+ }
+
+ // Wait for the installation to finish
+ try (TestReceiver receiver = new TestReceiver()) {
+ receiver.register();
+ ShellIdentityUtils.invokeMethodWithShellPermissions(session,
+ (s) -> {
+ s.commit(receiver.makeIntentSender());
+ return true;
+ });
+ receiver.waitForIntent();
+ }
+ }
+ }
+
+ private void uninstallPackage(Context context, String packageName) throws Exception {
+ try (TestReceiver receiver = new TestReceiver()) {
+ receiver.register();
+ final PackageInstaller pi = context.getPackageManager().getPackageInstaller();
+ pi.uninstall(packageName, receiver.makeIntentSender());
+ receiver.waitForIntent();
+ }
+ }
+
private Context createUiContext() {
final Context appContext = ApplicationProvider.getApplicationContext();
final DisplayManager displayManager = appContext.getSystemService(DisplayManager.class);
diff --git a/core/tests/coretests/src/android/os/PackageTagsListTest.java b/core/tests/coretests/src/android/os/PackageTagsListTest.java
index 518e02e..9034a5c 100644
--- a/core/tests/coretests/src/android/os/PackageTagsListTest.java
+++ b/core/tests/coretests/src/android/os/PackageTagsListTest.java
@@ -30,6 +30,7 @@
import org.junit.runner.RunWith;
import java.util.Arrays;
+import java.util.Collections;
@Presubmit
@RunWith(AndroidJUnit4.class)
@@ -40,7 +41,8 @@
PackageTagsList.Builder builder = new PackageTagsList.Builder()
.add("package1", "attr1")
.add("package1", "attr2")
- .add("package2");
+ .add("package2")
+ .add("package4", Arrays.asList("attr1", "attr2"));
PackageTagsList list = builder.build();
assertTrue(list.contains(builder.build()));
@@ -49,10 +51,13 @@
assertTrue(list.contains("package2", "attr1"));
assertTrue(list.contains("package2", "attr2"));
assertTrue(list.contains("package2", "attr3"));
+ assertTrue(list.contains("package4", "attr1"));
+ assertTrue(list.contains("package4", "attr2"));
assertTrue(list.containsAll("package2"));
assertTrue(list.includes("package1"));
assertTrue(list.includes("package2"));
assertFalse(list.contains("package1", "attr3"));
+ assertFalse(list.contains("package4", "attr3"));
assertFalse(list.containsAll("package1"));
assertFalse(list.includes("package3"));
@@ -92,6 +97,51 @@
}
@Test
+ public void testPackageTagsList_Remove() {
+ PackageTagsList.Builder builder = new PackageTagsList.Builder()
+ .add("package1", "attr1")
+ .add("package1", "attr2")
+ .add("package2")
+ .add("package4", Arrays.asList("attr1", "attr2", "attr3"))
+ .add("package3", "attr1")
+ .remove("package1", "attr1")
+ .remove("package1", "attr2")
+ .remove("package2", "attr1")
+ .remove("package4", Arrays.asList("attr1", "attr2"))
+ .remove("package3");
+ PackageTagsList list = builder.build();
+
+ assertTrue(list.contains(builder.build()));
+ assertFalse(list.contains("package1", "attr1"));
+ assertFalse(list.contains("package1", "attr2"));
+ assertTrue(list.contains("package2", "attr1"));
+ assertTrue(list.contains("package2", "attr2"));
+ assertTrue(list.contains("package2", "attr3"));
+ assertFalse(list.contains("package3", "attr1"));
+ assertFalse(list.contains("package4", "attr1"));
+ assertFalse(list.contains("package4", "attr2"));
+ assertTrue(list.contains("package4", "attr3"));
+ assertTrue(list.containsAll("package2"));
+ assertFalse(list.includes("package1"));
+ assertTrue(list.includes("package2"));
+ assertFalse(list.includes("package3"));
+ assertTrue(list.includes("package4"));
+ }
+
+ @Test
+ public void testPackageTagsList_EmptyCollections() {
+ PackageTagsList.Builder builder = new PackageTagsList.Builder()
+ .add("package1", Collections.emptyList())
+ .add("package2")
+ .remove("package2", Collections.emptyList());
+ PackageTagsList list = builder.build();
+
+ assertTrue(list.contains(builder.build()));
+ assertFalse(list.contains("package1", "attr1"));
+ assertTrue(list.contains("package2", "attr2"));
+ }
+
+ @Test
public void testWriteToParcel() {
PackageTagsList list = new PackageTagsList.Builder()
.add("package1", "attr1")
diff --git a/core/tests/coretests/src/android/os/VibratorInfoTest.java b/core/tests/coretests/src/android/os/VibratorInfoTest.java
index 8c7d10c..6e07fa2 100644
--- a/core/tests/coretests/src/android/os/VibratorInfoTest.java
+++ b/core/tests/coretests/src/android/os/VibratorInfoTest.java
@@ -87,14 +87,14 @@
public void testIsPrimitiveSupported() {
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
.build();
assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
// Returns false when there is no compose capability.
info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
- .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
.build();
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
}
@@ -103,8 +103,7 @@
public void testGetPrimitiveDuration() {
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
- .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
- .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
.build();
assertEquals(20, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_TICK));
@@ -113,6 +112,26 @@
}
@Test
+ public void testCompositionLimits() {
+ VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+ .setPrimitiveDelayMax(100)
+ .setCompositionSizeMax(10)
+ .setPwlePrimitiveDurationMax(50)
+ .setPwleSizeMax(20)
+ .build();
+ assertEquals(100, info.getPrimitiveDelayMax());
+ assertEquals(10, info.getCompositionSizeMax());
+ assertEquals(50, info.getPwlePrimitiveDurationMax());
+ assertEquals(20, info.getPwleSizeMax());
+
+ VibratorInfo emptyInfo = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
+ assertEquals(0, emptyInfo.getPrimitiveDelayMax());
+ assertEquals(0, emptyInfo.getCompositionSizeMax());
+ assertEquals(0, emptyInfo.getPwlePrimitiveDurationMax());
+ assertEquals(0, emptyInfo.getPwleSizeMax());
+ }
+
+ @Test
public void testGetDefaultBraking_returnsFirstSupportedBraking() {
assertEquals(Braking.NONE, new VibratorInfo.Builder(
TEST_VIBRATOR_ID).build().getDefaultBraking());
@@ -263,8 +282,12 @@
VibratorInfo.Builder completeBuilder = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
- .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
- .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+ .setPrimitiveDelayMax(100)
+ .setCompositionSizeMax(10)
+ .setSupportedBraking(Braking.CLAB)
+ .setPwlePrimitiveDurationMax(50)
+ .setPwleSizeMax(20)
.setQFactor(2f)
.setFrequencyMapping(TEST_FREQUENCY_MAPPING);
VibratorInfo complete = completeBuilder.build();
@@ -279,8 +302,7 @@
assertNotEquals(complete, completeWithComposeControl);
VibratorInfo completeWithNoEffects = completeBuilder
- .setSupportedEffects()
- .setSupportedPrimitives()
+ .setSupportedEffects(new int[0])
.build();
assertNotEquals(complete, completeWithNoEffects);
@@ -289,13 +311,8 @@
.build();
assertNotEquals(complete, completeWithUnknownEffects);
- VibratorInfo completeWithUnknownPrimitives = completeBuilder
- .setSupportedPrimitives(null)
- .build();
- assertNotEquals(complete, completeWithUnknownPrimitives);
-
VibratorInfo completeWithDifferentPrimitiveDuration = completeBuilder
- .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
.build();
assertNotEquals(complete, completeWithDifferentPrimitiveDuration);
@@ -321,12 +338,17 @@
.build();
assertNotEquals(complete, completeWithDifferentQFactor);
- VibratorInfo empty = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
- VibratorInfo emptyWithKnownSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
- .setSupportedEffects()
- .setSupportedPrimitives()
+ VibratorInfo unknownEffectSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
+ VibratorInfo knownEmptyEffectSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+ .setSupportedEffects(new int[0])
.build();
- assertNotEquals(empty, emptyWithKnownSupport);
+ assertNotEquals(unknownEffectSupport, knownEmptyEffectSupport);
+
+ VibratorInfo unknownBrakingSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
+ VibratorInfo knownEmptyBrakingSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
+ .setSupportedBraking(new int[0])
+ .build();
+ assertNotEquals(unknownBrakingSupport, knownEmptyBrakingSupport);
}
@Test
@@ -334,8 +356,7 @@
VibratorInfo original = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
- .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
- .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
.setQFactor(Float.NaN)
.setFrequencyMapping(TEST_FREQUENCY_MAPPING)
.build();
diff --git a/core/tests/coretests/src/android/os/VibratorTest.java b/core/tests/coretests/src/android/os/VibratorTest.java
index 8f9168b..0ece793 100644
--- a/core/tests/coretests/src/android/os/VibratorTest.java
+++ b/core/tests/coretests/src/android/os/VibratorTest.java
@@ -16,6 +16,8 @@
package android.os;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
import static junit.framework.TestCase.assertEquals;
import static org.mockito.ArgumentMatchers.any;
@@ -26,6 +28,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.hardware.vibrator.IVibrator;
import android.media.AudioAttributes;
import android.platform.test.annotations.Presubmit;
@@ -70,6 +73,54 @@
}
@Test
+ public void areEffectsSupported_noVibrator_returnsAlwaysNo() {
+ SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+ new VibratorInfo[0]);
+ assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO,
+ info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
+ }
+
+ @Test
+ public void areEffectsSupported_unsupportedInOneVibrator_returnsNo() {
+ VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+ .build();
+ VibratorInfo unsupportedVibrator = new VibratorInfo.Builder(/* id= */ 2)
+ .setSupportedEffects(new int[0])
+ .build();
+ SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+ new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
+ assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO,
+ info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
+ }
+
+ @Test
+ public void areEffectsSupported_unknownInOneVibrator_returnsUnknown() {
+ VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+ .build();
+ VibratorInfo unknownSupportVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
+ SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+ new VibratorInfo[]{supportedVibrator, unknownSupportVibrator});
+ assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN,
+ info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
+ }
+
+ @Test
+ public void arePrimitivesSupported_supportedInAllVibrators_returnsYes() {
+ VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+ .build();
+ VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
+ .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
+ .build();
+ SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+ new VibratorInfo[]{firstVibrator, secondVibrator});
+ assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_YES,
+ info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
+ }
+
+ @Test
public void arePrimitivesSupported_returnsArrayOfSameSize() {
assertEquals(0, mVibratorSpy.arePrimitivesSupported(new int[0]).length);
assertEquals(1, mVibratorSpy.arePrimitivesSupported(
@@ -80,6 +131,40 @@
}
@Test
+ public void arePrimitivesSupported_noVibrator_returnsAlwaysFalse() {
+ SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+ new VibratorInfo[0]);
+ assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
+ }
+
+ @Test
+ public void arePrimitivesSupported_unsupportedInOneVibrator_returnsFalse() {
+ VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .build();
+ VibratorInfo unsupportedVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
+ SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+ new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
+ assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
+ }
+
+ @Test
+ public void arePrimitivesSupported_supportedInAllVibrators_returnsTrue() {
+ VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 5)
+ .build();
+ VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 15)
+ .build();
+ SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+ new VibratorInfo[]{firstVibrator, secondVibrator});
+ assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
+ }
+
+ @Test
public void getPrimitivesDurations_returnsArrayOfSameSize() {
assertEquals(0, mVibratorSpy.getPrimitiveDurations(new int[0]).length);
assertEquals(1, mVibratorSpy.getPrimitiveDurations(
@@ -90,6 +175,40 @@
}
@Test
+ public void getPrimitivesDurations_noVibrator_returnsAlwaysZero() {
+ SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+ new VibratorInfo[0]);
+ assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
+ }
+
+ @Test
+ public void getPrimitivesDurations_unsupportedInOneVibrator_returnsZero() {
+ VibratorInfo supportedVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .build();
+ VibratorInfo unsupportedVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
+ SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+ new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
+ assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
+ }
+
+ @Test
+ public void getPrimitivesDurations_supportedInAllVibrators_returnsMaxDuration() {
+ VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
+ .build();
+ VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
+ .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
+ .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
+ .build();
+ SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
+ new VibratorInfo[]{firstVibrator, secondVibrator});
+ assertEquals(20, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
+ }
+
+ @Test
public void vibrate_withAudioAttributes_createsVibrationAttributesWithSameUsage() {
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
AudioAttributes audioAttributes = new AudioAttributes.Builder().setUsage(
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 46e2772..90a9572 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -44,6 +44,7 @@
BatteryStatsUidTest.class,
BatteryUsageStatsProviderTest.class,
BatteryUsageStatsTest.class,
+ BatteryUsageStatsStoreTest.class,
BatteryStatsUserLifecycleTests.class,
BluetoothPowerCalculatorTest.class,
BstatsCpuTimesValidationTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
index d83645d..cbd67c8 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsProviderTest.java
@@ -20,10 +20,14 @@
import android.app.ActivityManager;
import android.content.Context;
+import android.os.BatteryConsumer;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.os.Parcel;
import android.os.Process;
import android.os.UidBatteryConsumer;
@@ -36,6 +40,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.File;
import java.util.List;
@SmallTest
@@ -45,7 +50,8 @@
private static final long MINUTE_IN_MS = 60 * 1000;
@Rule
- public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(12345);
+ public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(12345)
+ .setAveragePower(PowerProfile.POWER_FLASHLIGHT, 360.0);
@Test
public void test_getBatteryUsageStats() {
@@ -187,4 +193,84 @@
mStatsRule.setTime(11500, 0);
assertThat(provider.shouldUpdateStats(queries, 10000)).isTrue();
}
+
+ @Test
+ public void testAggregateBatteryStats() {
+ Context context = InstrumentationRegistry.getContext();
+ BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+ mStatsRule.setCurrentTime(5 * MINUTE_IN_MS);
+ batteryStats.resetAllStatsCmdLocked();
+
+ BatteryUsageStatsStore batteryUsageStatsStore = new BatteryUsageStatsStore(context,
+ batteryStats, new File(context.getCacheDir(), "BatteryUsageStatsProviderTest"),
+ new TestHandler(), Integer.MAX_VALUE);
+
+ BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(context,
+ batteryStats, batteryUsageStatsStore);
+
+ batteryStats.noteFlashlightOnLocked(APP_UID,
+ 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS);
+ batteryStats.noteFlashlightOffLocked(APP_UID,
+ 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
+ mStatsRule.setCurrentTime(25 * MINUTE_IN_MS);
+ batteryStats.resetAllStatsCmdLocked();
+
+ batteryStats.noteFlashlightOnLocked(APP_UID,
+ 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS);
+ batteryStats.noteFlashlightOffLocked(APP_UID,
+ 50 * MINUTE_IN_MS, 50 * MINUTE_IN_MS);
+ mStatsRule.setCurrentTime(55 * MINUTE_IN_MS);
+ batteryStats.resetAllStatsCmdLocked();
+
+ // This section should be ignored because the timestamp is out or range
+ batteryStats.noteFlashlightOnLocked(APP_UID,
+ 60 * MINUTE_IN_MS, 60 * MINUTE_IN_MS);
+ batteryStats.noteFlashlightOffLocked(APP_UID,
+ 70 * MINUTE_IN_MS, 70 * MINUTE_IN_MS);
+ mStatsRule.setCurrentTime(75 * MINUTE_IN_MS);
+ batteryStats.resetAllStatsCmdLocked();
+
+ // This section should be ignored because it represents the current stats session
+ batteryStats.noteFlashlightOnLocked(APP_UID,
+ 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
+ batteryStats.noteFlashlightOffLocked(APP_UID,
+ 90 * MINUTE_IN_MS, 90 * MINUTE_IN_MS);
+ mStatsRule.setCurrentTime(95 * MINUTE_IN_MS);
+
+ // Include the first and the second snapshot, but not the third or current
+ BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
+ .aggregateSnapshots(20 * MINUTE_IN_MS, 60 * MINUTE_IN_MS)
+ .build();
+ final BatteryUsageStats stats = provider.getBatteryUsageStats(query);
+
+ assertThat(stats.getStatsStartTimestamp()).isEqualTo(5 * MINUTE_IN_MS);
+ assertThat(stats.getStatsEndTimestamp()).isEqualTo(55 * MINUTE_IN_MS);
+ assertThat(stats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
+ .isWithin(0.0001)
+ .of(180.0); // 360 mA * 0.5 hours
+ assertThat(stats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
+ .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
+ .isEqualTo((10 + 20) * MINUTE_IN_MS);
+ final UidBatteryConsumer uidBatteryConsumer = stats.getUidBatteryConsumers().stream()
+ .filter(uid -> uid.getUid() == APP_UID).findFirst().get();
+ assertThat(uidBatteryConsumer
+ .getConsumedPower(BatteryConsumer.POWER_COMPONENT_FLASHLIGHT))
+ .isWithin(0.1)
+ .of(180.0);
+ }
+
+ private static class TestHandler extends Handler {
+ TestHandler() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ msg.getCallback().run();
+ return true;
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java
new file mode 100644
index 0000000..141a9fa
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.os.BatteryManager;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+public class BatteryUsageStatsStoreTest {
+ private static final long MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES = 2 * 1024;
+
+ private final MockClocks mMockClocks = new MockClocks();
+ private MockBatteryStatsImpl mBatteryStats;
+ private BatteryUsageStatsStore mBatteryUsageStatsStore;
+ private BatteryUsageStatsProvider mBatteryUsageStatsProvider;
+ private File mStoreDirectory;
+
+ @Before
+ public void setup() {
+ mMockClocks.currentTime = 123;
+ mBatteryStats = new MockBatteryStatsImpl(mMockClocks);
+ mBatteryStats.setNoAutoReset(true);
+ mBatteryStats.setPowerProfile(mock(PowerProfile.class));
+
+ Context context = InstrumentationRegistry.getContext();
+
+ mStoreDirectory = new File(context.getCacheDir(), "BatteryUsageStatsStoreTest");
+ clearDirectory(mStoreDirectory);
+
+ mBatteryUsageStatsStore = new BatteryUsageStatsStore(context, mBatteryStats,
+ mStoreDirectory, new TestHandler(), MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES);
+
+ mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mBatteryStats);
+ }
+
+ @Test
+ public void testStoreSnapshot() {
+ mMockClocks.currentTime = 1_600_000;
+
+ prepareBatteryStats();
+ mBatteryStats.resetAllStatsCmdLocked();
+
+ final long[] timestamps = mBatteryUsageStatsStore.listBatteryUsageStatsTimestamps();
+ assertThat(timestamps).hasLength(1);
+ assertThat(timestamps[0]).isEqualTo(1_600_000);
+
+ final BatteryUsageStats batteryUsageStats = mBatteryUsageStatsStore.loadBatteryUsageStats(
+ 1_600_000);
+ assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(123);
+ assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(1_600_000);
+ assertThat(batteryUsageStats.getBatteryCapacity()).isEqualTo(4000);
+ assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(5);
+ assertThat(batteryUsageStats.getAggregateBatteryConsumer(
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE).getConsumedPower())
+ .isEqualTo(600); // (3_600_000 - 3_000_000) / 1000
+ }
+
+ @Test
+ public void testGarbageCollectOldSnapshots() throws Exception {
+ prepareBatteryStats();
+
+ mMockClocks.realtime = 10_000_000;
+ mMockClocks.uptime = 10_000_000;
+ mMockClocks.currentTime = 10_000_000;
+
+ final int snapshotFileSize = getSnapshotFileSize();
+ final int numberOfSnapshots =
+ (int) (MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES / snapshotFileSize);
+ for (int i = 0; i < numberOfSnapshots + 2; i++) {
+ mBatteryStats.resetAllStatsCmdLocked();
+
+ mMockClocks.realtime += 10_000_000;
+ mMockClocks.uptime += 10_000_000;
+ mMockClocks.currentTime += 10_000_000;
+ prepareBatteryStats();
+ }
+
+ final long[] timestamps = mBatteryUsageStatsStore.listBatteryUsageStatsTimestamps();
+ Arrays.sort(timestamps);
+ assertThat(timestamps).hasLength(numberOfSnapshots);
+ // Two snapshots (10_000_000 and 20_000_000) should have been discarded
+ assertThat(timestamps[0]).isEqualTo(30_000_000);
+ assertThat(getDirectorySize(mStoreDirectory))
+ .isAtMost(MAX_BATTERY_STATS_SNAPSHOT_STORAGE_BYTES);
+ }
+
+ @Test
+ public void testSavingStatsdAtomPullTimestamp() {
+ mBatteryUsageStatsStore.setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(1234);
+ assertThat(mBatteryUsageStatsStore.getLastBatteryUsageStatsBeforeResetAtomPullTimestamp())
+ .isEqualTo(1234);
+ mBatteryUsageStatsStore.setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(5478);
+ assertThat(mBatteryUsageStatsStore.getLastBatteryUsageStatsBeforeResetAtomPullTimestamp())
+ .isEqualTo(5478);
+ }
+
+ private void prepareBatteryStats() {
+ mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
+ /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0,
+ mMockClocks.realtime, mMockClocks.uptime, mMockClocks.currentTime);
+ mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING, 100,
+ /* plugType */ 0, 85, 72, 3700, 3_000_000, 4_000_000, 0,
+ mMockClocks.realtime + 500_000, mMockClocks.uptime + 500_000,
+ mMockClocks.currentTime + 500_000);
+ }
+
+ private void clearDirectory(File dir) {
+ if (dir.exists()) {
+ for (File child : dir.listFiles()) {
+ if (child.isDirectory()) {
+ clearDirectory(child);
+ }
+ child.delete();
+ }
+ }
+ }
+
+ private long getDirectorySize(File dir) {
+ long size = 0;
+ if (dir.exists()) {
+ for (File child : dir.listFiles()) {
+ if (child.isDirectory()) {
+ size += getDirectorySize(child);
+ } else {
+ size += child.length();
+ }
+ }
+ }
+ return size;
+ }
+
+ private int getSnapshotFileSize() throws IOException {
+ BatteryUsageStats stats = mBatteryUsageStatsProvider.getBatteryUsageStats(
+ new BatteryUsageStatsQuery.Builder()
+ .setMaxStatsAgeMs(0)
+ .includePowerModels()
+ .build());
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ TypedXmlSerializer serializer = Xml.newBinarySerializer();
+ serializer.setOutput(out, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ stats.writeXml(serializer);
+ serializer.endDocument();
+ return out.toByteArray().length;
+ }
+
+ private static class TestHandler extends Handler {
+ TestHandler() {
+ super(Looper.getMainLooper());
+ }
+
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+ msg.getCallback().run();
+ return true;
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 380b4ae..fedbf7a 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -16,15 +16,25 @@
package com.android.internal.os;
+import static android.os.BatteryConsumer.POWER_MODEL_MEASURED_ENERGY;
+import static android.os.BatteryConsumer.POWER_MODEL_POWER_PROFILE;
+import static android.os.BatteryConsumer.POWER_MODEL_UNDEFINED;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.os.BatteryConsumer;
import android.os.BatteryUsageStats;
import android.os.Parcel;
import android.os.UidBatteryConsumer;
import android.os.UserBatteryConsumer;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -32,50 +42,101 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BatteryUsageStatsTest {
+ private static final int USER_ID = 42;
+ private static final int APP_UID1 = 271;
+ private static final int APP_UID2 = 314;
+
@Test
public void testBuilder() {
- BatteryUsageStats batteryUsageStats = buildBatteryUsageStats().build();
- validateBatteryUsageStats(batteryUsageStats);
+ BatteryUsageStats batteryUsageStats = buildBatteryUsageStats1(true).build();
+ assertBatteryUsageStats1(batteryUsageStats, true);
}
@Test
- public void testParcelability() {
- final BatteryUsageStats outBatteryUsageStats = buildBatteryUsageStats().build();
+ public void testParcelability_smallNumberOfUids() {
+ final BatteryUsageStats outBatteryUsageStats = buildBatteryUsageStats1(true).build();
final Parcel outParcel = Parcel.obtain();
outParcel.writeParcelable(outBatteryUsageStats, 0);
final byte[] bytes = outParcel.marshall();
outParcel.recycle();
+ assertThat(bytes.length).isLessThan(2000);
+
final Parcel inParcel = Parcel.obtain();
inParcel.unmarshall(bytes, 0, bytes.length);
inParcel.setDataPosition(0);
final BatteryUsageStats inBatteryUsageStats =
inParcel.readParcelable(getClass().getClassLoader());
assertThat(inBatteryUsageStats).isNotNull();
- validateBatteryUsageStats(inBatteryUsageStats);
+ assertBatteryUsageStats1(inBatteryUsageStats, true);
}
+ @Test
+ public void testParcelability_largeNumberOfUids() {
+ final BatteryUsageStats.Builder builder =
+ new BatteryUsageStats.Builder(new String[0]);
+
+ // Without the use of a blob, this BatteryUsageStats object would generate a Parcel
+ // larger than 64 Kb
+ final int uidCount = 200;
+ for (int i = 0; i < uidCount; i++) {
+ BatteryStatsImpl.Uid mockUid = mock(BatteryStatsImpl.Uid.class);
+ when(mockUid.getUid()).thenReturn(i);
+ builder.getOrCreateUidBatteryConsumerBuilder(mockUid)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, i * 100);
+ }
+
+ BatteryUsageStats outBatteryUsageStats = builder.build();
+
+ final Parcel parcel = Parcel.obtain();
+ parcel.writeParcelable(outBatteryUsageStats, 0);
+
+ assertThat(parcel.dataSize()).isLessThan(2000);
+
+ // This parcel cannot be marshaled because it contains a file descriptor.
+ // Assuming that parcel marshaling works fine, let's just rewind the parcel.
+ parcel.setDataPosition(0);
+
+ final BatteryUsageStats inBatteryUsageStats =
+ parcel.readParcelable(getClass().getClassLoader());
+ parcel.recycle();
+
+ assertThat(inBatteryUsageStats.getUidBatteryConsumers()).hasSize(uidCount);
+ final Map<Integer, UidBatteryConsumer> consumersByUid =
+ inBatteryUsageStats.getUidBatteryConsumers().stream().collect(
+ Collectors.toMap(UidBatteryConsumer::getUid, c -> c));
+ for (int i = 0; i < uidCount; i++) {
+ final UidBatteryConsumer uidBatteryConsumer = consumersByUid.get(i);
+ assertThat(uidBatteryConsumer).isNotNull();
+ assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(i * 100);
+ }
+ }
@Test
public void testDefaultSessionDuration() {
final BatteryUsageStats stats =
- buildBatteryUsageStats().setStatsDuration(10000).build();
+ buildBatteryUsageStats1(true).setStatsDuration(10000).build();
assertThat(stats.getStatsDuration()).isEqualTo(10000);
}
@Test
public void testDump() {
- final BatteryUsageStats stats = buildBatteryUsageStats().build();
+ final BatteryUsageStats stats = buildBatteryUsageStats1(true).build();
final StringWriter out = new StringWriter();
try (PrintWriter pw = new PrintWriter(out)) {
stats.dump(pw, " ");
@@ -87,7 +148,7 @@
assertThat(dump).contains("actual drain: 1000-2000");
assertThat(dump).contains("cpu: 20100 apps: 10100 duration: 20s 300ms");
assertThat(dump).contains("FOO: 20200 apps: 10200 duration: 20s 400ms");
- assertThat(dump).contains("UID 2000: 1200 ( screen=300 cpu=400 FOO=500 )");
+ assertThat(dump).contains("UID 271: 1200 ( screen=300 cpu=400 FOO=500 )");
assertThat(dump).contains("User 42: 30.0 ( cpu=10.0 FOO=20.0 )");
}
@@ -101,154 +162,297 @@
assertThat(allNames).hasSize(BatteryConsumer.POWER_COMPONENT_COUNT);
}
- private BatteryUsageStats.Builder buildBatteryUsageStats() {
- final MockClocks clocks = new MockClocks();
- final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);
- final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(2000);
+ @Test
+ public void testAdd() {
+ final BatteryUsageStats stats1 = buildBatteryUsageStats1(false).build();
+ final BatteryUsageStats stats2 = buildBatteryUsageStats2(new String[] {"FOO"}).build();
- final BatteryUsageStats.Builder builder =
- new BatteryUsageStats.Builder(new String[]{"FOO"}, true)
- .setBatteryCapacity(4000)
- .setDischargePercentage(20)
- .setDischargedPowerRange(1000, 2000)
- .setStatsStartTimestamp(1000)
- .setStatsEndTimestamp(3000);
- builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid)
- .setPackageWithHighestDrain("foo")
- .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 1000)
- .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, 2000)
- .setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_SCREEN, 300)
- .setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 400)
- .setConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 500)
- .setUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU, 600)
- .setUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 800);
+ final BatteryUsageStats sum =
+ new BatteryUsageStats.Builder(new String[] {"FOO"}, true)
+ .add(stats1)
+ .add(stats2)
+ .build();
- builder.getAggregateBatteryConsumerBuilder(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
- .setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 10100)
- .setConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200)
- .setUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU, 10300)
- .setUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10400);
-
- builder.getAggregateBatteryConsumerBuilder(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
- .setConsumedPower(30000)
- .setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 20100)
- .setConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20200)
- .setUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU, 20300)
- .setUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20400);
-
- builder.getOrCreateUserBatteryConsumerBuilder(42)
- .setConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU, 10)
- .setConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20)
- .setUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU, 30)
- .setUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 40);
-
- return builder;
- }
-
- public void validateBatteryUsageStats(BatteryUsageStats batteryUsageStats) {
- assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(30000);
- assertThat(batteryUsageStats.getBatteryCapacity()).isEqualTo(4000);
- assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(20);
- assertThat(batteryUsageStats.getDischargedPowerRange().getLower()).isEqualTo(1000);
- assertThat(batteryUsageStats.getDischargedPowerRange().getUpper()).isEqualTo(2000);
- assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(1000);
- assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(3000);
- assertThat(batteryUsageStats.getStatsDuration()).isEqualTo(2000);
+ assertBatteryUsageStats(sum, 42345, 50, 2234, 4345, 1000, 5000, 5000);
final List<UidBatteryConsumer> uidBatteryConsumers =
- batteryUsageStats.getUidBatteryConsumers();
+ sum.getUidBatteryConsumers();
for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
- if (uidBatteryConsumer.getUid() == 2000) {
- assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo("foo");
- assertThat(uidBatteryConsumer.getTimeInStateMs(
- UidBatteryConsumer.STATE_FOREGROUND)).isEqualTo(1000);
- assertThat(uidBatteryConsumer.getTimeInStateMs(
- UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(2000);
- assertThat(uidBatteryConsumer.getConsumedPower(
- BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(300);
- assertThat(uidBatteryConsumer.getConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(400);
- assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(500);
- assertThat(uidBatteryConsumer.getUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(600);
- assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(800);
- assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(1200);
- assertThat(uidBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
- assertThat(uidBatteryConsumer.getCustomPowerComponentName(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
+ if (uidBatteryConsumer.getUid() == APP_UID1) {
+ assertUidBatteryConsumer(uidBatteryConsumer, 2124, null,
+ 5321, 7432, 423, POWER_MODEL_POWER_PROFILE, 745, POWER_MODEL_UNDEFINED,
+ 956, 1167, 1478);
+ } else if (uidBatteryConsumer.getUid() == APP_UID2) {
+ assertUidBatteryConsumer(uidBatteryConsumer, 1332, "bar",
+ 1111, 2222, 333, POWER_MODEL_POWER_PROFILE, 444, POWER_MODEL_POWER_PROFILE,
+ 555, 666, 777);
} else {
fail("Unexpected UID " + uidBatteryConsumer.getUid());
}
}
- final BatteryConsumer appsBatteryConsumer = batteryUsageStats.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
- assertThat(appsBatteryConsumer.getConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10100);
- assertThat(appsBatteryConsumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(10200);
- assertThat(appsBatteryConsumer.getUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10300);
- assertThat(appsBatteryConsumer.getUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(10400);
- assertThat(appsBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
- assertThat(appsBatteryConsumer.getCustomPowerComponentName(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
+ assertAggregateBatteryConsumer(sum,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+ 20223, 20434, 20645, 20856);
- final BatteryConsumer deviceBatteryConsumer = batteryUsageStats.getAggregateBatteryConsumer(
- BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
- assertThat(deviceBatteryConsumer.getConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(20100);
- assertThat(deviceBatteryConsumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(20200);
- assertThat(deviceBatteryConsumer.getUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(20300);
- assertThat(deviceBatteryConsumer.getUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(20400);
- assertThat(deviceBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
- assertThat(deviceBatteryConsumer.getCustomPowerComponentName(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
+ assertAggregateBatteryConsumer(sum,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+ 40211, 40422, 40633, 40844);
+ }
+
+ @Test
+ public void testAdd_customComponentMismatch() {
+ final BatteryUsageStats.Builder builder =
+ new BatteryUsageStats.Builder(new String[] {"FOO"}, true);
+ final BatteryUsageStats stats = buildBatteryUsageStats2(new String[] {"BAR"}).build();
+
+ assertThrows(IllegalArgumentException.class, () -> builder.add(stats));
+ }
+
+ @Test
+ public void testXml() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ TypedXmlSerializer serializer = Xml.newBinarySerializer();
+ serializer.setOutput(out, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ final BatteryUsageStats stats = buildBatteryUsageStats1(true).build();
+ stats.writeXml(serializer);
+ serializer.endDocument();
+
+ ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+ TypedXmlPullParser parser = Xml.newBinaryPullParser();
+ parser.setInput(in, StandardCharsets.UTF_8.name());
+ final BatteryUsageStats fromXml = BatteryUsageStats.createFromXml(parser);
+
+ assertBatteryUsageStats1(fromXml, true);
+ }
+
+ private BatteryUsageStats.Builder buildBatteryUsageStats1(boolean includeUserBatteryConsumer) {
+ final MockClocks clocks = new MockClocks();
+ final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);
+
+ final BatteryUsageStats.Builder builder =
+ new BatteryUsageStats.Builder(new String[] {"FOO"}, true)
+ .setBatteryCapacity(4000)
+ .setDischargePercentage(20)
+ .setDischargedPowerRange(1000, 2000)
+ .setStatsStartTimestamp(1000)
+ .setStatsEndTimestamp(3000);
+
+ addUidBatteryConsumer(builder, batteryStats, APP_UID1, "foo",
+ 1000, 2000,
+ 300, POWER_MODEL_POWER_PROFILE, 400, POWER_MODEL_POWER_PROFILE, 500, 600, 800);
+
+ addAggregateBatteryConsumer(builder,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS, 0,
+ 10100, 10200, 10300, 10400);
+
+ addAggregateBatteryConsumer(builder,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE, 30000,
+ 20100, 20200, 20300, 20400);
+
+
+ if (includeUserBatteryConsumer) {
+ builder.getOrCreateUserBatteryConsumerBuilder(USER_ID)
+ .setConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU, 10)
+ .setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20)
+ .setUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU, 30)
+ .setUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 40);
+ }
+ return builder;
+ }
+
+ private BatteryUsageStats.Builder buildBatteryUsageStats2(String[] customPowerComponentNames) {
+ final MockClocks clocks = new MockClocks();
+ final MockBatteryStatsImpl batteryStats = new MockBatteryStatsImpl(clocks);
+
+ final BatteryUsageStats.Builder builder =
+ new BatteryUsageStats.Builder(customPowerComponentNames, true)
+ .setDischargePercentage(30)
+ .setDischargedPowerRange(1234, 2345)
+ .setStatsStartTimestamp(2000)
+ .setStatsEndTimestamp(5000);
+
+ addUidBatteryConsumer(builder, batteryStats, APP_UID1, null,
+ 4321, 5432,
+ 123, POWER_MODEL_POWER_PROFILE, 345, POWER_MODEL_MEASURED_ENERGY, 456, 567, 678);
+
+ addUidBatteryConsumer(builder, batteryStats, APP_UID2, "bar",
+ 1111, 2222,
+ 333, POWER_MODEL_POWER_PROFILE, 444, POWER_MODEL_POWER_PROFILE, 555, 666, 777);
+
+ addAggregateBatteryConsumer(builder,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS, 0,
+ 10123, 10234, 10345, 10456);
+
+ addAggregateBatteryConsumer(builder,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE, 12345,
+ 20111, 20222, 20333, 20444);
+
+ return builder;
+ }
+
+ private void addUidBatteryConsumer(BatteryUsageStats.Builder builder,
+ MockBatteryStatsImpl batteryStats, int uid, String packageWithHighestDrain,
+ int timeInStateForeground, int timeInStateBackground, int screenPower,
+ int screenPowerModel, int cpuPower, int cpuPowerModel, int customComponentPower,
+ int cpuDuration, int customComponentDuration) {
+ final BatteryStatsImpl.Uid batteryStatsUid = batteryStats.getUidStatsLocked(uid);
+ builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid)
+ .setPackageWithHighestDrain(packageWithHighestDrain)
+ .setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, timeInStateForeground)
+ .setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, timeInStateBackground)
+ .setConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_SCREEN, screenPower, screenPowerModel)
+ .setConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU, cpuPower, cpuPowerModel)
+ .setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentPower)
+ .setUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU, cpuDuration)
+ .setUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentDuration);
+ }
+
+ private void addAggregateBatteryConsumer(BatteryUsageStats.Builder builder, int scope,
+ double consumedPower, int cpuPower, int customComponentPower, int cpuDuration,
+ int customComponentDuration) {
+ builder.getAggregateBatteryConsumerBuilder(scope)
+ .setConsumedPower(consumedPower)
+ .setConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU, cpuPower)
+ .setConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentPower)
+ .setUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU, cpuDuration)
+ .setUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, customComponentDuration);
+ }
+
+ public void assertBatteryUsageStats1(BatteryUsageStats batteryUsageStats,
+ boolean includesUserBatteryConsumers) {
+ assertBatteryUsageStats(batteryUsageStats, 30000, 20, 1000, 2000, 1000, 3000, 2000);
+
+ final List<UidBatteryConsumer> uidBatteryConsumers =
+ batteryUsageStats.getUidBatteryConsumers();
+ assertThat(uidBatteryConsumers).hasSize(1);
+ for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
+ if (uidBatteryConsumer.getUid() == APP_UID1) {
+ assertUidBatteryConsumer(uidBatteryConsumer, 1200, "foo",
+ 1000, 2000, 300, POWER_MODEL_POWER_PROFILE, 400, POWER_MODEL_POWER_PROFILE,
+ 500, 600, 800);
+ } else {
+ fail("Unexpected UID " + uidBatteryConsumer.getUid());
+ }
+ }
final List<UserBatteryConsumer> userBatteryConsumers =
batteryUsageStats.getUserBatteryConsumers();
- for (UserBatteryConsumer userBatteryConsumer : userBatteryConsumers) {
- if (userBatteryConsumer.getUserId() == 42) {
- assertThat(userBatteryConsumer.getConsumedPower(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10);
- assertThat(userBatteryConsumer.getConsumedPowerForCustomComponent(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(20);
- assertThat(userBatteryConsumer.getUsageDurationMillis(
- BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(30);
- assertThat(userBatteryConsumer.getUsageDurationForCustomComponentMillis(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(40);
- assertThat(userBatteryConsumer.getConsumedPower()).isEqualTo(30);
- assertThat(userBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
- assertThat(userBatteryConsumer.getCustomPowerComponentName(
- BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
- } else {
- fail("Unexpected user ID " + userBatteryConsumer.getUserId());
+ if (includesUserBatteryConsumers) {
+ assertThat(userBatteryConsumers).hasSize(1);
+ for (UserBatteryConsumer userBatteryConsumer : userBatteryConsumers) {
+ if (userBatteryConsumer.getUserId() == USER_ID) {
+ assertUserBatteryConsumer(userBatteryConsumer, 42, 10, 20, 30, 40);
+ } else {
+ fail("Unexpected User ID " + userBatteryConsumer.getUserId());
+ }
}
+ } else {
+ assertThat(userBatteryConsumers).isEmpty();
}
+
+ assertAggregateBatteryConsumer(batteryUsageStats,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS,
+ 10100, 10200, 10300, 10400);
+
+ assertAggregateBatteryConsumer(batteryUsageStats,
+ BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE,
+ 20100, 20200, 20300, 20400);
+ }
+
+ private void assertBatteryUsageStats(BatteryUsageStats batteryUsageStats, int consumedPower,
+ int dischargePercentage, int dischagePowerLower, int dischargePowerUpper,
+ int statsStartTimestamp, int statsEndTimestamp, int statsDuration) {
+ assertThat(batteryUsageStats.getConsumedPower()).isEqualTo(consumedPower);
+ assertThat(batteryUsageStats.getDischargePercentage()).isEqualTo(dischargePercentage);
+ assertThat(batteryUsageStats.getDischargedPowerRange().getLower()).isEqualTo(
+ dischagePowerLower);
+ assertThat(batteryUsageStats.getDischargedPowerRange().getUpper()).isEqualTo(
+ dischargePowerUpper);
+ assertThat(batteryUsageStats.getStatsStartTimestamp()).isEqualTo(statsStartTimestamp);
+ assertThat(batteryUsageStats.getStatsEndTimestamp()).isEqualTo(statsEndTimestamp);
+ assertThat(batteryUsageStats.getStatsDuration()).isEqualTo(statsDuration);
+ }
+
+ private void assertUidBatteryConsumer(UidBatteryConsumer uidBatteryConsumer,
+ int consumedPower, String packageWithHighestDrain, int timeInStateForeground,
+ int timeInStateBackground, int screenPower, int screenPowerModel, int cpuPower,
+ int cpuPowerModel, int customComponentPower, int cpuDuration,
+ int customComponentDuration) {
+ assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(consumedPower);
+ assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo(
+ packageWithHighestDrain);
+ assertThat(uidBatteryConsumer.getTimeInStateMs(
+ UidBatteryConsumer.STATE_FOREGROUND)).isEqualTo(timeInStateForeground);
+ assertThat(uidBatteryConsumer.getTimeInStateMs(
+ UidBatteryConsumer.STATE_BACKGROUND)).isEqualTo(timeInStateBackground);
+ assertThat(uidBatteryConsumer.getConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(screenPower);
+ assertThat(uidBatteryConsumer.getPowerModel(
+ BatteryConsumer.POWER_COMPONENT_SCREEN)).isEqualTo(screenPowerModel);
+ assertThat(uidBatteryConsumer.getConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPower);
+ assertThat(uidBatteryConsumer.getPowerModel(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPowerModel);
+ assertThat(uidBatteryConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(customComponentPower);
+ assertThat(uidBatteryConsumer.getUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuDuration);
+ assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(
+ customComponentDuration);
+ assertThat(uidBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
+ assertThat(uidBatteryConsumer.getCustomPowerComponentName(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
+ }
+
+ private void assertUserBatteryConsumer(UserBatteryConsumer userBatteryConsumer,
+ int userId, int cpuPower, int customComponentPower,
+ int cpuDuration, int customComponentDuration) {
+ assertThat(userBatteryConsumer.getConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPower);
+ assertThat(userBatteryConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(customComponentPower);
+ assertThat(userBatteryConsumer.getUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuDuration);
+ assertThat(userBatteryConsumer.getUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(
+ customComponentDuration);
+ assertThat(userBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
+ assertThat(userBatteryConsumer.getCustomPowerComponentName(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
+ }
+
+ private void assertAggregateBatteryConsumer(BatteryUsageStats batteryUsageStats,
+ int aggregateBatteryConsumerScopeAllApps, int cpuPower, int customComponentPower,
+ int cpuDuration, int customComponentDuration) {
+ final BatteryConsumer appsBatteryConsumer = batteryUsageStats.getAggregateBatteryConsumer(
+ aggregateBatteryConsumerScopeAllApps);
+ assertThat(appsBatteryConsumer.getConsumedPower(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuPower);
+ assertThat(appsBatteryConsumer.getConsumedPowerForCustomComponent(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(customComponentPower);
+ assertThat(appsBatteryConsumer.getUsageDurationMillis(
+ BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(cpuDuration);
+ assertThat(appsBatteryConsumer.getUsageDurationForCustomComponentMillis(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo(
+ customComponentDuration);
+ assertThat(appsBatteryConsumer.getCustomPowerComponentCount()).isEqualTo(1);
+ assertThat(appsBatteryConsumer.getCustomPowerComponentName(
+ BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID)).isEqualTo("FOO");
}
}
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 55943a0..82b2bf4 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -969,11 +970,61 @@
}
@Test
+ public void testLatencyCollectionActiveEvenWithoutDeviceState() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats(null);
+ bcs.setCollectLatencyData(true);
+
+ Binder binder = new Binder();
+ CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+ assertNotEquals(null, callSession);
+
+ bcs.time += 10;
+ bcs.elapsedTime += 20;
+ bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+
+ assertEquals(1, bcs.getLatencyObserver().getLatencyHistograms().size());
+ }
+
+ @Test
public void testLatencyCollectionEnabledByDefault() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
assertEquals(true, bcs.getCollectLatencyData());
}
+ @Test
+ public void testProcessSource() {
+ BinderCallsStats defaultCallsStats = new BinderCallsStats(
+ new BinderCallsStats.Injector());
+
+ BinderCallsStats systemServerCallsStats = new BinderCallsStats(
+ new BinderCallsStats.Injector(),
+ com.android.internal.os.BinderLatencyProto.Dims.SYSTEM_SERVER);
+
+ BinderCallsStats telephonyCallsStats = new BinderCallsStats(
+ new BinderCallsStats.Injector(),
+ com.android.internal.os.BinderLatencyProto.Dims.TELEPHONY);
+
+ BinderCallsStats bluetoothCallsStats = new BinderCallsStats(
+ new BinderCallsStats.Injector(),
+ com.android.internal.os.BinderLatencyProto.Dims.BLUETOOTH);
+
+ assertEquals(
+ com.android.internal.os.BinderLatencyProto.Dims.SYSTEM_SERVER,
+ defaultCallsStats.getLatencyObserver().getProcessSource());
+
+ assertEquals(
+ com.android.internal.os.BinderLatencyProto.Dims.SYSTEM_SERVER,
+ systemServerCallsStats.getLatencyObserver().getProcessSource());
+
+ assertEquals(
+ com.android.internal.os.BinderLatencyProto.Dims.TELEPHONY,
+ telephonyCallsStats.getLatencyObserver().getProcessSource());
+
+ assertEquals(
+ com.android.internal.os.BinderLatencyProto.Dims.BLUETOOTH,
+ bluetoothCallsStats.getLatencyObserver().getProcessSource());
+ }
+
private static class TestHandler extends Handler {
ArrayList<Runnable> mRunnables = new ArrayList<>();
diff --git a/data/etc/car/com.android.car.cluster.home.xml b/data/etc/car/com.android.car.cluster.home.xml
index e1d2b18..a3d0fcf 100644
--- a/data/etc/car/com.android.car.cluster.home.xml
+++ b/data/etc/car/com.android.car.cluster.home.xml
@@ -18,5 +18,6 @@
<privapp-permissions package="com.android.car.cluster.home">
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"/>
+ <permission name="android.car.permission.CAR_MONITOR_INPUT"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/car/com.android.car.rotary.xml b/data/etc/car/com.android.car.rotary.xml
index eddef1a..a39b244 100644
--- a/data/etc/car/com.android.car.rotary.xml
+++ b/data/etc/car/com.android.car.rotary.xml
@@ -18,6 +18,7 @@
<privapp-permissions package="com.android.car.rotary">
<permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <permission name="android.car.permission.CAR_MONITOR_INPUT"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 04291e3..12eb50e 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -428,6 +428,8 @@
<!-- Permissions required for CTS test - TVInputManagerTest -->
<permission name="android.permission.ACCESS_TUNED_INFO" />
<permission name="android.permission.TV_INPUT_HARDWARE" />
+ <permission name="com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS" />
+ <permission name="com.android.providers.tv.permission.WRITE_EPG_DATA"/>
<!-- Permission required for CTS test - PrivilegedLocationPermissionTest -->
<permission name="android.permission.LOCATION_HARDWARE" />
<!-- Permissions required for GTS test - GtsDialerAudioTestCases -->
diff --git a/data/keyboards/Vendor_0a5c_Product_8502.kl b/data/keyboards/idroid_con.kl
similarity index 100%
rename from data/keyboards/Vendor_0a5c_Product_8502.kl
rename to data/keyboards/idroid_con.kl
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 4534d36..6c1c2ee 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -131,4 +131,12 @@
nativeMergeWithNextTransaction(mNativeObject, t.mNativeObject, frameNumber);
}
+ /**
+ * Merge the transaction passed in to the next transaction in BlastBufferQueue.
+ * @param nativeTransaction native handle passed from native c/c++ code.
+ */
+ public void mergeWithNextTransaction(long nativeTransaction, long frameNumber) {
+ nativeMergeWithNextTransaction(mNativeObject, nativeTransaction, frameNumber);
+ }
+
}
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 954d062..30d1e0f 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -753,11 +753,25 @@
nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
}
+ private ASurfaceTransactionCallback mASurfaceTransactionCallback;
+
/** @hide */
public void setASurfaceTransactionCallback(ASurfaceTransactionCallback callback) {
+ // ensure callback is kept alive on the java side since weak ref is used in native code
+ mASurfaceTransactionCallback = callback;
nSetASurfaceTransactionCallback(mNativeProxy, callback);
}
+ private PrepareSurfaceControlForWebviewCallback mAPrepareSurfaceControlForWebviewCallback;
+
+ /** @hide */
+ public void setPrepareSurfaceControlForWebviewCallback(
+ PrepareSurfaceControlForWebviewCallback callback) {
+ // ensure callback is kept alive on the java side since weak ref is used in native code
+ mAPrepareSurfaceControlForWebviewCallback = callback;
+ nSetPrepareSurfaceControlForWebviewCallback(mNativeProxy, callback);
+ }
+
/** @hide */
public void setFrameCallback(FrameDrawingCallback callback) {
nSetFrameCallback(mNativeProxy, callback);
@@ -872,6 +886,19 @@
session.close();
}
+ /**
+ * Interface used to receive callbacks when Webview requests a surface control.
+ *
+ * @hide
+ */
+ public interface PrepareSurfaceControlForWebviewCallback {
+ /**
+ * Invoked when Webview calls to get a surface control.
+ *
+ */
+ void prepare();
+ }
+
/**
* Interface used to receive callbacks when a transaction needs to be merged.
*
@@ -885,7 +912,7 @@
* @param aSurfaceControlNativeObj ASurfaceControl native object handle
* @param frame The id of the frame being drawn.
*/
- void onMergeTransaction(long aSurfaceTranactionNativeObj,
+ boolean onMergeTransaction(long aSurfaceTranactionNativeObj,
long aSurfaceControlNativeObj, long frame);
}
@@ -1370,6 +1397,9 @@
private static native void nSetASurfaceTransactionCallback(long nativeProxy,
ASurfaceTransactionCallback callback);
+ private static native void nSetPrepareSurfaceControlForWebviewCallback(long nativeProxy,
+ PrepareSurfaceControlForWebviewCallback callback);
+
private static native void nSetFrameCallback(long nativeProxy, FrameDrawingCallback callback);
private static native void nSetFrameCompleteCallback(long nativeProxy,
diff --git a/graphics/java/android/graphics/drawable/RippleAnimationSession.java b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
index 55fb83c..74fb618 100644
--- a/graphics/java/android/graphics/drawable/RippleAnimationSession.java
+++ b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
@@ -60,6 +60,10 @@
mForceSoftware = forceSoftware;
}
+ boolean isForceSoftware() {
+ return mForceSoftware;
+ }
+
@NonNull RippleAnimationSession enter(Canvas canvas) {
mStartTime = AnimationUtils.currentAnimationTimeMillis();
if (isHwAccelerated(canvas)) {
@@ -130,7 +134,6 @@
return this;
}
-
private void exitHardware(RecordingCanvas canvas) {
AnimationProperties<CanvasProperty<Float>, CanvasProperty<Paint>>
props = getCanvasProperties();
@@ -199,6 +202,15 @@
startAnimation(expand, loop);
}
+ void setRadius(float radius) {
+ mProperties.setRadius(radius);
+ mProperties.getShader().setRadius(radius);
+ if (mCanvasProperties != null) {
+ mCanvasProperties.setRadius(CanvasProperty.createFloat(radius));
+ mCanvasProperties.getShader().setRadius(radius);
+ }
+ }
+
@NonNull AnimationProperties<Float, Paint> getProperties() {
return mProperties;
}
@@ -249,7 +261,7 @@
static class AnimationProperties<FloatType, PaintType> {
private final FloatType mProgress;
- private final FloatType mMaxRadius;
+ private FloatType mMaxRadius;
private final FloatType mNoisePhase;
private final PaintType mPaint;
private final RippleShader mShader;
@@ -273,6 +285,10 @@
return mProgress;
}
+ void setRadius(FloatType radius) {
+ mMaxRadius = radius;
+ }
+
void setOrigin(FloatType x, FloatType y) {
mX = x;
mY = y;
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index fe80b58..b994ad2 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -184,7 +184,6 @@
private PorterDuffColorFilter mMaskColorFilter;
private PorterDuffColorFilter mFocusColorFilter;
private boolean mHasValidMask;
- private int mComputedRadius = -1;
/** The current ripple. May be actively animating or pending entry. */
private RippleForeground mRipple;
@@ -221,6 +220,7 @@
private boolean mForceSoftware;
// Patterned
+ private boolean mAddRipple = false;
private float mTargetBackgroundOpacity;
private ValueAnimator mBackgroundAnimation;
private float mBackgroundOpacity;
@@ -389,8 +389,6 @@
if (mRipple != null) {
mRipple.onBoundsChange();
}
-
- mComputedRadius = Math.round(computeRadius());
invalidateSelf();
}
@@ -716,6 +714,7 @@
}
cancelExitingRipples();
+ exitPatternedAnimation();
}
@Override
@@ -732,7 +731,7 @@
}
/**
- * Notifies all the animating ripples that the hotspot bounds have changed.
+ * Notifies all the animating ripples that the hotspot bounds have changed and modify sessions.
*/
private void onHotspotBoundsChanged() {
final int count = mExitingRipplesCount;
@@ -748,6 +747,20 @@
if (mBackground != null) {
mBackground.onHotspotBoundsChanged();
}
+ float newRadius = Math.round(getComputedRadius());
+ for (int i = 0; i < mRunningAnimations.size(); i++) {
+ RippleAnimationSession s = mRunningAnimations.get(i);
+ s.setRadius(newRadius);
+ s.getProperties().getShader()
+ .setResolution(mHotspotBounds.width(), mHotspotBounds.height());
+ float cx = mHotspotBounds.centerX(), cy = mHotspotBounds.centerY();
+ s.getProperties().getShader().setOrigin(cx, cy);
+ s.getProperties().setOrigin(cx, cy);
+ if (!s.isForceSoftware()) {
+ s.getCanvasProperties()
+ .setOrigin(CanvasProperty.createFloat(cx), CanvasProperty.createFloat(cy));
+ }
+ }
}
/**
@@ -807,7 +820,7 @@
}
private void startPatternedAnimation() {
- mRippleActive = true;
+ mAddRipple = true;
invalidateSelf(false);
}
@@ -838,41 +851,36 @@
}
private void drawPatterned(@NonNull Canvas canvas) {
- final Rect bounds = getDirtyBounds();
+ final Rect bounds = mHotspotBounds;
final int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
boolean useCanvasProps = shouldUseCanvasProps(canvas);
- boolean changedHotspotBounds = !bounds.equals(mHotspotBounds);
if (isBounded()) {
- canvas.clipRect(bounds);
+ canvas.clipRect(getDirtyBounds());
}
final float x, y, cx, cy, w, h;
- if (changedHotspotBounds) {
- x = mHotspotBounds.exactCenterX();
- y = mHotspotBounds.exactCenterY();
- cx = x;
- cy = y;
- h = mHotspotBounds.height();
- w = mHotspotBounds.width();
- useCanvasProps = false;
- } else {
- x = mPendingX;
- y = mPendingY;
- cx = bounds.centerX();
- cy = bounds.centerY();
- h = bounds.height();
- w = bounds.width();
- }
- boolean shouldAnimate = mRippleActive;
+ boolean addRipple = mAddRipple;
+ cx = bounds.centerX();
+ cy = bounds.centerY();
boolean shouldExit = mExitingAnimation;
- mRippleActive = false;
mExitingAnimation = false;
- if (mRunningAnimations.size() > 0 && !shouldAnimate) {
+ mAddRipple = false;
+ if (mRunningAnimations.size() > 0 && !addRipple) {
// update paint when view is invalidated
getRipplePaint();
}
drawContent(canvas);
drawPatternedBackground(canvas, cx, cy);
- if (shouldAnimate && mRunningAnimations.size() <= MAX_RIPPLES) {
+ if (addRipple && mRunningAnimations.size() <= MAX_RIPPLES) {
+ if (mHasPending) {
+ x = mPendingX;
+ y = mPendingY;
+ mHasPending = false;
+ } else {
+ x = bounds.exactCenterX();
+ y = bounds.exactCenterY();
+ }
+ h = bounds.height();
+ w = bounds.width();
RippleAnimationSession.AnimationProperties<Float, Paint> properties =
createAnimationProperties(x, y, cx, cy, w, h);
mRunningAnimations.add(new RippleAnimationSession(properties, !useCanvasProps)
@@ -896,33 +904,13 @@
CanvasProperty<Paint>>
p = s.getCanvasProperties();
RecordingCanvas can = (RecordingCanvas) canvas;
- CanvasProperty<Float> xProp, yProp;
- if (changedHotspotBounds) {
- xProp = CanvasProperty.createFloat(x);
- yProp = CanvasProperty.createFloat(y);
- p.getShader().setTouch(x, y);
- p.getShader().setOrigin(x, y);
- } else {
- xProp = p.getX();
- yProp = p.getY();
- }
- can.drawRipple(xProp, yProp, p.getMaxRadius(), p.getPaint(),
+ can.drawRipple(p.getX(), p.getY(), p.getMaxRadius(), p.getPaint(),
p.getProgress(), p.getNoisePhase(), p.getColor(), p.getShader());
} else {
RippleAnimationSession.AnimationProperties<Float, Paint> p =
s.getProperties();
- float xProp, yProp;
- if (changedHotspotBounds) {
- xProp = x;
- yProp = y;
- p.getShader().setTouch(x, y);
- p.getShader().setOrigin(x, y);
- } else {
- xProp = p.getX();
- yProp = p.getY();
- }
float radius = p.getMaxRadius();
- canvas.drawCircle(xProp, yProp, radius, p.getPaint());
+ canvas.drawCircle(p.getX(), p.getY(), radius, p.getPaint());
}
}
canvas.restoreToCount(saveCount);
@@ -948,14 +936,13 @@
}
private float computeRadius() {
- Rect b = getDirtyBounds();
- float radius = (float) Math.sqrt(b.width() * b.width() + b.height() * b.height()) / 2;
- return radius;
+ final float halfWidth = mHotspotBounds.width() / 2.0f;
+ final float halfHeight = mHotspotBounds.height() / 2.0f;
+ return (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight);
}
private int getComputedRadius() {
if (mState.mMaxRadius >= 0) return mState.mMaxRadius;
- if (mComputedRadius >= 0) return mComputedRadius;
return (int) computeRadius();
}
@@ -1109,6 +1096,11 @@
if (mState.mRippleStyle == STYLE_SOLID) {
mMaskCanvas.translate(left, top);
}
+ if (mState.mRippleStyle == STYLE_PATTERNED) {
+ for (int i = 0; i < mRunningAnimations.size(); i++) {
+ mRunningAnimations.get(i).getProperties().getShader().setShader(mMaskShader);
+ }
+ }
}
private int getMaskType() {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 89d2b74..72a145f 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -151,7 +151,7 @@
* Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto
* primitive.
*
- * <p>The following primitives are supported: {@link Cipher} and {@link Mac}.
+ * <p>The following primitives are supported: {@link Cipher}, {@link Signature} and {@link Mac}.
*
* @return KeyStore operation handle or {@code 0} if the provided primitive's KeyStore operation
* is not in progress.
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 4c2863e..8cea869 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -40,7 +40,7 @@
<integer name="long_press_dock_anim_duration">250</integer>
<!-- Animation duration for translating of one handed when trigger / dismiss. -->
- <integer name="config_one_handed_translate_animation_duration">800</integer>
+ <integer name="config_one_handed_translate_animation_duration">600</integer>
<!-- One handed mode default offset % of display size -->
<fraction name="config_one_handed_offset">40%</fraction>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 70f03d2..f28ee82 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -117,7 +117,9 @@
<!-- This should be at least the size of bubble_expanded_view_padding; it is used to include
a slight touch slop around the expanded view. -->
<dimen name="bubble_expanded_view_slop">8dp</dimen>
- <!-- Default (and minimum) height of the expanded view shown when the bubble is expanded -->
+ <!-- Default (and minimum) height of the expanded view shown when the bubble is expanded.
+ If this value changes then R.dimen.bubble_expanded_view_min_height in CtsVerifier
+ should also be updated. -->
<dimen name="bubble_expanded_default_height">180dp</dimen>
<!-- On large screens the width of the expanded view is restricted to this size. -->
<dimen name="bubble_expanded_view_tablet_width">412dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index afb40d1..9d65d28 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -284,6 +284,15 @@
return mTitle;
}
+ /**
+ * @return the ShortcutInfo id if it exists, or the metadata shortcut id otherwise.
+ */
+ String getShortcutId() {
+ return getShortcutInfo() != null
+ ? getShortcutInfo().getId()
+ : getMetadataShortcutId();
+ }
+
String getMetadataShortcutId() {
return mMetadataShortcutId;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index dfd878f..09fcb86e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -93,6 +93,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -265,15 +266,7 @@
public void initialize() {
mBubbleData.setListener(mBubbleDataListener);
- mBubbleData.setSuppressionChangedListener(bubble -> {
- // Make sure NoMan knows suppression state so that anyone querying it can tell.
- try {
- mBarService.onBubbleNotificationSuppressionChanged(bubble.getKey(),
- !bubble.showInShade(), bubble.isSuppressed());
- } catch (RemoteException e) {
- // Bad things have happened
- }
- });
+ mBubbleData.setSuppressionChangedListener(this::onBubbleNotificationSuppressionChanged);
mBubbleData.setPendingIntentCancelledListener(bubble -> {
if (bubble.getBubbleIntent() == null) {
@@ -401,6 +394,11 @@
return mImpl;
}
+ @VisibleForTesting
+ public BubblesImpl.CachedState getImplCachedState() {
+ return mImpl.mCachedState;
+ }
+
public ShellExecutor getMainExecutor() {
return mMainExecutor;
}
@@ -500,6 +498,18 @@
updateStack();
}
+ @VisibleForTesting
+ public void onBubbleNotificationSuppressionChanged(Bubble bubble) {
+ // Make sure NoMan knows suppression state so that anyone querying it can tell.
+ try {
+ mBarService.onBubbleNotificationSuppressionChanged(bubble.getKey(),
+ !bubble.showInShade(), bubble.isSuppressed());
+ } catch (RemoteException e) {
+ // Bad things have happened
+ }
+ mImpl.mCachedState.updateBubbleSuppressedState(bubble);
+ }
+
/** Called when the current user changes. */
@VisibleForTesting
public void onUserChanged(int newUserId) {
@@ -694,7 +704,7 @@
mSysuiProxy.getShouldRestoredEntries(savedBubbleKeys, (entries) -> {
mMainExecutor.execute(() -> {
for (BubbleEntry e : entries) {
- if (canLaunchInActivityView(mContext, e)) {
+ if (canLaunchInTaskView(mContext, e)) {
updateBubble(e, true /* suppressFlyout */, false /* showInShade */);
}
}
@@ -808,11 +818,6 @@
}
}
- private boolean isBubbleExpanded(String key) {
- return isStackExpanded() && mBubbleData != null && mBubbleData.getSelectedBubble() != null
- && mBubbleData.getSelectedBubble().getKey().equals(key);
- }
-
/** Promote the provided bubble from the overflow view. */
public void promoteBubbleFromOverflow(Bubble bubble) {
mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK);
@@ -957,14 +962,14 @@
}
private void onEntryAdded(BubbleEntry entry) {
- if (canLaunchInActivityView(mContext, entry)) {
+ if (canLaunchInTaskView(mContext, entry)) {
updateBubble(entry);
}
}
private void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
// shouldBubbleUp checks canBubble & for bubble metadata
- boolean shouldBubble = shouldBubbleUp && canLaunchInActivityView(mContext, entry);
+ boolean shouldBubble = shouldBubbleUp && canLaunchInTaskView(mContext, entry);
if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
// It was previously a bubble but no longer a bubble -- lets remove it
removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE);
@@ -1191,6 +1196,9 @@
mSysuiProxy.notifyInvalidateNotifications("BubbleData.Listener.applyUpdate");
updateStack();
+
+ // Update the cached state for queries from SysUI
+ mImpl.mCachedState.update(update);
}
};
@@ -1295,15 +1303,16 @@
}
/**
- * Whether an intent is properly configured to display in an {@link android.app.ActivityView}.
+ * Whether an intent is properly configured to display in a
+ * {@link com.android.wm.shell.TaskView}.
*
- * Keep checks in sync with NotificationManagerService#canLaunchInActivityView. Typically
+ * Keep checks in sync with BubbleExtractor#canLaunchInTaskView. Typically
* that should filter out any invalid bubbles, but should protect SysUI side just in case.
*
* @param context the context to use.
* @param entry the entry to bubble.
*/
- static boolean canLaunchInActivityView(Context context, BubbleEntry entry) {
+ static boolean canLaunchInTaskView(Context context, BubbleEntry entry) {
PendingIntent intent = entry.getBubbleMetadata() != null
? entry.getBubbleMetadata().getIntent()
: null;
@@ -1364,25 +1373,124 @@
}
private class BubblesImpl implements Bubbles {
+ // Up-to-date cached state of bubbles data for SysUI to query from the calling thread
+ @VisibleForTesting
+ public class CachedState {
+ private boolean mIsStackExpanded;
+ private String mSelectedBubbleKey;
+ private HashSet<String> mSuppressedBubbleKeys = new HashSet<>();
+ private HashMap<String, String> mSuppressedGroupToNotifKeys = new HashMap<>();
+ private HashMap<String, Bubble> mShortcutIdToBubble = new HashMap<>();
+
+ private ArrayList<Bubble> mTmpBubbles = new ArrayList<>();
+
+ /**
+ * Updates the cached state based on the last full BubbleData change.
+ */
+ synchronized void update(BubbleData.Update update) {
+ if (update.selectionChanged) {
+ mSelectedBubbleKey = update.selectedBubble != null
+ ? update.selectedBubble.getKey()
+ : null;
+ }
+ if (update.expandedChanged) {
+ mIsStackExpanded = update.expanded;
+ }
+ if (update.suppressedSummaryChanged) {
+ String summaryKey =
+ mBubbleData.getSummaryKey(update.suppressedSummaryGroup);
+ if (summaryKey != null) {
+ mSuppressedGroupToNotifKeys.put(update.suppressedSummaryGroup, summaryKey);
+ } else {
+ mSuppressedGroupToNotifKeys.remove(update.suppressedSummaryGroup);
+ }
+ }
+
+ mTmpBubbles.clear();
+ mTmpBubbles.addAll(update.bubbles);
+ mTmpBubbles.addAll(update.overflowBubbles);
+
+ mSuppressedBubbleKeys.clear();
+ mShortcutIdToBubble.clear();
+ for (Bubble b : mTmpBubbles) {
+ mShortcutIdToBubble.put(b.getShortcutId(), b);
+ updateBubbleSuppressedState(b);
+ }
+ }
+
+ /**
+ * Updates a specific bubble suppressed state. This is used mainly because notification
+ * suppression changes don't go through the same BubbleData update mechanism.
+ */
+ synchronized void updateBubbleSuppressedState(Bubble b) {
+ if (!b.showInShade()) {
+ mSuppressedBubbleKeys.add(b.getKey());
+ } else {
+ mSuppressedBubbleKeys.remove(b.getKey());
+ }
+ }
+
+ public synchronized boolean isStackExpanded() {
+ return mIsStackExpanded;
+ }
+
+ public synchronized boolean isBubbleExpanded(String key) {
+ return mIsStackExpanded && key.equals(mSelectedBubbleKey);
+ }
+
+ public synchronized boolean isBubbleNotificationSuppressedFromShade(String key,
+ String groupKey) {
+ return mSuppressedBubbleKeys.contains(key)
+ || (mSuppressedGroupToNotifKeys.containsKey(groupKey)
+ && key.equals(mSuppressedGroupToNotifKeys.get(groupKey)));
+ }
+
+ @Nullable
+ public synchronized Bubble getBubbleWithShortcutId(String id) {
+ return mShortcutIdToBubble.get(id);
+ }
+
+ synchronized void dump(PrintWriter pw) {
+ pw.println("BubbleImpl.CachedState state:");
+
+ pw.println("mIsStackExpanded: " + mIsStackExpanded);
+ pw.println("mSelectedBubbleKey: " + mSelectedBubbleKey);
+
+ pw.print("mSuppressedBubbleKeys: ");
+ pw.println(mSuppressedBubbleKeys.size());
+ for (String key : mSuppressedBubbleKeys) {
+ pw.println(" suppressing: " + key);
+ }
+
+ pw.print("mSuppressedGroupToNotifKeys: ");
+ pw.println(mSuppressedGroupToNotifKeys.size());
+ for (String key : mSuppressedGroupToNotifKeys.keySet()) {
+ pw.println(" suppressing: " + key);
+ }
+ }
+ }
+
+ private CachedState mCachedState = new CachedState();
+
@Override
public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) {
- return mMainExecutor.executeBlockingForResult(() -> {
- return BubbleController.this.isBubbleNotificationSuppressedFromShade(key, groupKey);
- }, Boolean.class);
+ return mCachedState.isBubbleNotificationSuppressedFromShade(key, groupKey);
}
@Override
public boolean isBubbleExpanded(String key) {
- return mMainExecutor.executeBlockingForResult(() -> {
- return BubbleController.this.isBubbleExpanded(key);
- }, Boolean.class);
+ return mCachedState.isBubbleExpanded(key);
}
@Override
public boolean isStackExpanded() {
- return mMainExecutor.executeBlockingForResult(() -> {
- return BubbleController.this.isStackExpanded();
- }, Boolean.class);
+ return mCachedState.isStackExpanded();
+ }
+
+ @Override
+ @Nullable
+ public Bubble getBubbleWithShortcutId(String shortcutId) {
+ return mCachedState.getBubbleWithShortcutId(shortcutId);
}
@Override
@@ -1425,14 +1533,6 @@
}
@Override
- @Nullable
- public Bubble getBubbleWithShortcutId(String shortcutId) {
- return mMainExecutor.executeBlockingForResult(() -> {
- return BubbleController.this.mBubbleData.getAnyBubbleWithShortcutId(shortcutId);
- }, Bubble.class);
- }
-
- @Override
public void onTaskbarChanged(Bundle b) {
mMainExecutor.execute(() -> {
BubbleController.this.onTaskbarChanged(b);
@@ -1555,6 +1655,7 @@
try {
mMainExecutor.executeBlocking(() -> {
BubbleController.this.dump(fd, pw, args);
+ mCachedState.dump(pw);
});
} catch (InterruptedException e) {
Slog.e(TAG, "Failed to dump BubbleController in 2s");
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 6f5cfd1..d73ce69 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
@@ -73,6 +73,7 @@
boolean expandedChanged;
boolean selectionChanged;
boolean orderChanged;
+ boolean suppressedSummaryChanged;
boolean expanded;
@Nullable BubbleViewProvider selectedBubble;
@Nullable Bubble addedBubble;
@@ -81,6 +82,7 @@
@Nullable Bubble removedOverflowBubble;
@Nullable Bubble suppressedBubble;
@Nullable Bubble unsuppressedBubble;
+ @Nullable String suppressedSummaryGroup;
// Pair with Bubble and @DismissReason Integer
final List<Pair<Bubble, Integer>> removedBubbles = new ArrayList<>();
@@ -103,7 +105,9 @@
|| removedOverflowBubble != null
|| orderChanged
|| suppressedBubble != null
- || unsuppressedBubble != null;
+ || unsuppressedBubble != null
+ || suppressedSummaryChanged
+ || suppressedSummaryGroup != null;
}
void bubbleRemoved(Bubble bubbleToRemove, @DismissReason int reason) {
@@ -380,6 +384,9 @@
*/
void addSummaryToSuppress(String groupKey, String notifKey) {
mSuppressedGroupKeys.put(groupKey, notifKey);
+ mStateChange.suppressedSummaryChanged = true;
+ mStateChange.suppressedSummaryGroup = groupKey;
+ dispatchPendingChanges();
}
/**
@@ -397,6 +404,9 @@
*/
void removeSuppressedSummary(String groupKey) {
mSuppressedGroupKeys.remove(groupKey);
+ mStateChange.suppressedSummaryChanged = true;
+ mStateChange.suppressedSummaryGroup = groupKey;
+ dispatchPendingChanges();
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index f81f086..9687ec6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -90,15 +90,15 @@
private boolean mNeedsNewHeight;
/**
- * Whether we want the TaskView's content to be visible (alpha = 1f). If
- * {@link #mIsAlphaAnimating} is true, this may not reflect the TaskView's actual alpha value
- * until the animation ends.
+ * Whether we want the {@code TaskView}'s content to be visible (alpha = 1f). If
+ * {@link #mIsAlphaAnimating} is true, this may not reflect the {@code TaskView}'s actual alpha
+ * value until the animation ends.
*/
private boolean mIsContentVisible = false;
/**
- * Whether we're animating the TaskView's alpha value. If so, we will hold off on applying alpha
- * changes from {@link #setContentVisibility} until the animation ends.
+ * Whether we're animating the {@code TaskView}'s alpha value. If so, we will hold off on
+ * applying alpha changes from {@link #setContentVisibility} until the animation ends.
*/
private boolean mIsAlphaAnimating = false;
@@ -127,8 +127,8 @@
private BubblePositioner mPositioner;
/**
- * Container for the ActivityView that has a solid, round-rect background that shows if the
- * ActivityView hasn't loaded.
+ * Container for the {@code TaskView} that has a solid, round-rect background that shows if the
+ * {@code TaskView} hasn't loaded.
*/
private final FrameLayout mExpandedViewContainer = new FrameLayout(getContext());
@@ -139,7 +139,7 @@
@Override
public void onInitialized() {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
- Log.d(TAG, "onActivityViewReady: destroyed=" + mDestroyed
+ Log.d(TAG, "onInitialized: destroyed=" + mDestroyed
+ " initialized=" + mInitialized
+ " bubble=" + getBubbleKey());
}
@@ -159,7 +159,7 @@
// Post to keep the lifecycle normal
post(() -> {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
- Log.d(TAG, "onActivityViewReady: calling startActivity, bubble="
+ Log.d(TAG, "onInitialized: calling startActivity, bubble="
+ getBubbleKey());
}
try {
@@ -265,7 +265,7 @@
mCurrentPointer = mTopPointer;
mPointerView.setVisibility(INVISIBLE);
- // Set TaskView's alpha value as zero, since there is no view content to be shown.
+ // Set {@code TaskView}'s alpha value as zero, since there is no view content to be shown.
setContentVisibility(false);
mExpandedViewContainer.setOutlineProvider(new ViewOutlineProvider() {
@@ -290,6 +290,27 @@
applyThemeAttrs();
setClipToPadding(false);
+ setOnTouchListener((view, motionEvent) -> {
+ if (mTaskView == null) {
+ return false;
+ }
+
+ final Rect avBounds = new Rect();
+ mTaskView.getBoundsOnScreen(avBounds);
+
+ // Consume and ignore events on the expanded view padding that are within the
+ // {@code TaskView}'s vertical bounds. These events are part of a back gesture, and so
+ // they should not collapse the stack (which all other touches on areas around the AV
+ // would do).
+ if (motionEvent.getRawY() >= avBounds.top
+ && motionEvent.getRawY() <= avBounds.bottom
+ && (motionEvent.getRawX() < avBounds.left
+ || motionEvent.getRawX() > avBounds.right)) {
+ return true;
+ }
+
+ return false;
+ });
// BubbleStackView is forced LTR, but we want to respect the locale for expanded view layout
// so the Manage button appears on the right.
@@ -470,7 +491,7 @@
/**
* Updates the obscured touchable region for the task surface. This calls onLocationChanged,
* which results in a call to {@link BubbleStackView#subtractObscuredTouchableRegion}. This is
- * useful if a view has been added or removed from on top of the ActivityView, such as the
+ * useful if a view has been added or removed from on top of the {@code TaskView}, such as the
* manage menu.
*/
void updateObscuredTouchableRegion() {
@@ -490,8 +511,9 @@
}
/**
- * Whether we are currently animating the TaskView's alpha value. If this is set to true, calls
- * to {@link #setContentVisibility} will not be applied until this is set to false again.
+ * Whether we are currently animating the {@code TaskView}'s alpha value. If this is set to
+ * true, calls to {@link #setContentVisibility} will not be applied until this is set to false
+ * again.
*/
void setAlphaAnimating(boolean animating) {
mIsAlphaAnimating = animating;
@@ -503,8 +525,8 @@
}
/**
- * Sets the alpha of the underlying TaskView, since changing the expanded view's alpha does not
- * affect the TaskView since it uses a Surface.
+ * Sets the alpha of the underlying {@code TaskView}, since changing the expanded view's alpha
+ * does not affect the {@code TaskView} since it uses a Surface.
*/
void setTaskViewAlpha(float alpha) {
if (mTaskView != null) {
@@ -743,9 +765,9 @@
}
/**
- * Cleans up anything related to the task and TaskView. If this view should be reused after this
- * method is called, then {@link #initialize(BubbleController, BubbleStackView, boolean)} must
- * be invoked first.
+ * Cleans up anything related to the task and {@code TaskView}. If this view should be reused
+ * after this method is called, then
+ * {@link #initialize(BubbleController, BubbleStackView, boolean)} must be invoked first.
*/
public void cleanUpExpandedState() {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
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 c71f123..92e455c 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
@@ -1611,7 +1611,7 @@
if (mIsExpanded && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null
&& !mExpandedViewTemporarilyHidden) {
if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
- // Before screenshotting, have the real ActivityView show on top of other surfaces
+ // Before screenshotting, have the real TaskView show on top of other surfaces
// so that the screenshot doesn't flicker on top of it.
mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(true);
}
@@ -2583,8 +2583,8 @@
}
/**
- * Requests a snapshot from the currently expanded bubble's ActivityView and displays it in a
- * SurfaceView. This allows us to load a newly expanded bubble's Activity into the ActivityView,
+ * Requests a snapshot from the currently expanded bubble's TaskView and displays it in a
+ * SurfaceView. This allows us to load a newly expanded bubble's Activity into the TaskView,
* while animating the (screenshot of the) previously selected bubble's content away.
*
* @param onComplete Callback to run once we're done here - called with 'false' if something
@@ -2628,13 +2628,13 @@
mAnimatingOutSurfaceContainer.setTranslationX(mExpandedViewContainer.getPaddingLeft());
mAnimatingOutSurfaceContainer.setTranslationY(0);
- final int[] activityViewLocation =
+ final int[] taskViewLocation =
mExpandedBubble.getExpandedView().getTaskViewLocationOnScreen();
final int[] surfaceViewLocation = mAnimatingOutSurfaceView.getLocationOnScreen();
- // Translate the surface to overlap the real ActivityView.
+ // Translate the surface to overlap the real TaskView.
mAnimatingOutSurfaceContainer.setTranslationY(
- activityViewLocation[1] - surfaceViewLocation[1]);
+ taskViewLocation[1] - surfaceViewLocation[1]);
// Set the width/height of the SurfaceView to match the snapshot.
mAnimatingOutSurfaceView.getLayoutParams().width =
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index b2ac61c..cb27ad9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -249,6 +249,7 @@
!activeControl.getSurfacePosition().equals(lastSurfacePosition);
final boolean leashChanged =
!haveSameLeash(mImeSourceControl, activeControl);
+ final InsetsSourceControl lastImeControl = mImeSourceControl;
mImeSourceControl = activeControl;
if (mAnimation != null) {
if (positionChanged) {
@@ -262,6 +263,9 @@
removeImeSurface();
}
}
+ if (lastImeControl != null) {
+ lastImeControl.release(SurfaceControl::release);
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java
index 53dd391..75a1dde 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java
@@ -19,8 +19,8 @@
import static android.view.Display.DEFAULT_DISPLAY;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Insets;
-import android.graphics.Point;
import android.graphics.Rect;
import android.util.ArrayMap;
import android.util.Log;
@@ -40,13 +40,12 @@
import androidx.annotation.VisibleForTesting;
import com.android.internal.R;
-import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import java.io.PrintWriter;
import java.util.List;
-import java.util.concurrent.Executor;
/**
* Manages the display areas of hide display cutout feature.
@@ -76,19 +75,29 @@
@VisibleForTesting
int mRotation;
- /**
- * Handles rotation based on OnDisplayChangingListener callback.
- */
- private final DisplayChangeController.OnDisplayChangingListener mRotationController =
- (display, fromRotation, toRotation, wct) -> {
- mRotation = toRotation;
- updateBoundsAndOffsets(true /* enable */);
- final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- applyAllBoundsAndOffsets(wct, t);
- // Only apply t here since the server will do the wct.apply when the method
- // finishes.
- t.apply();
- };
+ private final DisplayController.OnDisplaysChangedListener mListener =
+ new DisplayController.OnDisplaysChangedListener() {
+ @Override
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+ DisplayLayout displayLayout =
+ mDisplayController.getDisplayLayout(DEFAULT_DISPLAY);
+ if (displayLayout == null) {
+ return;
+ }
+ final boolean rotationChanged = mRotation != displayLayout.rotation();
+ mRotation = displayLayout.rotation();
+ if (rotationChanged || isDisplayBoundsChanged()) {
+ updateBoundsAndOffsets(true /* enabled */);
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ applyAllBoundsAndOffsets(wct, t);
+ applyTransaction(wct, t);
+ }
+ }
+ };
HideDisplayCutoutOrganizer(Context context, DisplayController displayController,
ShellExecutor mainExecutor) {
@@ -154,10 +163,10 @@
* Enables hide display cutout.
*/
void enableHideDisplayCutout() {
- mDisplayController.addDisplayChangingController(mRotationController);
- final Display display = mDisplayController.getDisplay(DEFAULT_DISPLAY);
- if (display != null) {
- mRotation = display.getRotation();
+ mDisplayController.addDisplayWindowListener(mListener);
+ final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(DEFAULT_DISPLAY);
+ if (displayLayout != null) {
+ mRotation = displayLayout.rotation();
}
final List<DisplayAreaAppearedInfo> displayAreaInfos =
registerOrganizer(DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT);
@@ -174,7 +183,7 @@
*/
void disableHideDisplayCutout() {
updateBoundsAndOffsets(false /* enabled */);
- mDisplayController.removeDisplayChangingController(mRotationController);
+ mDisplayController.removeDisplayWindowListener(mListener);
unregisterOrganizer();
}
@@ -193,23 +202,35 @@
@VisibleForTesting
Rect getDisplayBoundsOfNaturalOrientation() {
- Point realSize = new Point(0, 0);
- final Display display = mDisplayController.getDisplay(DEFAULT_DISPLAY);
- if (display != null) {
- display.getRealSize(realSize);
+ final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(DEFAULT_DISPLAY);
+ if (displayLayout == null) {
+ return new Rect();
}
final boolean isDisplaySizeFlipped = isDisplaySizeFlipped();
return new Rect(
0,
0,
- isDisplaySizeFlipped ? realSize.y : realSize.x,
- isDisplaySizeFlipped ? realSize.x : realSize.y);
+ isDisplaySizeFlipped ? displayLayout.height() : displayLayout.width(),
+ isDisplaySizeFlipped ? displayLayout.width() : displayLayout.height());
}
private boolean isDisplaySizeFlipped() {
return mRotation == Surface.ROTATION_90 || mRotation == Surface.ROTATION_270;
}
+ private boolean isDisplayBoundsChanged() {
+ final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(DEFAULT_DISPLAY);
+ if (displayLayout == null) {
+ return false;
+ }
+ final boolean isDisplaySizeFlipped = isDisplaySizeFlipped();
+ final int width = isDisplaySizeFlipped ? displayLayout.height() : displayLayout.width();
+ final int height = isDisplaySizeFlipped ? displayLayout.width() : displayLayout.height();
+ return mDefaultDisplayBounds.isEmpty()
+ || mDefaultDisplayBounds.width() != width
+ || mDefaultDisplayBounds.height() != height;
+ }
+
/**
* Updates bounds and offsets according to current state.
*
@@ -237,7 +258,6 @@
mCurrentDisplayBounds.right);
}
mCurrentDisplayBounds.inset(mCurrentCutoutInsets);
-
// Replace the top bound with the max(status bar height, cutout height) if there is
// cutout on the top side.
mStatusBarHeight = getStatusBarHeight();
@@ -256,7 +276,7 @@
}
private void initDefaultValuesIfNeeded() {
- if (!mDefaultDisplayBounds.isEmpty()) {
+ if (!isDisplayBoundsChanged()) {
return;
}
mDefaultDisplayBounds.set(getDisplayBoundsOfNaturalOrientation());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
index 8dc05de9..3253bb0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java
@@ -68,6 +68,11 @@
void setLockedDisabled(boolean locked, boolean enabled);
/**
+ * Registers callback to notify WMShell when user tap shortcut to expand notification.
+ */
+ void registerEventCallback(OneHandedEventCallback callback);
+
+ /**
* Registers callback to be notified after {@link OneHandedDisplayAreaOrganizer}
* transition start or finish
*/
@@ -82,4 +87,9 @@
* Notifies when user switch complete
*/
void onUserSwitch(int userId);
+
+ /**
+ * Notifies when keyguard visibility changed
+ */
+ void onKeyguardVisibilityChanged(boolean showing);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index c275d50..1cc7ed3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -43,6 +43,7 @@
import android.view.Surface;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -72,6 +73,7 @@
private static final String ONE_HANDED_MODE_GESTURAL_OVERLAY =
"com.android.internal.systemui.onehanded.gestural";
private static final int OVERLAY_ENABLED_DELAY_MS = 250;
+ private static final int DISPLAY_AREA_READY_RETRY_MS = 10;
static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
@@ -79,6 +81,7 @@
private volatile boolean mIsSwipeToNotificationEnabled;
private boolean mTaskChangeToExit;
private boolean mLockedDisabled;
+ private boolean mKeyguardShowing;
private int mUserId;
private float mOffSetFraction;
@@ -92,15 +95,16 @@
private final OneHandedTouchHandler mTouchHandler;
private final OneHandedState mState;
private final OneHandedTutorialHandler mTutorialHandler;
- private final OneHandedUiEventLogger mOneHandedUiEventLogger;
private final TaskStackListenerImpl mTaskStackListener;
private final IOverlayManager mOverlayManager;
private final ShellExecutor mMainExecutor;
private final Handler mMainHandler;
private final OneHandedImpl mImpl = new OneHandedImpl();
+ private OneHandedEventCallback mEventCallback;
private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer;
+ private OneHandedUiEventLogger mOneHandedUiEventLogger;
/**
* Handle rotation based on OnDisplayChangingListener callback
@@ -111,6 +115,8 @@
return;
}
mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation, wct);
+ mOneHandedUiEventLogger.writeEvent(
+ OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_ROTATION_OUT);
};
private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener =
@@ -288,7 +294,7 @@
mTimeoutObserver = getObserver(this::onTimeoutSettingChanged);
mTaskChangeExitObserver = getObserver(this::onTaskChangeExitSettingChanged);
mSwipeToNotificationEnabledObserver =
- getObserver(this::onSwipeToNotificationEnabledSettingChanged);
+ getObserver(this::onSwipeToNotificationEnabledChanged);
mDisplayController.addDisplayChangingController(mRotationController);
setupCallback();
@@ -354,18 +360,27 @@
@VisibleForTesting
void startOneHanded() {
- if (isLockedDisabled()) {
+ if (isLockedDisabled() || mKeyguardShowing) {
Slog.d(TAG, "Temporary lock disabled");
return;
}
+
+ if (!mDisplayAreaOrganizer.isReady()) {
+ // Must wait until DisplayAreaOrganizer is ready for transitioning.
+ mMainExecutor.executeDelayed(this::startOneHanded, DISPLAY_AREA_READY_RETRY_MS);
+ return;
+ }
+
if (mState.isTransitioning() || mState.isInOneHanded()) {
return;
}
+
final int currentRotation = mDisplayAreaOrganizer.getDisplayLayout().rotation();
if (currentRotation != Surface.ROTATION_0 && currentRotation != Surface.ROTATION_180) {
Slog.w(TAG, "One handed mode only support portrait mode");
return;
}
+
mState.setState(STATE_ENTERING);
final int yOffSet = Math.round(
mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction);
@@ -394,6 +409,10 @@
mOneHandedUiEventLogger.writeEvent(uiEvent);
}
+ void registerEventCallback(OneHandedEventCallback callback) {
+ mEventCallback = callback;
+ }
+
@VisibleForTesting
void registerTransitionCallback(OneHandedTransitionCallback callback) {
mDisplayAreaOrganizer.registerTransitionCallback(callback);
@@ -464,8 +483,36 @@
}
@VisibleForTesting
+ void notifyExpandNotification() {
+ if (mEventCallback != null) {
+ mMainExecutor.execute(() -> mEventCallback.notifyExpandNotification());
+ }
+ }
+
+ @VisibleForTesting
+ void notifyUserConfigChanged(boolean success) {
+ if (!success) {
+ return;
+ }
+ // TODO Check UX if popup Toast to notify user when auto-enabled one-handed is good option.
+ Toast.makeText(mContext, R.string.one_handed_tutorial_title, Toast.LENGTH_LONG).show();
+ }
+
+ @VisibleForTesting
void onActivatedActionChanged() {
- if (mState.isTransitioning() || !isOneHandedEnabled()) {
+ if (!isShortcutEnabled()) {
+ Slog.w(TAG, "Shortcut not enabled, skip onActivatedActionChanged()");
+ return;
+ }
+
+ if (!isOneHandedEnabled()) {
+ final boolean success = mOneHandedSettingsUtil.setOneHandedModeEnabled(
+ mContext.getContentResolver(), 1 /* Enabled for shortcut */, mUserId);
+ notifyUserConfigChanged(success);
+ }
+
+ if (isSwipeToNotificationEnabled()) {
+ notifyExpandNotification();
return;
}
@@ -494,11 +541,9 @@
setOneHandedEnabled(enabled);
// Also checks swipe to notification settings since they all need gesture overlay.
- // Enabled overlay package may affect the current animation(e.g:Settings switch),
- // so we delay 250ms to enabled overlay after switch animation finish
- mMainExecutor.executeDelayed(() -> setEnabledGesturalOverlay(
+ setEnabledGesturalOverlay(
enabled || mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
- mContext.getContentResolver(), mUserId)), OVERLAY_ENABLED_DELAY_MS);
+ mContext.getContentResolver(), mUserId), true /* DelayExecute */);
}
@VisibleForTesting
@@ -542,16 +587,20 @@
}
@VisibleForTesting
- void onSwipeToNotificationEnabledSettingChanged() {
+ void onSwipeToNotificationEnabledChanged() {
final boolean enabled =
mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
mContext.getContentResolver(), mUserId);
setSwipeToNotificationEnabled(enabled);
+ mOneHandedUiEventLogger.writeEvent(enabled
+ ? OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_SHOW_NOTIFICATION_ENABLED_ON
+ : OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_SHOW_NOTIFICATION_ENABLED_OFF);
+
// Also checks one handed mode settings since they all need gesture overlay.
setEnabledGesturalOverlay(
enabled || mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
- mContext.getContentResolver(), mUserId));
+ mContext.getContentResolver(), mUserId), true /* DelayExecute */);
}
private void setupTimeoutListener() {
@@ -569,11 +618,27 @@
return mIsOneHandedEnabled;
}
+ @VisibleForTesting
+ boolean isShortcutEnabled() {
+ return mOneHandedSettingsUtil.getShortcutEnabled(mContext.getContentResolver(), mUserId);
+ }
+
+ @VisibleForTesting
+ boolean isSwipeToNotificationEnabled() {
+ return mIsSwipeToNotificationEnabled;
+ }
+
private void updateOneHandedEnabled() {
if (mState.getState() == STATE_ENTERING || mState.getState() == STATE_ACTIVE) {
mMainExecutor.execute(() -> stopOneHanded());
}
+ // If setting is pull screen, notify shortcut one_handed_mode_activated to reset
+ // and align status with current mState when function enabled.
+ if (isOneHandedEnabled() && !isSwipeToNotificationEnabled()) {
+ notifyShortcutState(mState.getState());
+ }
+
mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled);
if (!mIsOneHandedEnabled) {
@@ -608,12 +673,19 @@
if (info != null && !info.isEnabled()) {
// Enable the default gestural one handed overlay.
- setEnabledGesturalOverlay(true);
+ setEnabledGesturalOverlay(true /* enabled */, false /* delayExecute */);
}
}
@VisibleForTesting
- private void setEnabledGesturalOverlay(boolean enabled) {
+ private void setEnabledGesturalOverlay(boolean enabled, boolean delayExecute) {
+ if (mState.isTransitioning() || delayExecute) {
+ // Enabled overlay package may affect the current animation(e.g:Settings switch),
+ // so we delay 250ms to enabled overlay after switch animation finish, only delay once.
+ mMainExecutor.executeDelayed(() -> setEnabledGesturalOverlay(enabled, false),
+ OVERLAY_ENABLED_DELAY_MS);
+ return;
+ }
try {
mOverlayManager.setEnabled(ONE_HANDED_MODE_GESTURAL_OVERLAY, enabled, USER_CURRENT);
} catch (RemoteException e) {
@@ -628,6 +700,7 @@
if (enabled == isFeatureEnabled) {
return;
}
+
mLockedDisabled = locked && !enabled;
}
@@ -641,6 +714,10 @@
mTutorialHandler.onConfigurationChanged();
}
+ private void onKeyguardVisibilityChanged(boolean showing) {
+ mKeyguardShowing = showing;
+ }
+
private void onUserSwitch(int newUserId) {
unregisterSettingObservers();
mUserId = newUserId;
@@ -659,6 +736,8 @@
pw.println(mLockedDisabled);
pw.print(innerPrefix + "mUserId=");
pw.println(mUserId);
+ pw.print(innerPrefix + "isShortcutEnabled=");
+ pw.println(isShortcutEnabled());
if (mBackgroundPanelOrganizer != null) {
mBackgroundPanelOrganizer.dump(pw);
@@ -761,6 +840,13 @@
}
@Override
+ public void registerEventCallback(OneHandedEventCallback callback) {
+ mMainExecutor.execute(() -> {
+ OneHandedController.this.registerEventCallback(callback);
+ });
+ }
+
+ @Override
public void registerTransitionCallback(OneHandedTransitionCallback callback) {
mMainExecutor.execute(() -> {
OneHandedController.this.registerTransitionCallback(callback);
@@ -780,6 +866,13 @@
OneHandedController.this.onUserSwitch(userId);
});
}
+
+ @Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ mMainExecutor.execute(() -> {
+ OneHandedController.this.onKeyguardVisibilityChanged(showing);
+ });
+ }
}
/**
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 b8da37f..03a90c6 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
@@ -61,11 +61,12 @@
private DisplayLayout mDisplayLayout = new DisplayLayout();
- private float mLastVisualOffset = 0;
private final Rect mLastVisualDisplayBounds = new Rect();
private final Rect mDefaultDisplayBounds = new Rect();
private final OneHandedSettingsUtil mOneHandedSettingsUtil;
+ private boolean mIsReady;
+ private float mLastVisualOffset = 0;
private int mEnterExitAnimationDurationMs;
private ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap = new ArrayMap();
@@ -157,6 +158,7 @@
final DisplayAreaAppearedInfo info = displayAreaInfos.get(i);
onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash());
}
+ mIsReady = true;
updateDisplayBounds();
return displayAreaInfos;
}
@@ -164,9 +166,14 @@
@Override
public void unregisterOrganizer() {
super.unregisterOrganizer();
+ mIsReady = false;
resetWindowsOffset();
}
+ boolean isReady() {
+ return mIsReady;
+ }
+
/**
* Handler for display rotation changes by {@link DisplayLayout}
*
@@ -184,8 +191,15 @@
myUserId())) {
return;
}
+
mDisplayLayout.rotateTo(context.getResources(), toRotation);
updateDisplayBounds();
+
+ if (mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
+ context.getContentResolver(), myUserId())) {
+ // If current settings is swipe notification, skip finishOffset.
+ return;
+ }
finishOffset(0, TRANSITION_DIRECTION_EXIT);
}
@@ -312,6 +326,8 @@
pw.println(mDisplayAreaTokenMap);
pw.print(innerPrefix + "mDefaultDisplayBounds=");
pw.println(mDefaultDisplayBounds);
+ pw.print(innerPrefix + "mIsReady=");
+ pw.println(mIsReady);
pw.print(innerPrefix + "mLastVisualDisplayBounds=");
pw.println(mLastVisualDisplayBounds);
pw.print(innerPrefix + "mLastVisualOffset=");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java
new file mode 100644
index 0000000..d07eea2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java
@@ -0,0 +1,28 @@
+/*
+ * 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.onehanded;
+
+/**
+ * Additional callback interface for OneHanded events.
+ */
+public interface OneHandedEventCallback {
+ /**
+ * Called to notify expand notification shade.
+ */
+ default void notifyExpandNotification() {
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
index 90fc823..3baa69f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java
@@ -16,6 +16,8 @@
package com.android.wm.shell.onehanded;
+import static com.android.internal.accessibility.AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME;
+
import android.annotation.IntDef;
import android.content.ContentResolver;
import android.database.ContentObserver;
@@ -34,6 +36,9 @@
public final class OneHandedSettingsUtil {
private static final String TAG = "OneHandedSettingsUtil";
+ private static final String ONE_HANDED_MODE_TARGET_NAME =
+ ONE_HANDED_COMPONENT_NAME.getShortClassName();
+
@IntDef(prefix = {"ONE_HANDED_TIMEOUT_"}, value = {
ONE_HANDED_TIMEOUT_NEVER,
ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS,
@@ -105,6 +110,17 @@
}
/**
+ * Sets one handed enable or disable flag from Settings provider.
+ *
+ * @return true if the value was set, false on database errors
+ */
+ public boolean setOneHandedModeEnabled(ContentResolver resolver, int enabled, int userId) {
+ return Settings.Secure.putIntForUser(resolver,
+ Settings.Secure.ONE_HANDED_MODE_ENABLED, enabled, userId);
+ }
+
+
+ /**
* Queries taps app to exit config from Settings provider.
*
* @return enable or disable taps app exit.
@@ -147,6 +163,17 @@
}
/**
+ * Queries one-handed mode shortcut enabled in settings or not.
+ *
+ * @return true if user enabled one-handed shortcut in settings, false otherwise.
+ */
+ public boolean getShortcutEnabled(ContentResolver resolver, int userId) {
+ final String targets = Settings.Secure.getStringForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId);
+ return targets != null ? targets.contains(ONE_HANDED_MODE_TARGET_NAME) : false;
+ }
+
+ /**
* Sets tutorial shown counts.
*
* @return true if the value was set, false on database errors.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedUiEventLogger.java
index 38ffb07..4e610fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedUiEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedUiEventLogger.java
@@ -50,6 +50,8 @@
public static final int EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_4 = 15;
public static final int EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_8 = 16;
public static final int EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12 = 17;
+ public static final int EVENT_ONE_HANDED_SETTINGS_SHOW_NOTIFICATION_ENABLED_ON = 18;
+ public static final int EVENT_ONE_HANDED_SETTINGS_SHOW_NOTIFICATION_ENABLED_OFF = 19;
private static final String[] EVENT_TAGS = {
"one_handed_trigger_gesture_in",
@@ -69,7 +71,9 @@
"one_handed_settings_timeout_seconds_never",
"one_handed_settings_timeout_seconds_4",
"one_handed_settings_timeout_seconds_8",
- "one_handed_settings_timeout_seconds_12"
+ "one_handed_settings_timeout_seconds_12",
+ "one_handed_settings_show_notification_enabled_on",
+ "one_handed_settings_show_notification_enabled_off"
};
public OneHandedUiEventLogger(UiEventLogger uiEventLogger) {
@@ -152,7 +156,13 @@
ONE_HANDED_SETTINGS_TOGGLES_TIMEOUT_SECONDS_8(364),
@UiEvent(doc = "One-Handed mode timeout value changed to 12 seconds")
- ONE_HANDED_SETTINGS_TOGGLES_TIMEOUT_SECONDS_12(365);
+ ONE_HANDED_SETTINGS_TOGGLES_TIMEOUT_SECONDS_12(365),
+
+ @UiEvent(doc = "One-Handed mode show notification toggle on")
+ ONE_HANDED_SETTINGS_TOGGLES_SHOW_NOTIFICATION_ENABLED_ON(847),
+
+ @UiEvent(doc = "One-Handed mode show notification toggle off")
+ ONE_HANDED_SETTINGS_TOGGLES_SHOW_NOTIFICATION_ENABLED_OFF(848);
private final int mId;
@@ -247,6 +257,14 @@
mUiEventLogger.log(OneHandedSettingsTogglesEvent
.ONE_HANDED_SETTINGS_TOGGLES_TIMEOUT_SECONDS_12);
break;
+ case EVENT_ONE_HANDED_SETTINGS_SHOW_NOTIFICATION_ENABLED_ON:
+ mUiEventLogger.log(OneHandedSettingsTogglesEvent
+ .ONE_HANDED_SETTINGS_TOGGLES_SHOW_NOTIFICATION_ENABLED_ON);
+ break;
+ case EVENT_ONE_HANDED_SETTINGS_SHOW_NOTIFICATION_ENABLED_OFF:
+ mUiEventLogger.log(OneHandedSettingsTogglesEvent
+ .ONE_HANDED_SETTINGS_TOGGLES_SHOW_NOTIFICATION_ENABLED_OFF);
+ break;
default:
// Do nothing
break;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 6451b94..324a6e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -835,11 +835,14 @@
WindowContainerTransaction wct) {
// note that this can be called when swipe-to-home or fixed-rotation is happening.
// Skip this entirely if that's the case.
- if ((mInSwipePipToHomeTransition || mWaitForFixedRotation) && fromRotation) {
+ final boolean waitForFixedRotationOnEnteringPip = mWaitForFixedRotation
+ && (mState != State.ENTERED_PIP);
+ if ((mInSwipePipToHomeTransition || waitForFixedRotationOnEnteringPip) && fromRotation) {
if (DEBUG) {
Log.d(TAG, "Skip onMovementBoundsChanged on rotation change"
+ " mInSwipePipToHomeTransition=" + mInSwipePipToHomeTransition
- + " mWaitForFixedRotation=" + mWaitForFixedRotation);
+ + " mWaitForFixedRotation=" + mWaitForFixedRotation
+ + " mState=" + mState);
}
return;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index f6b5889..bc8e1e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -278,6 +278,10 @@
return;
}
+ // Sync the menu bounds before showing it in case it is out of sync.
+ movePipMenu(null /* pipLeash */, null /* transaction */, stackBounds);
+ updateMenuBounds(stackBounds);
+
mPipMenuView.showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay,
showResizeHandle);
}
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 841edef..f0bd8a2 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
@@ -235,15 +235,20 @@
@VisibleForTesting
void onInputEvent(InputEvent ev) {
+ if (!mEnableDragCornerResize && !mEnablePinchResize) {
+ // No need to handle anything if neither form of resizing is enabled.
+ return;
+ }
+
// Don't allow resize when PiP is stashed.
if (mPipBoundsState.isStashed()) {
return;
}
if (ev instanceof MotionEvent) {
- if (mOngoingPinchToResize) {
+ if (mEnablePinchResize && mOngoingPinchToResize) {
onPinchResize((MotionEvent) ev);
- } else {
+ } else if (mEnableDragCornerResize) {
onDragCornerResize((MotionEvent) ev);
}
}
@@ -318,8 +323,8 @@
case MotionEvent.ACTION_POINTER_DOWN:
if (mEnablePinchResize && ev.getPointerCount() == 2) {
onPinchResize(ev);
- mOngoingPinchToResize = true;
- return true;
+ mOngoingPinchToResize = mAllowGesture;
+ return mAllowGesture;
}
break;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
index 1a365fe..9986154 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -235,7 +235,8 @@
}
void onAnimationProgress(float linearProgress) {
- if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()) {
+ if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()
+ || !mSplashScreenView.isAttachedToWindow()) {
return;
}
@@ -267,15 +268,20 @@
return;
}
final SurfaceControl.Transaction tx = mTransactionPool.acquire();
- tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
+ if (mSplashScreenView.isAttachedToWindow()) {
+ tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
- SyncRtSurfaceTransactionApplier.SurfaceParams
- params = new SyncRtSurfaceTransactionApplier.SurfaceParams
- .Builder(mFirstWindowSurface)
- .withWindowCrop(null)
- .withMergeTransaction(tx)
- .build();
- mApplier.scheduleApply(params);
+ SyncRtSurfaceTransactionApplier.SurfaceParams
+ params = new SyncRtSurfaceTransactionApplier.SurfaceParams
+ .Builder(mFirstWindowSurface)
+ .withWindowCrop(null)
+ .withMergeTransaction(tx)
+ .build();
+ mApplier.scheduleApply(params);
+ } else {
+ tx.setWindowCrop(mFirstWindowSurface, null);
+ tx.apply();
+ }
mTransactionPool.release(tx);
Choreographer.getSfInstance().postCallback(CALLBACK_COMMIT,
@@ -287,13 +293,14 @@
if (DEBUG_EXIT_ANIMATION) {
Slog.v(TAG, "vanish animation finished");
}
- mSplashScreenView.post(() -> {
+
+ if (mSplashScreenView.isAttachedToWindow()) {
mSplashScreenView.setVisibility(GONE);
if (mFinishCallback != null) {
mFinishCallback.run();
mFinishCallback = null;
}
- });
+ }
if (mShiftUpAnimation != null) {
mShiftUpAnimation.finish();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 46db35a..670af96 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -288,6 +288,7 @@
// create splash screen view finished.
final SplashScreenViewSupplier viewSupplier = new SplashScreenViewSupplier();
final FrameLayout rootLayout = new FrameLayout(context);
+ rootLayout.setPadding(0, 0, 0, 0);
final Runnable setViewSynchronized = () -> {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addSplashScreenView");
// waiting for setContentView before relayoutWindow
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index acf7f33..382d580 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.startingsurface;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.graphics.Color.WHITE;
import static android.graphics.Color.alpha;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -63,6 +64,7 @@
import android.hardware.HardwareBuffer;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.Trace;
import android.util.MergedConfiguration;
import android.util.Slog;
@@ -116,11 +118,15 @@
private static final boolean DEBUG = StartingSurfaceDrawer.DEBUG_TASK_SNAPSHOT;
private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
+ private static final long DELAY_REMOVAL_TIME_GENERAL = 100;
+ private static final long DELAY_REMOVAL_TIME_IME_VISIBLE = 350;
+
//tmp vars for unused relayout params
private static final Point TMP_SURFACE_SIZE = new Point();
private final Window mWindow;
private final Runnable mClearWindowHandler;
+ private final long mDelayRemovalTime;
private final ShellExecutor mSplashScreenExecutor;
private final SurfaceControl mSurfaceControl;
private final IWindowSession mSession;
@@ -132,8 +138,10 @@
private final RectF mTmpDstFrame = new RectF();
private final CharSequence mTitle;
private boolean mHasDrawn;
+ private long mShownTime;
private boolean mSizeMismatch;
private final Paint mBackgroundPaint = new Paint();
+ private final int mActivityType;
private final int mStatusBarColor;
private final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
private final int mOrientationOnCreation;
@@ -190,6 +198,7 @@
final Point taskSize = snapshot.getTaskSize();
final Rect taskBounds = new Rect(0, 0, taskSize.x, taskSize.y);
final int orientation = snapshot.getOrientation();
+ final int activityType = runningTaskInfo.topActivityType;
final int displayId = runningTaskInfo.displayId;
final IWindowSession session = WindowManagerGlobal.getWindowSession();
@@ -207,10 +216,13 @@
taskDescription.setBackgroundColor(WHITE);
}
+ final long delayRemovalTime = snapshot.hasImeSurface() ? DELAY_REMOVAL_TIME_IME_VISIBLE
+ : DELAY_REMOVAL_TIME_GENERAL;
+
final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow(
surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, appearance,
- windowFlags, windowPrivateFlags, taskBounds, orientation,
- topWindowInsetsState, clearWindowHandler, splashScreenExecutor);
+ windowFlags, windowPrivateFlags, taskBounds, orientation, activityType,
+ delayRemovalTime, topWindowInsetsState, clearWindowHandler, splashScreenExecutor);
final Window window = snapshotSurface.mWindow;
final InsetsState mTmpInsetsState = new InsetsState();
@@ -248,7 +260,8 @@
public TaskSnapshotWindow(SurfaceControl surfaceControl,
TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription,
int appearance, int windowFlags, int windowPrivateFlags, Rect taskBounds,
- int currentOrientation, InsetsState topWindowInsetsState, Runnable clearWindowHandler,
+ int currentOrientation, int activityType, long delayRemovalTime,
+ InsetsState topWindowInsetsState, Runnable clearWindowHandler,
ShellExecutor splashScreenExecutor) {
mSplashScreenExecutor = splashScreenExecutor;
mSession = WindowManagerGlobal.getWindowSession();
@@ -264,6 +277,8 @@
windowPrivateFlags, appearance, taskDescription, 1f, topWindowInsetsState);
mStatusBarColor = taskDescription.getStatusBarColor();
mOrientationOnCreation = currentOrientation;
+ mActivityType = activityType;
+ mDelayRemovalTime = delayRemovalTime;
mTransaction = new SurfaceControl.Transaction();
mClearWindowHandler = clearWindowHandler;
}
@@ -286,6 +301,17 @@
}
void remove() {
+ final long now = SystemClock.uptimeMillis();
+ if ((now - mShownTime < mDelayRemovalTime)
+ // Show the latest content as soon as possible for unlocking to home.
+ && mActivityType != ACTIVITY_TYPE_HOME) {
+ final long delayTime = mShownTime + mDelayRemovalTime - now;
+ mSplashScreenExecutor.executeDelayed(() -> remove(), delayTime);
+ if (DEBUG) {
+ Slog.d(TAG, "Defer removing snapshot surface in " + delayTime);
+ }
+ return;
+ }
try {
if (DEBUG) {
Slog.d(TAG, "Removing snapshot surface, mHasDrawn: " + mHasDrawn);
@@ -326,6 +352,7 @@
} else {
drawSizeMatchSnapshot();
}
+ mShownTime = SystemClock.uptimeMillis();
mHasDrawn = true;
reportDrawn();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java
index 9637570..3c124ba 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java
@@ -50,6 +50,7 @@
import com.android.internal.R;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
@@ -82,6 +83,8 @@
@Mock
private Display mDisplay;
@Mock
+ private DisplayLayout mDisplayLayout;
+ @Mock
private IWindowContainerToken mMockRealToken;
private WindowContainerToken mToken;
@@ -95,6 +98,7 @@
MockitoAnnotations.initMocks(this);
when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
+ when(mMockDisplayController.getDisplayLayout(anyInt())).thenReturn(mDisplayLayout);
HideDisplayCutoutOrganizer organizer = new HideDisplayCutoutOrganizer(
mContext, mMockDisplayController, mMockMainExecutor);
@@ -152,7 +156,7 @@
.getDisplayCutoutInsetsOfNaturalOrientation();
mContext.getOrCreateTestableResources().addOverride(
R.dimen.status_bar_height_portrait, mFakeStatusBarHeightPortrait);
- doReturn(Surface.ROTATION_0).when(mDisplay).getRotation();
+ doReturn(Surface.ROTATION_0).when(mDisplayLayout).rotation();
mOrganizer.enableHideDisplayCutout();
verify(mOrganizer).registerOrganizer(DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT);
@@ -171,7 +175,7 @@
.getDisplayCutoutInsetsOfNaturalOrientation();
mContext.getOrCreateTestableResources().addOverride(
R.dimen.status_bar_height_landscape, mFakeStatusBarHeightLandscape);
- doReturn(Surface.ROTATION_90).when(mDisplay).getRotation();
+ doReturn(Surface.ROTATION_90).when(mDisplayLayout).rotation();
mOrganizer.enableHideDisplayCutout();
verify(mOrganizer).registerOrganizer(DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT);
@@ -190,7 +194,7 @@
.getDisplayCutoutInsetsOfNaturalOrientation();
mContext.getOrCreateTestableResources().addOverride(
R.dimen.status_bar_height_landscape, mFakeStatusBarHeightLandscape);
- doReturn(Surface.ROTATION_270).when(mDisplay).getRotation();
+ doReturn(Surface.ROTATION_270).when(mDisplayLayout).rotation();
mOrganizer.enableHideDisplayCutout();
verify(mOrganizer).registerOrganizer(DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT);
@@ -219,4 +223,22 @@
assertThat(mOrganizer.mOffsetX).isEqualTo(0);
assertThat(mOrganizer.mOffsetY).isEqualTo(0);
}
+
+ @Test
+ public void testDisplaySizeChange() {
+ doReturn(100).when(mDisplayLayout).width();
+ doReturn(200).when(mDisplayLayout).height();
+ doReturn(mFakeDefaultCutoutInsets).when(mOrganizer)
+ .getDisplayCutoutInsetsOfNaturalOrientation();
+ mContext.getOrCreateTestableResources().addOverride(
+ R.dimen.status_bar_height_portrait, mFakeStatusBarHeightPortrait);
+ doReturn(Surface.ROTATION_0).when(mDisplayLayout).rotation();
+ mOrganizer.enableHideDisplayCutout();
+ assertThat(mOrganizer.mCurrentDisplayBounds).isEqualTo(new Rect(0, 15, 100, 200));
+
+ doReturn(200).when(mDisplayLayout).width();
+ doReturn(400).when(mDisplayLayout).height();
+ mOrganizer.updateBoundsAndOffsets(true);
+ assertThat(mOrganizer.mCurrentDisplayBounds).isEqualTo(new Rect(0, 15, 200, 400));
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index 1852279..be786fb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -74,6 +74,8 @@
@Mock
OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
@Mock
+ OneHandedEventCallback mMockEventCallback;
+ @Mock
OneHandedTouchHandler mMockTouchHandler;
@Mock
OneHandedTutorialHandler mMockTutorialHandler;
@@ -106,6 +108,7 @@
when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
+ when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true);
when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash);
when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn(
mDefaultEnabled);
@@ -115,6 +118,7 @@
mDefaultTapAppToExitEnabled);
when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn(
mDefaultSwipeToNotificationEnabled);
+ when(mMockSettingsUitl.getShortcutEnabled(any(), anyInt())).thenReturn(false);
when(mMockDisplayAreaOrganizer.getLastDisplayBounds()).thenReturn(
new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height()));
@@ -241,7 +245,7 @@
@Test
public void testSettingsObserverUpdateSwipeToNotification() {
- mSpiedOneHandedController.onSwipeToNotificationEnabledSettingChanged();
+ mSpiedOneHandedController.onSwipeToNotificationEnabledChanged();
verify(mSpiedOneHandedController).setSwipeToNotificationEnabled(anyBoolean());
}
@@ -311,6 +315,7 @@
final DisplayLayout testDisplayLayout = new DisplayLayout(mDisplayLayout);
testDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180);
mSpiedTransitionState.setState(STATE_NONE);
+ when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true);
when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(testDisplayLayout);
mSpiedOneHandedController.setOneHandedEnabled(true);
mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */);
@@ -337,6 +342,7 @@
when(mSpiedTransitionState.getState()).thenReturn(STATE_ACTIVE);
when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
+ when(mSpiedOneHandedController.isShortcutEnabled()).thenReturn(true);
mSpiedOneHandedController.onActivatedActionChanged();
verify(mSpiedOneHandedController, never()).startOneHanded();
@@ -348,6 +354,7 @@
when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(false);
+ when(mSpiedOneHandedController.isShortcutEnabled()).thenReturn(true);
mSpiedOneHandedController.onActivatedActionChanged();
verify(mSpiedOneHandedController, never()).startOneHanded();
@@ -359,6 +366,7 @@
when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
+ when(mSpiedOneHandedController.isShortcutEnabled()).thenReturn(true);
mSpiedOneHandedController.onActivatedActionChanged();
verify(mSpiedOneHandedController).startOneHanded();
@@ -370,10 +378,10 @@
when(mSpiedTransitionState.getState()).thenReturn(STATE_ENTERING);
when(mSpiedTransitionState.isTransitioning()).thenReturn(true);
when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
+ when(mSpiedOneHandedController.isShortcutEnabled()).thenReturn(true);
mSpiedOneHandedController.onActivatedActionChanged();
- verify(mSpiedOneHandedController, never()).startOneHanded();
- verify(mSpiedOneHandedController, never()).stopOneHanded();
+ verify(mSpiedTransitionState, never()).setState(STATE_EXITING);
}
@Test
@@ -381,22 +389,24 @@
when(mSpiedTransitionState.getState()).thenReturn(STATE_EXITING);
when(mSpiedTransitionState.isTransitioning()).thenReturn(true);
when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
+ when(mSpiedOneHandedController.isShortcutEnabled()).thenReturn(true);
mSpiedOneHandedController.onActivatedActionChanged();
- verify(mSpiedOneHandedController, never()).startOneHanded();
- verify(mSpiedOneHandedController, never()).stopOneHanded();
+ verify(mSpiedTransitionState, never()).setState(STATE_ENTERING);
}
@Test
- public void testOneHandedDisabled_shortcutEnabled_skipActions() {
+ public void testOneHandedDisabled_shortcutTrigger_thenAutoEnabled() {
when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false);
+ when(mSpiedOneHandedController.isShortcutEnabled()).thenReturn(true);
when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
- when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true);
+ when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(false);
+ when(mMockSettingsUitl.setOneHandedModeEnabled(any(), anyInt(), anyInt())).thenReturn(
+ false);
mSpiedOneHandedController.onActivatedActionChanged();
- verify(mSpiedOneHandedController, never()).startOneHanded();
- verify(mSpiedOneHandedController, never()).stopOneHanded();
+ verify(mSpiedOneHandedController).notifyUserConfigChanged(anyBoolean());
}
@Test
@@ -408,4 +418,69 @@
verify(mSpiedTransitionState).addSListeners(mMockTutorialHandler);
}
+
+ @Test
+ public void testNotifyEventCallbackWithMainExecutor() {
+ when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(true);
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+ when(mSpiedOneHandedController.isShortcutEnabled()).thenReturn(true);
+ when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(true);
+ mSpiedOneHandedController.registerEventCallback(mMockEventCallback);
+ mSpiedOneHandedController.onActivatedActionChanged();
+
+ verify(mMockShellMainExecutor).execute(any());
+ }
+
+ @Test
+ public void testNotifyShortcutState_whenSetOneHandedEnabled() {
+ when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(true);
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+ when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(false);
+ mSpiedOneHandedController.registerEventCallback(mMockEventCallback);
+ mSpiedOneHandedController.setOneHandedEnabled(true);
+
+ verify(mSpiedOneHandedController).notifyShortcutState(anyInt());
+ }
+
+ @Test
+ public void testNotifyExpandNotification_withNullCheckProtection() {
+ when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false);
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+ when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(true);
+ mSpiedOneHandedController.setOneHandedEnabled(true);
+ mSpiedOneHandedController.notifyExpandNotification();
+
+ // Verify no NPE crash and mMockShellMainExecutor never be execute.
+ verify(mMockShellMainExecutor, never()).execute(any());
+ }
+
+ @Test
+ public void testShortcutEnable_ableToAutoEnableOneHandedMode() {
+ when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false);
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+ when(mSpiedOneHandedController.isShortcutEnabled()).thenReturn(true);
+ when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(false);
+ when(mMockSettingsUitl.setOneHandedModeEnabled(any(), anyInt(), anyInt())).thenReturn(
+ false /* To avoid test runner create Toast */);
+ mSpiedOneHandedController.onActivatedActionChanged();
+
+ verify(mSpiedOneHandedController).notifyUserConfigChanged(anyBoolean());
+ }
+
+ @Test
+ public void testShortcutDisable_shouldNotAutoEnableOneHandedMode() {
+ when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false);
+ when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
+ when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
+ when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(false);
+ when(mMockSettingsUitl.setOneHandedModeEnabled(any(), anyInt(), anyInt())).thenReturn(true);
+ mSpiedOneHandedController.onActivatedActionChanged();
+
+ verify(mMockSettingsUitl, never()).setOneHandedModeEnabled(any(), anyInt(), anyInt());
+ verify(mSpiedOneHandedController, never()).notifyUserConfigChanged(anyBoolean());
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index a27ed11..ef16fd3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -418,4 +418,18 @@
verify(mSpiedDisplayAreaOrganizer, never()).resetWindowsOffset();
}
+
+ @Test
+ public void testDisplayArea_notReadyForTransition() {
+ OneHandedDisplayAreaOrganizer testSpiedDisplayAreaOrganizer = spy(
+ new OneHandedDisplayAreaOrganizer(mContext,
+ mDisplayLayout,
+ mMockSettingsUitl,
+ mMockAnimationController,
+ mTutorialHandler,
+ mMockBackgroundOrganizer,
+ mMockShellMainExecutor));
+
+ assertThat(testSpiedDisplayAreaOrganizer.isReady()).isFalse();
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
index 5945840..a098a68 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.startingsurface;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
@@ -81,7 +82,8 @@
mWindow = new TaskSnapshotWindow(new SurfaceControl(), snapshot, "Test",
createTaskDescription(Color.WHITE, Color.RED, Color.BLUE),
0 /* appearance */, windowFlags /* windowFlags */, 0 /* privateWindowFlags */,
- taskBounds, ORIENTATION_PORTRAIT, new InsetsState(),
+ taskBounds, ORIENTATION_PORTRAIT, ACTIVITY_TYPE_STANDARD,
+ 100 /* delayRemovalTime */, new InsetsState(),
null /* clearWindow */, new TestShellExecutor());
}
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 332f7e6..44c335f 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -455,8 +455,7 @@
if (hasLayer()) {
this->setLayerSurface(nullptr);
}
- mSnapshotResult.snapshot = nullptr;
- mTargetImageFilter = nullptr;
+
if (mDisplayList) {
mDisplayList.updateChildren([](RenderNode* child) { child->destroyLayers(); });
}
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 8595b6e..45a4f6c 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -330,6 +330,13 @@
} else {
mSkiaLayer.reset();
}
+
+ mProperties.mutateLayerProperties().mutableStretchEffect().clear();
+ mStretchMask.clear();
+ // Clear out the previous snapshot and the image filter the previous
+ // snapshot was created with whenever the layer changes.
+ mSnapshotResult.snapshot = nullptr;
+ mTargetImageFilter = nullptr;
}
/**
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
index d9b9e24..92e20c4 100644
--- a/libs/hwui/WebViewFunctorManager.cpp
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -197,6 +197,8 @@
auto funcs = renderthread::RenderThread::getInstance().getASurfaceControlFunctions();
mSurfaceControl = funcs.createFunc(rootSurfaceControl, "Webview Overlay SurfaceControl");
ASurfaceTransaction* transaction = funcs.transactionCreateFunc();
+ activeContext->prepareSurfaceControlForWebview();
+ funcs.transactionSetZOrderFunc(transaction, mSurfaceControl, -1);
funcs.transactionSetVisibilityFunc(transaction, mSurfaceControl,
ASURFACE_TRANSACTION_VISIBILITY_SHOW);
funcs.transactionApplyFunc(transaction);
diff --git a/libs/hwui/effects/StretchEffect.cpp b/libs/hwui/effects/StretchEffect.cpp
index 43f805d..17cd3ce 100644
--- a/libs/hwui/effects/StretchEffect.cpp
+++ b/libs/hwui/effects/StretchEffect.cpp
@@ -186,6 +186,7 @@
static const float ZERO = 0.f;
static const float INTERPOLATION_STRENGTH_VALUE = 0.7f;
+static const char CONTENT_TEXTURE[] = "uContentTexture";
sk_sp<SkShader> StretchEffect::getShader(float width, float height,
const sk_sp<SkImage>& snapshotImage,
@@ -207,7 +208,7 @@
mBuilder = std::make_unique<SkRuntimeShaderBuilder>(getStretchEffect());
}
- mBuilder->child("uContentTexture") =
+ mBuilder->child(CONTENT_TEXTURE) =
snapshotImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
SkSamplingOptions(SkFilterMode::kLinear), matrix);
mBuilder->uniform("uInterpolationStrength").set(&INTERPOLATION_STRENGTH_VALUE, 1);
@@ -226,7 +227,9 @@
mBuilder->uniform("viewportWidth").set(&width, 1);
mBuilder->uniform("viewportHeight").set(&height, 1);
- return mBuilder->makeShader(nullptr, false);
+ auto result = mBuilder->makeShader(nullptr, false);
+ mBuilder->child(CONTENT_TEXTURE) = nullptr;
+ return result;
}
sk_sp<SkRuntimeEffect> StretchEffect::getStretchEffect() {
diff --git a/libs/hwui/effects/StretchEffect.h b/libs/hwui/effects/StretchEffect.h
index 25777c2..3eab9f0 100644
--- a/libs/hwui/effects/StretchEffect.h
+++ b/libs/hwui/effects/StretchEffect.h
@@ -113,6 +113,10 @@
return !isEmpty();
}
+ void clear() {
+ mBuilder = nullptr;
+ }
+
private:
static sk_sp<SkRuntimeEffect> getStretchEffect();
mutable SkVector mStretchDirection{0, 0};
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 602c32a..4d31cd9 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -71,6 +71,10 @@
} gASurfaceTransactionCallback;
struct {
+ jmethodID prepare;
+} gPrepareSurfaceControlForWebviewCallback;
+
+struct {
jmethodID onFrameDraw;
} gFrameDrawingCallback;
@@ -500,6 +504,28 @@
jobject mObject;
};
+class JWeakGlobalRefHolder {
+public:
+ JWeakGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm) {
+ mWeakRef = getenv(vm)->NewWeakGlobalRef(object);
+ }
+
+ virtual ~JWeakGlobalRefHolder() {
+ if (mWeakRef != nullptr) getenv(mVm)->DeleteWeakGlobalRef(mWeakRef);
+ mWeakRef = nullptr;
+ }
+
+ jobject ref() { return mWeakRef; }
+ JavaVM* vm() { return mVm; }
+
+private:
+ JWeakGlobalRefHolder(const JWeakGlobalRefHolder&) = delete;
+ void operator=(const JWeakGlobalRefHolder&) = delete;
+
+ JavaVM* mVm;
+ jobject mWeakRef;
+};
+
using TextureMap = std::unordered_map<uint32_t, sk_sp<SkImage>>;
struct PictureCaptureState {
@@ -633,19 +659,47 @@
} else {
JavaVM* vm = nullptr;
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
- auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(
- vm, env->NewGlobalRef(aSurfaceTransactionCallback));
+ auto globalCallbackRef =
+ std::make_shared<JWeakGlobalRefHolder>(vm, aSurfaceTransactionCallback);
proxy->setASurfaceTransactionCallback(
- [globalCallbackRef](int64_t transObj, int64_t scObj, int64_t frameNr) {
+ [globalCallbackRef](int64_t transObj, int64_t scObj, int64_t frameNr) -> bool {
JNIEnv* env = getenv(globalCallbackRef->vm());
- env->CallVoidMethod(globalCallbackRef->object(),
- gASurfaceTransactionCallback.onMergeTransaction,
- static_cast<jlong>(transObj), static_cast<jlong>(scObj),
- static_cast<jlong>(frameNr));
+ jobject localref = env->NewLocalRef(globalCallbackRef->ref());
+ if (CC_UNLIKELY(!localref)) {
+ return false;
+ }
+ jboolean ret = env->CallBooleanMethod(
+ localref, gASurfaceTransactionCallback.onMergeTransaction,
+ static_cast<jlong>(transObj), static_cast<jlong>(scObj),
+ static_cast<jlong>(frameNr));
+ env->DeleteLocalRef(localref);
+ return ret;
});
}
}
+static void android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCallback(
+ JNIEnv* env, jobject clazz, jlong proxyPtr, jobject callback) {
+ RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+ if (!callback) {
+ proxy->setPrepareSurfaceControlForWebviewCallback(nullptr);
+ } else {
+ JavaVM* vm = nullptr;
+ LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
+ auto globalCallbackRef =
+ std::make_shared<JWeakGlobalRefHolder>(vm, callback);
+ proxy->setPrepareSurfaceControlForWebviewCallback([globalCallbackRef]() {
+ JNIEnv* env = getenv(globalCallbackRef->vm());
+ jobject localref = env->NewLocalRef(globalCallbackRef->ref());
+ if (CC_UNLIKELY(!localref)) {
+ return;
+ }
+ env->CallVoidMethod(localref, gPrepareSurfaceControlForWebviewCallback.prepare);
+ env->DeleteLocalRef(localref);
+ });
+ }
+}
+
static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env,
jobject clazz, jlong proxyPtr, jobject frameCallback) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -944,6 +998,9 @@
{"nSetASurfaceTransactionCallback",
"(JLandroid/graphics/HardwareRenderer$ASurfaceTransactionCallback;)V",
(void*)android_view_ThreadedRenderer_setASurfaceTransactionCallback},
+ {"nSetPrepareSurfaceControlForWebviewCallback",
+ "(JLandroid/graphics/HardwareRenderer$PrepareSurfaceControlForWebviewCallback;)V",
+ (void*)android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCallback},
{"nSetFrameCallback", "(JLandroid/graphics/HardwareRenderer$FrameDrawingCallback;)V",
(void*)android_view_ThreadedRenderer_setFrameCallback},
{"nSetFrameCompleteCallback",
@@ -1009,7 +1066,12 @@
jclass aSurfaceTransactionCallbackClass =
FindClassOrDie(env, "android/graphics/HardwareRenderer$ASurfaceTransactionCallback");
gASurfaceTransactionCallback.onMergeTransaction =
- GetMethodIDOrDie(env, aSurfaceTransactionCallbackClass, "onMergeTransaction", "(JJJ)V");
+ GetMethodIDOrDie(env, aSurfaceTransactionCallbackClass, "onMergeTransaction", "(JJJ)Z");
+
+ jclass prepareSurfaceControlForWebviewCallbackClass = FindClassOrDie(
+ env, "android/graphics/HardwareRenderer$PrepareSurfaceControlForWebviewCallback");
+ gPrepareSurfaceControlForWebviewCallback.prepare =
+ GetMethodIDOrDie(env, prepareSurfaceControlForWebviewCallbackClass, "prepare", "()V");
jclass frameCallbackClass = FindClassOrDie(env,
"android/graphics/HardwareRenderer$FrameDrawingCallback");
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 8bfc2c1..0c9711b 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -174,16 +174,12 @@
ATRACE_CALL();
if (window) {
- int extraBuffers = 0;
- native_window_get_extra_buffer_count(window, &extraBuffers);
-
mNativeSurface = std::make_unique<ReliableSurface>(window);
mNativeSurface->init();
if (enableTimeout) {
// TODO: Fix error handling & re-shorten timeout
ANativeWindow_setDequeueTimeout(window, 4000_ms);
}
- mNativeSurface->setExtraBufferCount(extraBuffers);
} else {
mNativeSurface = nullptr;
}
@@ -197,6 +193,7 @@
if (surfaceControl == nullptr) {
setASurfaceTransactionCallback(nullptr);
+ setPrepareSurfaceControlForWebviewCallback(nullptr);
}
if (mSurfaceControl != nullptr) {
@@ -913,9 +910,14 @@
bool CanvasContext::mergeTransaction(ASurfaceTransaction* transaction, ASurfaceControl* control) {
if (!mASurfaceTransactionCallback) return false;
- std::invoke(mASurfaceTransactionCallback, reinterpret_cast<int64_t>(transaction),
- reinterpret_cast<int64_t>(control), getFrameNumber());
- return true;
+ return std::invoke(mASurfaceTransactionCallback, reinterpret_cast<int64_t>(transaction),
+ reinterpret_cast<int64_t>(control), getFrameNumber());
+}
+
+void CanvasContext::prepareSurfaceControlForWebview() {
+ if (mPrepareSurfaceControlForWebviewCallback) {
+ std::invoke(mPrepareSurfaceControlForWebviewCallback);
+ }
}
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 4bdc251..3279ccb 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -206,12 +206,18 @@
ASurfaceControlStats* stats);
void setASurfaceTransactionCallback(
- const std::function<void(int64_t, int64_t, int64_t)>& callback) {
+ const std::function<bool(int64_t, int64_t, int64_t)>& callback) {
mASurfaceTransactionCallback = callback;
}
bool mergeTransaction(ASurfaceTransaction* transaction, ASurfaceControl* control);
+ void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback) {
+ mPrepareSurfaceControlForWebviewCallback = callback;
+ }
+
+ void prepareSurfaceControlForWebview();
+
static CanvasContext* getActiveContext();
private:
@@ -311,7 +317,9 @@
// If set to true, we expect that callbacks into onSurfaceStatsAvailable
bool mExpectSurfaceStats = false;
- std::function<void(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback;
+ std::function<bool(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback;
+ std::function<void()> mPrepareSurfaceControlForWebviewCallback;
+
void cleanupResources();
};
diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp
index c29cc11..6df34be 100644
--- a/libs/hwui/renderthread/ReliableSurface.cpp
+++ b/libs/hwui/renderthread/ReliableSurface.cpp
@@ -278,7 +278,6 @@
int result = query(window, what, value);
if (what == ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS && result == OK) {
std::lock_guard _lock{rs->mMutex};
- *value += rs->mExtraBuffers;
rs->mExpectedBufferCount = *value + 2;
}
return result;
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
index 41969e7..5959647 100644
--- a/libs/hwui/renderthread/ReliableSurface.h
+++ b/libs/hwui/renderthread/ReliableSurface.h
@@ -51,11 +51,6 @@
return ret;
}
- void setExtraBufferCount(size_t extraBuffers) {
- std::lock_guard _lock{mMutex};
- mExtraBuffers = extraBuffers;
- }
-
bool didSetExtraBuffers() const {
std::lock_guard _lock{mMutex};
return mDidSetExtraBuffers;
@@ -73,7 +68,6 @@
base::unique_fd mReservedFenceFd;
bool mHasDequeuedBuffer = false;
int mBufferQueueState = OK;
- size_t mExtraBuffers = 0;
size_t mExpectedBufferCount = 0;
bool mDidSetExtraBuffers = false;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 6fd644b..a77b5b5 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -314,11 +314,17 @@
}
void RenderProxy::setASurfaceTransactionCallback(
- const std::function<void(int64_t, int64_t, int64_t)>& callback) {
+ const std::function<bool(int64_t, int64_t, int64_t)>& callback) {
mRenderThread.queue().post(
[this, cb = callback]() { mContext->setASurfaceTransactionCallback(cb); });
}
+void RenderProxy::setPrepareSurfaceControlForWebviewCallback(
+ const std::function<void()>& callback) {
+ mRenderThread.queue().post(
+ [this, cb = callback]() { mContext->setPrepareSurfaceControlForWebviewCallback(cb); });
+}
+
void RenderProxy::setFrameCallback(std::function<void(int64_t)>&& callback) {
mDrawFrameTask.setFrameCallback(std::move(callback));
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 6d80949..1b0f22e 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -123,7 +123,8 @@
void setContentDrawBounds(int left, int top, int right, int bottom);
void setPictureCapturedCallback(const std::function<void(sk_sp<SkPicture>&&)>& callback);
void setASurfaceTransactionCallback(
- const std::function<void(int64_t, int64_t, int64_t)>& callback);
+ const std::function<bool(int64_t, int64_t, int64_t)>& callback);
+ void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback);
void setFrameCallback(std::function<void(int64_t)>&& callback);
void setFrameCompleteCallback(std::function<void(int64_t)>&& callback);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 4ba7748..524407d 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -102,6 +102,10 @@
(AST_setVisibility)dlsym(handle_, "ASurfaceTransaction_setVisibility");
LOG_ALWAYS_FATAL_IF(transactionSetVisibilityFunc == nullptr,
"Failed to find required symbol ASurfaceTransaction_setVisibility!");
+
+ transactionSetZOrderFunc = (AST_setZOrder)dlsym(handle_, "ASurfaceTransaction_setZOrder");
+ LOG_ALWAYS_FATAL_IF(transactionSetZOrderFunc == nullptr,
+ "Failed to find required symbol ASurfaceTransaction_setZOrder!");
}
void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index dd06009..c5e3746 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -96,6 +96,8 @@
typedef void (*AST_apply)(ASurfaceTransaction* transaction);
typedef void (*AST_setVisibility)(ASurfaceTransaction* transaction,
ASurfaceControl* surface_control, int8_t visibility);
+typedef void (*AST_setZOrder)(ASurfaceTransaction* transaction, ASurfaceControl* surface_control,
+ int32_t z_order);
struct ASurfaceControlFunctions {
ASurfaceControlFunctions();
@@ -112,6 +114,7 @@
AST_delete transactionDeleteFunc;
AST_apply transactionApplyFunc;
AST_setVisibility transactionSetVisibilityFunc;
+ AST_setZOrder transactionSetZOrderFunc;
};
class ChoreographerSource;
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index f446678..ecdd4b6 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -214,6 +214,25 @@
*
* <p> When this bit is unset, the {@link #getAccumulatedDeltaRangeMeters()} corresponds to the
* carrier phase measurement plus an accumulated integer number of carrier half cycles.
+ *
+ * <p> For signals that have databits, the carrier phase tracking loops typically use a costas
+ * loop discriminator. This type of tracking loop introduces a half-cycle ambiguity that is
+ * resolved by searching through the received data for known patterns of databits (e.g. GPS uses
+ * the TLM word) which then determines the polarity of the incoming data and resolves the
+ * half-cycle ambiguity.
+ *
+ * <p>Before the half-cycle ambiguity has been resolved it is possible that the ADR_STATE_VALID
+ * flag is set:
+ *
+ * <ul>
+ * <li> In cases where ADR_STATE_HALF_CYCLE_REPORTED is not set, the
+ * ADR_STATE_HALF_CYCLE_RESOLVED flag will not be available. Here, a half wave length will be
+ * added to the returned accumulated delta range uncertainty to indicate the half cycle
+ * ambiguity.
+ * <li> In cases where ADR_STATE_HALF_CYCLE_REPORTED is set, half cycle ambiguity will be
+ * indicated via both the ADR_STATE_HALF_CYCLE_RESOLVED flag and as well a half wave length
+ * added to the returned accumulated delta range uncertainty.
+ * </ul>
*/
public static final int ADR_STATE_HALF_CYCLE_RESOLVED = (1<<3);
@@ -363,9 +382,9 @@
}
/**
- * Gets per-satellite sync state.
+ * Gets per-satellite-signal sync state.
*
- * <p>It represents the current sync state for the associated satellite.
+ * <p>It represents the current sync state for the associated satellite signal.
*
* <p>This value helps interpret {@link #getReceivedSvTimeNanos()}.
*/
@@ -1039,9 +1058,6 @@
* with integer ambiguity resolution, to determine highly precise relative location between
* receivers.
*
- * <p>This includes ensuring that all half-cycle ambiguities are resolved before this value is
- * reported as {@link #ADR_STATE_VALID}.
- *
* <p>The alignment of the phase measurement will not be adjusted by the receiver so the
* in-phase and quadrature phase components will have a quarter cycle offset as they do when
* transmitted from the satellites. If the measurement is from a combination of the in-phase
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index c9e4e0a..5d5c0fc 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -122,6 +122,9 @@
boolean isLocationEnabledForUser(int userId);
void setLocationEnabledForUser(boolean enabled, int userId);
+ boolean isAdasGnssLocationEnabledForUser(int userId);
+ void setAdasGnssLocationEnabledForUser(boolean enabled, int userId);
+
void addTestProvider(String name, in ProviderProperties properties,
in List<String> locationTags, String packageName, @nullable String attributionTag);
void removeTestProvider(String provider, String packageName, @nullable String attributionTag);
diff --git a/location/java/android/location/LastLocationRequest.java b/location/java/android/location/LastLocationRequest.java
index 9ea8048..0970c1c 100644
--- a/location/java/android/location/LastLocationRequest.java
+++ b/location/java/android/location/LastLocationRequest.java
@@ -34,12 +34,15 @@
public final class LastLocationRequest implements Parcelable {
private final boolean mHiddenFromAppOps;
+ private final boolean mAdasGnssBypass;
private final boolean mLocationSettingsIgnored;
private LastLocationRequest(
boolean hiddenFromAppOps,
+ boolean adasGnssBypass,
boolean locationSettingsIgnored) {
mHiddenFromAppOps = hiddenFromAppOps;
+ mAdasGnssBypass = adasGnssBypass;
mLocationSettingsIgnored = locationSettingsIgnored;
}
@@ -56,6 +59,21 @@
}
/**
+ * Returns true if this request may access GNSS even if location settings would normally deny
+ * this, in order to enable automotive safety features. This field is only respected on
+ * automotive devices, and only if the client is recognized as a legitimate ADAS (Advanced
+ * Driving Assistance Systems) application.
+ *
+ * @return true if all limiting factors will be ignored to satisfy GNSS request
+ * @hide
+ */
+ // TODO: make this system api
+ public boolean isAdasGnssBypass() {
+ return mAdasGnssBypass;
+ }
+
+
+ /**
* Returns true if location settings, throttling, background location limits, and any other
* possible limiting factors will be ignored in order to satisfy this last location request.
*
@@ -65,12 +83,22 @@
return mLocationSettingsIgnored;
}
+ /**
+ * Returns true if any bypass flag is set on this request. For internal use only.
+ *
+ * @hide
+ */
+ public boolean isBypass() {
+ return mAdasGnssBypass || mLocationSettingsIgnored;
+ }
+
public static final @NonNull Parcelable.Creator<LastLocationRequest> CREATOR =
new Parcelable.Creator<LastLocationRequest>() {
@Override
public LastLocationRequest createFromParcel(Parcel in) {
return new LastLocationRequest(
/* hiddenFromAppOps= */ in.readBoolean(),
+ /* adasGnssBypass= */ in.readBoolean(),
/* locationSettingsIgnored= */ in.readBoolean());
}
@Override
@@ -86,6 +114,7 @@
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeBoolean(mHiddenFromAppOps);
+ parcel.writeBoolean(mAdasGnssBypass);
parcel.writeBoolean(mLocationSettingsIgnored);
}
@@ -99,12 +128,13 @@
}
LastLocationRequest that = (LastLocationRequest) o;
return mHiddenFromAppOps == that.mHiddenFromAppOps
+ && mAdasGnssBypass == that.mAdasGnssBypass
&& mLocationSettingsIgnored == that.mLocationSettingsIgnored;
}
@Override
public int hashCode() {
- return Objects.hash(mHiddenFromAppOps, mLocationSettingsIgnored);
+ return Objects.hash(mHiddenFromAppOps, mAdasGnssBypass, mLocationSettingsIgnored);
}
@NonNull
@@ -115,8 +145,11 @@
if (mHiddenFromAppOps) {
s.append("hiddenFromAppOps, ");
}
+ if (mAdasGnssBypass) {
+ s.append("adasGnssBypass, ");
+ }
if (mLocationSettingsIgnored) {
- s.append("locationSettingsIgnored, ");
+ s.append("settingsBypass, ");
}
if (s.length() > "LastLocationRequest[".length()) {
s.setLength(s.length() - 2);
@@ -131,6 +164,7 @@
public static final class Builder {
private boolean mHiddenFromAppOps;
+ private boolean mAdasGnssBypass;
private boolean mLocationSettingsIgnored;
/**
@@ -138,6 +172,7 @@
*/
public Builder() {
mHiddenFromAppOps = false;
+ mAdasGnssBypass = false;
mLocationSettingsIgnored = false;
}
@@ -146,6 +181,7 @@
*/
public Builder(@NonNull LastLocationRequest lastLocationRequest) {
mHiddenFromAppOps = lastLocationRequest.mHiddenFromAppOps;
+ mAdasGnssBypass = lastLocationRequest.mAdasGnssBypass;
mLocationSettingsIgnored = lastLocationRequest.mLocationSettingsIgnored;
}
@@ -164,6 +200,25 @@
}
/**
+ * If set to true, indicates that the client is an ADAS (Advanced Driving Assistance
+ * Systems) client, which requires access to GNSS even if location settings would normally
+ * deny this, in order to enable auto safety features. This field is only respected on
+ * automotive devices, and only if the client is recognized as a legitimate ADAS
+ * application. Defaults to false.
+ *
+ * <p>Permissions enforcement occurs when resulting location request is actually used, not
+ * when this method is invoked.
+ *
+ * @hide
+ */
+ // TODO: make this system api
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ public @NonNull LastLocationRequest.Builder setAdasGnssBypass(boolean adasGnssBypass) {
+ mAdasGnssBypass = adasGnssBypass;
+ return this;
+ }
+
+ /**
* If set to true, indicates that location settings, throttling, background location limits,
* and any other possible limiting factors should be ignored in order to satisfy this
* last location request. This is only intended for use in user initiated emergency
@@ -186,6 +241,7 @@
public @NonNull LastLocationRequest build() {
return new LastLocationRequest(
mHiddenFromAppOps,
+ mAdasGnssBypass,
mLocationSettingsIgnored);
}
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index ae44c5e..526b84e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -315,6 +315,33 @@
public static final String EXTRA_LOCATION_ENABLED = "android.location.extra.LOCATION_ENABLED";
/**
+ * Broadcast intent action when the ADAS (Advanced Driving Assistance Systems) GNSS location
+ * enabled state changes. Includes a boolean intent extra, {@link #EXTRA_ADAS_GNSS_ENABLED},
+ * with the enabled state of ADAS GNSS location. This broadcast only has meaning on automotive
+ * devices.
+ *
+ * @see #EXTRA_ADAS_GNSS_ENABLED
+ * @see #isAdasGnssLocationEnabled()
+ *
+ * @hide
+ */
+ // TODO: @SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_ADAS_GNSS_ENABLED_CHANGED =
+ "android.location.action.ADAS_GNSS_ENABLED_CHANGED";
+
+ /**
+ * Intent extra included with {@link #ACTION_ADAS_GNSS_ENABLED_CHANGED} broadcasts, containing
+ * the boolean enabled state of ADAS GNSS location.
+ *
+ * @see #ACTION_ADAS_GNSS_ENABLED_CHANGED
+ *
+ * @hide
+ */
+ // TODO: @SystemApi
+ public static final String EXTRA_ADAS_GNSS_ENABLED = "android.location.extra.ADAS_GNSS_ENABLED";
+
+ /**
* Broadcast intent action indicating that a high power location requests
* has either started or stopped being active. The current state of
* active location requests should be read from AppOpsManager using
@@ -621,6 +648,42 @@
}
/**
+ * Returns the current enabled/disabled state of ADAS (Advanced Driving Assistance Systems)
+ * GNSS location access for the given user. This controls safety critical automotive access to
+ * GNSS location. This only has meaning on automotive devices.
+ *
+ * @return true if ADAS location is enabled and false if ADAS location is disabled.
+ *
+ * @hide
+ */
+ //TODO: @SystemApi
+ public boolean isAdasGnssLocationEnabled() {
+ try {
+ return mService.isAdasGnssLocationEnabledForUser(mContext.getUser().getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Enables or disables ADAS (Advanced Driving Assistance Systems) GNSS location access for the
+ * given user. This only has meaning on automotive devices.
+ *
+ * @param enabled true to enable ADAS location and false to disable ADAS location.
+ *
+ * @hide
+ */
+ // TODO: @SystemApi
+ @RequiresPermission(WRITE_SECURE_SETTINGS)
+ public void setAdasGnssLocationEnabled(boolean enabled) {
+ try {
+ mService.setAdasGnssLocationEnabledForUser(enabled, mContext.getUser().getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the current enabled/disabled status of the given provider. To listen for changes, see
* {@link #PROVIDERS_CHANGED_ACTION}.
*
diff --git a/location/java/android/location/LocationManagerInternal.java b/location/java/android/location/LocationManagerInternal.java
index 763835c..d59756d 100644
--- a/location/java/android/location/LocationManagerInternal.java
+++ b/location/java/android/location/LocationManagerInternal.java
@@ -16,14 +16,10 @@
package android.location;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.location.util.identity.CallerIdentity;
-
-import com.android.internal.annotations.Immutable;
-
-import java.util.Set;
+import android.os.PackageTagsList;
/**
* Location manager local system service interface.
@@ -43,18 +39,14 @@
}
/**
- * Interface for getting callbacks when a location provider's location tags change.
- *
- * @see LocationTagInfo
+ * Interface for getting callbacks when an app id's location provider package tags change.
*/
- public interface OnProviderLocationTagsChangeListener {
+ public interface LocationPackageTagsListener {
/**
- * Called when the location tags for a provider change.
- *
- * @param providerLocationTagInfo The tag info for a provider.
+ * Called when the package tags for a location provider change for a uid.
*/
- void onLocationTagsChanged(@NonNull LocationTagInfo providerLocationTagInfo);
+ void onLocationPackageTagsChanged(int uid, @NonNull PackageTagsList packageTagsList);
}
/**
@@ -109,58 +101,9 @@
public abstract @Nullable LocationTime getGnssTimeMillis();
/**
- * Sets a listener for changes in the location providers' tags. Passing
+ * Sets a listener for changes in an app id's location provider package tags. Passing
* {@code null} clears the current listener.
- *
- * @param listener The listener.
*/
- public abstract void setOnProviderLocationTagsChangeListener(
- @Nullable OnProviderLocationTagsChangeListener listener);
-
- /**
- * This class represents the location permission tags used by the location provider
- * packages in a given UID. These tags are strictly used for accessing state guarded
- * by the location permission(s) by a location provider which are required for the
- * provider to fulfill its function as being a location provider.
- */
- @Immutable
- public static class LocationTagInfo {
- private final int mUid;
-
- @NonNull
- private final String mPackageName;
-
- @Nullable
- private final Set<String> mLocationTags;
-
- public LocationTagInfo(int uid, @NonNull String packageName,
- @Nullable Set<String> locationTags) {
- mUid = uid;
- mPackageName = packageName;
- mLocationTags = locationTags;
- }
-
- /**
- * @return The UID for which tags are related.
- */
- public int getUid() {
- return mUid;
- }
-
- /**
- * @return The package for which tags are related.
- */
- @NonNull
- public String getPackageName() {
- return mPackageName;
- }
-
- /**
- * @return The tags for the package used for location related accesses.
- */
- @Nullable
- public Set<String> getTags() {
- return mLocationTags;
- }
- }
+ public abstract void setLocationPackageTagsListener(
+ @Nullable LocationPackageTagsListener listener);
}
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index a3842a1..b48e596 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -194,6 +194,7 @@
private float mMinUpdateDistanceMeters;
private final long mMaxUpdateDelayMillis;
private boolean mHideFromAppOps;
+ private final boolean mAdasGnssBypass;
private boolean mLocationSettingsIgnored;
private boolean mLowPower;
private @Nullable WorkSource mWorkSource;
@@ -236,7 +237,7 @@
if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
quality = POWER_NONE;
} else if (LocationManager.GPS_PROVIDER.equals(provider)) {
- quality = ACCURACY_FINE;
+ quality = QUALITY_HIGH_ACCURACY;
} else {
quality = POWER_LOW;
}
@@ -289,6 +290,7 @@
float minUpdateDistanceMeters,
long maxUpdateDelayMillis,
boolean hiddenFromAppOps,
+ boolean adasGnssBypass,
boolean locationSettingsIgnored,
boolean lowPower,
WorkSource workSource) {
@@ -302,8 +304,9 @@
mMinUpdateDistanceMeters = minUpdateDistanceMeters;
mMaxUpdateDelayMillis = maxUpdateDelayMillis;
mHideFromAppOps = hiddenFromAppOps;
- mLowPower = lowPower;
+ mAdasGnssBypass = adasGnssBypass;
mLocationSettingsIgnored = locationSettingsIgnored;
+ mLowPower = lowPower;
mWorkSource = Objects.requireNonNull(workSource);
}
@@ -339,15 +342,15 @@
switch (quality) {
case POWER_HIGH:
// fall through
- case ACCURACY_FINE:
+ case QUALITY_HIGH_ACCURACY:
mQuality = QUALITY_HIGH_ACCURACY;
break;
- case ACCURACY_BLOCK:
+ case QUALITY_BALANCED_POWER_ACCURACY:
mQuality = QUALITY_BALANCED_POWER_ACCURACY;
break;
case POWER_LOW:
// fall through
- case ACCURACY_CITY:
+ case QUALITY_LOW_POWER:
mQuality = QUALITY_LOW_POWER;
break;
case POWER_NONE:
@@ -648,6 +651,21 @@
}
/**
+ * Returns true if this request may access GNSS even if location settings would normally deny
+ * this, in order to enable automotive safety features. This field is only respected on
+ * automotive devices, and only if the client is recognized as a legitimate ADAS (Advanced
+ * Driving Assistance Systems) application.
+ *
+ * @return true if all limiting factors will be ignored to satisfy GNSS request
+ *
+ * @hide
+ */
+ // TODO: @SystemApi
+ public boolean isAdasGnssBypass() {
+ return mAdasGnssBypass;
+ }
+
+ /**
* @hide
* @deprecated LocationRequests should be treated as immutable.
*/
@@ -673,6 +691,15 @@
}
/**
+ * Returns true if any bypass flag is set on this request. For internal use only.
+ *
+ * @hide
+ */
+ public boolean isBypass() {
+ return mAdasGnssBypass || mLocationSettingsIgnored;
+ }
+
+ /**
* @hide
* @deprecated LocationRequests should be treated as immutable.
*/
@@ -749,6 +776,7 @@
/* minUpdateDistanceMeters= */ in.readFloat(),
/* maxUpdateDelayMillis= */ in.readLong(),
/* hiddenFromAppOps= */ in.readBoolean(),
+ /* adasGnssBypass= */ in.readBoolean(),
/* locationSettingsIgnored= */ in.readBoolean(),
/* lowPower= */ in.readBoolean(),
/* workSource= */ in.readTypedObject(WorkSource.CREATOR));
@@ -777,6 +805,7 @@
parcel.writeFloat(mMinUpdateDistanceMeters);
parcel.writeLong(mMaxUpdateDelayMillis);
parcel.writeBoolean(mHideFromAppOps);
+ parcel.writeBoolean(mAdasGnssBypass);
parcel.writeBoolean(mLocationSettingsIgnored);
parcel.writeBoolean(mLowPower);
parcel.writeTypedObject(mWorkSource, 0);
@@ -801,6 +830,7 @@
&& Float.compare(that.mMinUpdateDistanceMeters, mMinUpdateDistanceMeters) == 0
&& mMaxUpdateDelayMillis == that.mMaxUpdateDelayMillis
&& mHideFromAppOps == that.mHideFromAppOps
+ && mAdasGnssBypass == that.mAdasGnssBypass
&& mLocationSettingsIgnored == that.mLocationSettingsIgnored
&& mLowPower == that.mLowPower
&& Objects.equals(mProvider, that.mProvider)
@@ -866,8 +896,11 @@
if (mHideFromAppOps) {
s.append(", hiddenFromAppOps");
}
+ if (mAdasGnssBypass) {
+ s.append(", adasGnssBypass");
+ }
if (mLocationSettingsIgnored) {
- s.append(", locationSettingsIgnored");
+ s.append(", settingsBypass");
}
if (mWorkSource != null && !mWorkSource.isEmpty()) {
s.append(", ").append(mWorkSource);
@@ -889,6 +922,7 @@
private float mMinUpdateDistanceMeters;
private long mMaxUpdateDelayMillis;
private boolean mHiddenFromAppOps;
+ private boolean mAdasGnssBypass;
private boolean mLocationSettingsIgnored;
private boolean mLowPower;
@Nullable private WorkSource mWorkSource;
@@ -908,6 +942,7 @@
mMinUpdateDistanceMeters = 0;
mMaxUpdateDelayMillis = 0;
mHiddenFromAppOps = false;
+ mAdasGnssBypass = false;
mLocationSettingsIgnored = false;
mLowPower = false;
mWorkSource = null;
@@ -925,6 +960,7 @@
mMinUpdateDistanceMeters = locationRequest.mMinUpdateDistanceMeters;
mMaxUpdateDelayMillis = locationRequest.mMaxUpdateDelayMillis;
mHiddenFromAppOps = locationRequest.mHideFromAppOps;
+ mAdasGnssBypass = locationRequest.mAdasGnssBypass;
mLocationSettingsIgnored = locationRequest.mLocationSettingsIgnored;
mLowPower = locationRequest.mLowPower;
mWorkSource = locationRequest.mWorkSource;
@@ -977,10 +1013,10 @@
public @NonNull Builder setQuality(@NonNull Criteria criteria) {
switch (criteria.getAccuracy()) {
case Criteria.ACCURACY_COARSE:
- mQuality = ACCURACY_BLOCK;
+ mQuality = QUALITY_BALANCED_POWER_ACCURACY;
break;
case Criteria.ACCURACY_FINE:
- mQuality = ACCURACY_FINE;
+ mQuality = QUALITY_HIGH_ACCURACY;
break;
default: {
if (criteria.getPowerRequirement() == Criteria.POWER_HIGH) {
@@ -1092,6 +1128,25 @@
}
/**
+ * If set to true, indicates that the client is an ADAS (Advanced Driving Assistance
+ * Systems) client, which requires access to GNSS even if location settings would normally
+ * deny this, in order to enable auto safety features. This field is only respected on
+ * automotive devices, and only if the client is recognized as a legitimate ADAS
+ * application. Defaults to false.
+ *
+ * <p>Permissions enforcement occurs when resulting location request is actually used, not
+ * when this method is invoked.
+ *
+ * @hide
+ */
+ // TODO: @SystemApi
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ public @NonNull Builder setAdasGnssBypass(boolean adasGnssBypass) {
+ mAdasGnssBypass = adasGnssBypass;
+ return this;
+ }
+
+ /**
* If set to true, indicates that location settings, throttling, background location limits,
* and any other possible limiting factors should be ignored in order to satisfy this
* request. This is only intended for use in user initiated emergency situations, and
@@ -1171,6 +1226,7 @@
mMinUpdateDistanceMeters,
mMaxUpdateDelayMillis,
mHiddenFromAppOps,
+ mAdasGnssBypass,
mLocationSettingsIgnored,
mLowPower,
new WorkSource(mWorkSource));
diff --git a/location/java/android/location/provider/ProviderRequest.java b/location/java/android/location/provider/ProviderRequest.java
index b72d365..4f33a52 100644
--- a/location/java/android/location/provider/ProviderRequest.java
+++ b/location/java/android/location/provider/ProviderRequest.java
@@ -44,12 +44,19 @@
public static final long INTERVAL_DISABLED = Long.MAX_VALUE;
public static final @NonNull ProviderRequest EMPTY_REQUEST = new ProviderRequest(
- INTERVAL_DISABLED, QUALITY_BALANCED_POWER_ACCURACY, 0, false, false, new WorkSource());
+ INTERVAL_DISABLED,
+ QUALITY_BALANCED_POWER_ACCURACY,
+ 0,
+ false,
+ false,
+ false,
+ new WorkSource());
private final long mIntervalMillis;
private final @Quality int mQuality;
private final long mMaxUpdateDelayMillis;
private final boolean mLowPower;
+ private final boolean mAdasGnssBypass;
private final boolean mLocationSettingsIgnored;
private final WorkSource mWorkSource;
@@ -72,12 +79,14 @@
@Quality int quality,
long maxUpdateDelayMillis,
boolean lowPower,
+ boolean adasGnssBypass,
boolean locationSettingsIgnored,
@NonNull WorkSource workSource) {
mIntervalMillis = intervalMillis;
mQuality = quality;
mMaxUpdateDelayMillis = maxUpdateDelayMillis;
mLowPower = lowPower;
+ mAdasGnssBypass = adasGnssBypass;
mLocationSettingsIgnored = locationSettingsIgnored;
mWorkSource = Objects.requireNonNull(workSource);
}
@@ -126,6 +135,18 @@
}
/**
+ * Returns true if this request may access GNSS even if location settings would normally deny
+ * this, in order to enable automotive safety features. This field is only respected on
+ * automotive devices, and only if the client is recognized as a legitimate ADAS (Advanced
+ * Driving Assistance Systems) application.
+ *
+ * @hide
+ */
+ public boolean isAdasGnssBypass() {
+ return mAdasGnssBypass;
+ }
+
+ /**
* Whether the provider should ignore all location settings, user consents, power restrictions
* or any other restricting factors and always satisfy this request to the best of their
* ability. This should only be used in case of a user initiated emergency.
@@ -135,6 +156,15 @@
}
/**
+ * Returns true if any bypass flag is set on this request.
+ *
+ * @hide
+ */
+ public boolean isBypass() {
+ return mAdasGnssBypass || mLocationSettingsIgnored;
+ }
+
+ /**
* The power blame for this provider request.
*/
public @NonNull WorkSource getWorkSource() {
@@ -153,6 +183,7 @@
/* quality= */ in.readInt(),
/* maxUpdateDelayMillis= */ in.readLong(),
/* lowPower= */ in.readBoolean(),
+ /* adasGnssBypass= */ in.readBoolean(),
/* locationSettingsIgnored= */ in.readBoolean(),
/* workSource= */ in.readTypedObject(WorkSource.CREATOR));
}
@@ -176,6 +207,7 @@
parcel.writeInt(mQuality);
parcel.writeLong(mMaxUpdateDelayMillis);
parcel.writeBoolean(mLowPower);
+ parcel.writeBoolean(mAdasGnssBypass);
parcel.writeBoolean(mLocationSettingsIgnored);
parcel.writeTypedObject(mWorkSource, flags);
}
@@ -198,6 +230,7 @@
&& mQuality == that.mQuality
&& mMaxUpdateDelayMillis == that.mMaxUpdateDelayMillis
&& mLowPower == that.mLowPower
+ && mAdasGnssBypass == that.mAdasGnssBypass
&& mLocationSettingsIgnored == that.mLocationSettingsIgnored
&& mWorkSource.equals(that.mWorkSource);
}
@@ -229,8 +262,11 @@
if (mLowPower) {
s.append(", lowPower");
}
+ if (mAdasGnssBypass) {
+ s.append(", adasGnssBypass");
+ }
if (mLocationSettingsIgnored) {
- s.append(", locationSettingsIgnored");
+ s.append(", settingsBypass");
}
if (!mWorkSource.isEmpty()) {
s.append(", ").append(mWorkSource);
@@ -246,10 +282,12 @@
* A Builder for {@link ProviderRequest}s.
*/
public static final class Builder {
+
private long mIntervalMillis = INTERVAL_DISABLED;
private int mQuality = QUALITY_BALANCED_POWER_ACCURACY;
private long mMaxUpdateDelayMillis = 0;
private boolean mLowPower;
+ private boolean mAdasGnssBypass;
private boolean mLocationSettingsIgnored;
private WorkSource mWorkSource = new WorkSource();
@@ -299,6 +337,16 @@
}
/**
+ * Sets whether this ADAS request should bypass GNSS settings. False by default.
+ *
+ * @hide
+ */
+ public @NonNull Builder setAdasGnssBypass(boolean adasGnssBypass) {
+ this.mAdasGnssBypass = adasGnssBypass;
+ return this;
+ }
+
+ /**
* Sets whether location settings should be ignored. False by default.
*/
public @NonNull Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) {
@@ -326,6 +374,7 @@
mQuality,
mMaxUpdateDelayMillis,
mLowPower,
+ mAdasGnssBypass,
mLocationSettingsIgnored,
mWorkSource);
}
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 860d88a..d8f48c2 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -101,6 +101,8 @@
private int mBatteryLevel;
private int mBatteryScale;
private int mDeviceType;
+ private String mHostType;
+ private boolean mSkipThumbForHost = false;
private MtpServer mServer;
private MtpStorageManager mManager;
@@ -192,6 +194,7 @@
MtpConstants.DEVICE_PROPERTY_IMAGE_SIZE,
MtpConstants.DEVICE_PROPERTY_BATTERY_LEVEL,
MtpConstants.DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE,
+ MtpConstants.DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO,
};
@VisibleForNative
@@ -408,6 +411,8 @@
}
context.deleteDatabase(devicePropertiesName);
}
+ mHostType = "";
+ mSkipThumbForHost = false;
}
@VisibleForNative
@@ -672,12 +677,24 @@
@VisibleForNative
private int getDeviceProperty(int property, long[] outIntValue, char[] outStringValue) {
+ int length;
+ String value;
+
switch (property) {
case MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
case MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
// writable string properties kept in shared preferences
- String value = mDeviceProperties.getString(Integer.toString(property), "");
- int length = value.length();
+ value = mDeviceProperties.getString(Integer.toString(property), "");
+ length = value.length();
+ if (length > 255) {
+ length = 255;
+ }
+ value.getChars(0, length, outStringValue, 0);
+ outStringValue[length] = 0;
+ return MtpConstants.RESPONSE_OK;
+ case MtpConstants.DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO:
+ value = mHostType;
+ length = value.length();
if (length > 255) {
length = 255;
}
@@ -717,6 +734,14 @@
e.putString(Integer.toString(property), stringValue);
return (e.commit() ? MtpConstants.RESPONSE_OK
: MtpConstants.RESPONSE_GENERAL_ERROR);
+ case MtpConstants.DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO:
+ mHostType = stringValue;
+ if (stringValue.startsWith("Android/")) {
+ Log.d(TAG, "setDeviceProperty." + Integer.toHexString(property)
+ + "=" + stringValue);
+ mSkipThumbForHost = true;
+ }
+ return MtpConstants.RESPONSE_OK;
}
return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
@@ -838,6 +863,10 @@
outLongs[0] = thumbOffsetAndSize != null ? thumbOffsetAndSize[1] : 0;
outLongs[1] = exif.getAttributeInt(ExifInterface.TAG_PIXEL_X_DIMENSION, 0);
outLongs[2] = exif.getAttributeInt(ExifInterface.TAG_PIXEL_Y_DIMENSION, 0);
+ if (mSkipThumbForHost) {
+ Log.d(TAG, "getThumbnailInfo: Skip runtime thumbnail.");
+ return true;
+ }
if (exif.getThumbnailRange() != null) {
if ((outLongs[0] == 0) || (outLongs[1] == 0) || (outLongs[2] == 0)) {
Log.d(TAG, "getThumbnailInfo: check thumb info:"
@@ -880,6 +909,10 @@
try {
ExifInterface exif = new ExifInterface(path);
+ if (mSkipThumbForHost) {
+ Log.d(TAG, "getThumbnailData: Skip runtime thumbnail.");
+ return exif.getThumbnail();
+ }
if (exif.getThumbnailRange() != null)
return exif.getThumbnail();
} catch (IOException e) {
diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java
index e8b04ed..ec92591 100644
--- a/media/java/android/mtp/MtpDevice.java
+++ b/media/java/android/mtp/MtpDevice.java
@@ -170,6 +170,18 @@
}
/**
+ * Set device property SESSION_INITIATOR_VERSION_INFO
+ *
+ * @param propertyStr string value for device property SESSION_INITIATOR_VERSION_INFO
+ * @return -1 for error, 0 for success
+ *
+ * {@hide}
+ */
+ public int setDevicePropertyInitVersion(@NonNull String propertyStr) {
+ return native_set_device_property_init_version(propertyStr);
+ }
+
+ /**
* Returns the list of IDs for all storage units on this device
* Information about each storage unit can be accessed via {@link #getStorageInfo}.
*
@@ -421,6 +433,7 @@
private native boolean native_open(String deviceName, int fd);
private native void native_close();
private native MtpDeviceInfo native_get_device_info();
+ private native int native_set_device_property_init_version(String propertyStr);
private native int[] native_get_storage_ids();
private native MtpStorageInfo native_get_storage_info(int storageId);
private native int[] native_get_object_handles(int storageId, int format, int objectHandle);
diff --git a/media/java/android/mtp/MtpDeviceInfo.java b/media/java/android/mtp/MtpDeviceInfo.java
index 0304ee3..8851451 100644
--- a/media/java/android/mtp/MtpDeviceInfo.java
+++ b/media/java/android/mtp/MtpDeviceInfo.java
@@ -31,6 +31,7 @@
private String mSerialNumber;
private int[] mOperationsSupported;
private int[] mEventsSupported;
+ private int[] mDevicePropertySupported;
// only instantiated via JNI
private MtpDeviceInfo() {
@@ -144,6 +145,21 @@
}
/**
+ * Returns Device property code supported by the device.
+ *
+ * @return supported Device property code. Can be null if device does not provide the property.
+ *
+ * @see MtpConstants#DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER
+ * @see MtpConstants#DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME
+ * @see MtpConstants#DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO
+ *
+ * {@hide}
+ */
+ public final @NonNull int[] getDevicePropertySupported() {
+ return mDevicePropertySupported;
+ }
+
+ /**
* Returns if the given operation is supported by the device or not.
* @param code Operation code.
* @return If the given operation is supported by the device or not.
@@ -162,6 +178,17 @@
}
/**
+ * Returns if the given Device property is supported by the device or not.
+ * @param code Device property code.
+ * @return If the given Device property is supported by the device or not.
+ *
+ * {@hide}
+ */
+ public boolean isDevicePropertySupported(int code) {
+ return isSupported(mDevicePropertySupported, code);
+ }
+
+ /**
* Returns if the code set contains code.
* @hide
*/
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index ffed474..a77bc9f 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -1131,6 +1131,7 @@
{ MTP_DEVICE_PROPERTY_IMAGE_SIZE, MTP_TYPE_STR },
{ MTP_DEVICE_PROPERTY_BATTERY_LEVEL, MTP_TYPE_UINT8 },
{ MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE, MTP_TYPE_UINT32 },
+ { MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO, MTP_TYPE_STR },
};
bool MtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) {
@@ -1289,6 +1290,7 @@
switch (property) {
case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
+ case MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO:
writable = true;
// fall through
FALLTHROUGH_INTENDED;
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index 3d2b00fe..ac89fecd 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -72,6 +72,7 @@
static jfieldID field_deviceInfo_serialNumber;
static jfieldID field_deviceInfo_operationsSupported;
static jfieldID field_deviceInfo_eventsSupported;
+static jfieldID field_deviceInfo_devicePropertySupported;
// MtpStorageInfo fields
static jfieldID field_storageInfo_storageId;
@@ -129,6 +130,8 @@
GetFieldIDOrDie(env, clazz_deviceInfo, "mOperationsSupported", "[I");
field_deviceInfo_eventsSupported =
GetFieldIDOrDie(env, clazz_deviceInfo, "mEventsSupported", "[I");
+ field_deviceInfo_devicePropertySupported =
+ GetFieldIDOrDie(env, clazz_deviceInfo, "mDevicePropertySupported", "[I");
clazz_storageInfo =
(jclass)env->NewGlobalRef(FindClassOrDie(env, "android/mtp/MtpStorageInfo"));
@@ -377,9 +380,65 @@
}
}
+ assert(deviceInfo->mDeviceProperties);
+ {
+ const size_t size = deviceInfo->mDeviceProperties->size();
+ ScopedLocalRef<jintArray> events(env, static_cast<jintArray>(env->NewIntArray(size)));
+ {
+ ScopedIntArrayRW elements(env, events.get());
+ if (elements.get() == NULL) {
+ ALOGE("Could not create devicePropertySupported element.");
+ return NULL;
+ }
+ for (size_t i = 0; i < size; ++i) {
+ elements[i] = static_cast<int>(deviceInfo->mDeviceProperties->at(i));
+ }
+ env->SetObjectField(info, field_deviceInfo_devicePropertySupported, events.get());
+ }
+ }
+
return info;
}
+static jint
+android_mtp_MtpDevice_set_device_property_init_version(JNIEnv *env, jobject thiz,
+ jstring property_str) {
+ MtpDevice* const device = get_device_from_object(env, thiz);
+
+ if (!device) {
+ ALOGD("%s device is null\n", __func__);
+ env->ThrowNew(clazz_io_exception, "Failed to obtain MtpDevice.");
+ return -1;
+ }
+
+ const char *propertyStr = env->GetStringUTFChars(property_str, NULL);
+ if (propertyStr == NULL) {
+ return -1;
+ }
+
+ MtpProperty* property = new MtpProperty(MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO,
+ MTP_TYPE_STR, true);
+ if (!property) {
+ env->ThrowNew(clazz_io_exception, "Failed to obtain property.");
+ return -1;
+ }
+
+ if (property->getDataType() != MTP_TYPE_STR) {
+ env->ThrowNew(clazz_io_exception, "Unexpected property data type.");
+ return -1;
+ }
+
+ property->setCurrentValue(propertyStr);
+ if (!device->setDevicePropValueStr(property)) {
+ env->ThrowNew(clazz_io_exception, "Failed to obtain property value.");
+ return -1;
+ }
+
+ env->ReleaseStringUTFChars(property_str, propertyStr);
+
+ return 0;
+}
+
static jintArray
android_mtp_MtpDevice_get_storage_ids(JNIEnv *env, jobject thiz)
{
@@ -847,6 +906,8 @@
{"native_close", "()V", (void *)android_mtp_MtpDevice_close},
{"native_get_device_info", "()Landroid/mtp/MtpDeviceInfo;",
(void *)android_mtp_MtpDevice_get_device_info},
+ {"native_set_device_property_init_version", "(Ljava/lang/String;)I",
+ (void *)android_mtp_MtpDevice_set_device_property_init_version},
{"native_get_storage_ids", "()[I", (void *)android_mtp_MtpDevice_get_storage_ids},
{"native_get_storage_info", "(I)Landroid/mtp/MtpStorageInfo;",
(void *)android_mtp_MtpDevice_get_storage_info},
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index 8de5074..6dc05ad 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -158,7 +158,8 @@
ICamera cameraUser = mUtils.getCameraService()
.connect(dummyCallbacks, cameraId, clientPackageName,
ICameraService.USE_CALLING_UID,
- ICameraService.USE_CALLING_PID);
+ ICameraService.USE_CALLING_PID,
+ getContext().getApplicationInfo().targetSdkVersion);
assertNotNull(String.format("Camera %s was null", cameraId), cameraUser);
Log.v(TAG, String.format("Camera %s connected", cameraId));
@@ -262,7 +263,8 @@
mUtils.getCameraService().connectDevice(
dummyCallbacks, String.valueOf(cameraId),
clientPackageName, clientAttributionTag,
- ICameraService.USE_CALLING_UID, 0 /*oomScoreOffset*/);
+ ICameraService.USE_CALLING_UID, 0 /*oomScoreOffset*/,
+ getContext().getApplicationInfo().targetSdkVersion);
assertNotNull(String.format("Camera %s was null", cameraId), cameraUser);
Log.v(TAG, String.format("Camera %s connected", cameraId));
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 408f2f8..0890346 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -244,7 +244,7 @@
mCameraUser = mUtils.getCameraService().connectDevice(mMockCb, mCameraId,
clientPackageName, clientAttributionTag, ICameraService.USE_CALLING_UID,
- /*oomScoreOffset*/0);
+ /*oomScoreOffset*/0, getContext().getApplicationInfo().targetSdkVersion);
assertNotNull(String.format("Camera %s was null", mCameraId), mCameraUser);
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
@@ -416,7 +416,8 @@
@SmallTest
public void testCameraCharacteristics() throws RemoteException {
- CameraMetadataNative info = mUtils.getCameraService().getCameraCharacteristics(mCameraId);
+ CameraMetadataNative info = mUtils.getCameraService().getCameraCharacteristics(mCameraId,
+ getContext().getApplicationInfo().targetSdkVersion);
assertFalse(info.isEmpty());
assertNotNull(info.get(CameraCharacteristics.SCALER_AVAILABLE_FORMATS));
diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java
index 478297f..843793a 100644
--- a/obex/javax/obex/ObexHelper.java
+++ b/obex/javax/obex/ObexHelper.java
@@ -34,6 +34,8 @@
package javax.obex;
+import android.util.Log;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
@@ -43,7 +45,6 @@
import java.util.Date;
import java.util.TimeZone;
-import android.util.Log;
/**
* This class defines a set of helper methods for the implementation of Obex.
@@ -1083,11 +1084,12 @@
}
private static int validateMaxPacketSize(int size) {
- if(VDBG && (size > MAX_PACKET_SIZE_INT)) Log.w(TAG,
- "The packet size supported for the connection (" + size + ") is larger"
- + " than the configured OBEX packet size: " + MAX_PACKET_SIZE_INT);
- if(size != -1) {
- if(size < LOWER_LIMIT_MAX_PACKET_SIZE) {
+ if (VDBG && (size > MAX_PACKET_SIZE_INT)) {
+ Log.w(TAG, "The packet size supported for the connection (" + size + ") is larger"
+ + " than the configured OBEX packet size: " + MAX_PACKET_SIZE_INT);
+ }
+ if (size != -1 && size < MAX_PACKET_SIZE_INT) {
+ if (size < LOWER_LIMIT_MAX_PACKET_SIZE) {
throw new IllegalArgumentException(size + " is less that the lower limit: "
+ LOWER_LIMIT_MAX_PACKET_SIZE);
}
diff --git a/obex/javax/obex/ObexTransport.java b/obex/javax/obex/ObexTransport.java
index a5a75f5..4cef0b3 100644
--- a/obex/javax/obex/ObexTransport.java
+++ b/obex/javax/obex/ObexTransport.java
@@ -81,6 +81,8 @@
* size. Therefore this value shall not change.
* For RFCOMM or other transport types where the OBEX packets size
* is unrelated to the transport packet size, return -1;
+ * Exception can be made (like PBAP transport) with a smaller value
+ * to avoid bad effect on other profiles using the RFCOMM;
* @return the maximum allowed OBEX packet that can be send over
* the transport. Or -1 in case of don't care.
*/
diff --git a/packages/Android.bp b/packages/Android.bp
index 0030015..810dc56 100644
--- a/packages/Android.bp
+++ b/packages/Android.bp
@@ -24,8 +24,8 @@
java_defaults {
name: "platform_app_defaults",
- plugins: ["error_prone_android_framework"],
errorprone: {
+ extra_check_modules: ["error_prone_android_framework"],
javacflags: [
// We're less worried about performance in app code
"-Xep:AndroidFrameworkEfficientCollections:OFF",
diff --git a/packages/CompanionDeviceManager/OWNERS b/packages/CompanionDeviceManager/OWNERS
index da723b3..734d8b6 100644
--- a/packages/CompanionDeviceManager/OWNERS
+++ b/packages/CompanionDeviceManager/OWNERS
@@ -1 +1 @@
-eugenesusla@google.com
\ No newline at end of file
+include /core/java/android/companion/OWNERS
\ No newline at end of file
diff --git a/packages/CtsShim/apk/arm/CtsShim.apk b/packages/CtsShim/apk/arm/CtsShim.apk
index bb6dfa3..da4ae56 100644
--- a/packages/CtsShim/apk/arm/CtsShim.apk
+++ b/packages/CtsShim/apk/arm/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/arm/CtsShimPriv.apk b/packages/CtsShim/apk/arm/CtsShimPriv.apk
index 2835d57..214d5c2 100644
--- a/packages/CtsShim/apk/arm/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/arm/CtsShimPriv.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShim.apk b/packages/CtsShim/apk/x86/CtsShim.apk
index bb6dfa3..da4ae56 100644
--- a/packages/CtsShim/apk/x86/CtsShim.apk
+++ b/packages/CtsShim/apk/x86/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShimPriv.apk b/packages/CtsShim/apk/x86/CtsShimPriv.apk
index 2e1a789..b4c625f 100644
--- a/packages/CtsShim/apk/x86/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/x86/CtsShimPriv.apk
Binary files differ
diff --git a/packages/PackageInstaller/res/values-de/strings.xml b/packages/PackageInstaller/res/values-de/strings.xml
index dab3fcb0..a782fd2 100644
--- a/packages/PackageInstaller/res/values-de/strings.xml
+++ b/packages/PackageInstaller/res/values-de/strings.xml
@@ -46,7 +46,7 @@
<string name="out_of_space_dlg_text" msgid="8727714096031856231">"<xliff:g id="APP_NAME">%1$s</xliff:g> konnte nicht installiert werden. Gib Speicherplatz frei und versuche es noch einmal."</string>
<string name="app_not_found_dlg_title" msgid="5107924008597470285">"App nicht gefunden"</string>
<string name="app_not_found_dlg_text" msgid="5219983779377811611">"Die App wurde nicht in der Liste der installierten Apps gefunden."</string>
- <string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"Nicht zulässig"</string>
+ <string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"Nicht zugelassen"</string>
<string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"Der aktuelle Nutzer ist nicht dazu berechtigt, diese Deinstallation auszuführen."</string>
<string name="generic_error_dlg_title" msgid="5863195085927067752">"Fehler"</string>
<string name="generic_error_dlg_text" msgid="5287861443265795232">"App konnte nicht deinstalliert werden."</string>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
index 42c1997..bfc00bb 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
@@ -52,6 +52,7 @@
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.util.Arrays;
+import java.util.NoSuchElementException;
public final class RemotePrintDocument {
private static final String LOG_TAG = "RemotePrintDocument";
@@ -441,7 +442,12 @@
// Keep going - best effort...
}
- mPrintDocumentAdapter.asBinder().unlinkToDeath(mDeathRecipient, 0);
+ try {
+ mPrintDocumentAdapter.asBinder().unlinkToDeath(mDeathRecipient, 0);
+ } catch (NoSuchElementException e) {
+ Log.w(LOG_TAG, "Error unlinking print document adapter death recipient.");
+ // Keep going - best effort...
+ }
}
private void scheduleCommand(AsyncCommand command) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 59f272f..d25d5dc 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -61,7 +61,6 @@
import android.print.PrinterInfo;
import android.printservice.PrintService;
import android.printservice.PrintServiceInfo;
-import android.provider.DocumentsContract;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
diff --git a/packages/SettingsLib/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp
index 51c9c39..b6e1677 100644
--- a/packages/SettingsLib/ActionButtonsPreference/Android.bp
+++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp
@@ -14,7 +14,8 @@
resource_dirs: ["res"],
static_libs: [
- "androidx.preference_preference",
+ "androidx.preference_preference",
+ "SettingsLibUtils",
],
sdk_version: "system_current",
diff --git a/packages/SettingsLib/ActionButtonsPreference/lint-baseline.xml b/packages/SettingsLib/ActionButtonsPreference/lint-baseline.xml
index 22b25a3..95b7e3b 100644
--- a/packages/SettingsLib/ActionButtonsPreference/lint-baseline.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/lint-baseline.xml
@@ -26,10 +26,54 @@
<issue
id="NewApi"
message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)"
- errorLine1=" android:radius="?android:attr/dialogCornerRadius""
+ errorLine1=" android:topLeftRadius="?android:attr/dialogCornerRadius""
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml"
+ file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml"
+ line="23"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)"
+ errorLine1=" android:bottomLeftRadius="?android:attr/dialogCornerRadius""
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml"
+ line="25"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)"
+ errorLine1=" android:topRightRadius="?android:attr/dialogCornerRadius""
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml"
+ line="24"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)"
+ errorLine1=" android:bottomRightRadius="?android:attr/dialogCornerRadius""
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml"
+ line="26"
+ column="9"/>
+ </issue>
+
+ <issue
+ id="NewApi"
+ message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)"
+ errorLine1=" android:bottomRightRadius="?android:attr/dialogCornerRadius""
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml"
line="23"
column="9"/>
</issue>
diff --git a/packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
similarity index 63%
copy from packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml
copy to packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
index 93f8724..16a85d6 100644
--- a/packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_left_bk.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2019 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.
@@ -16,10 +16,13 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
-
- <corners android:radius="20dp"/>
- <solid android:color="@color/tv_audio_recording_indicator_icon_background"/>
- <stroke android:width="1dp" android:color="@color/tv_audio_recording_indicator_stroke"/>
-
+ <solid android:color="?androidprv:attr/colorSurface" />
+ <corners
+ android:topLeftRadius="?android:attr/dialogCornerRadius"
+ android:topRightRadius="0dp"
+ android:bottomLeftRadius="?android:attr/dialogCornerRadius"
+ android:bottomRightRadius="0dp"
+ />
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml
similarity index 63%
copy from packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml
copy to packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml
index 93f8724..1b9f68f 100644
--- a/packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/half_rounded_right_bk.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2019 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.
@@ -16,10 +16,13 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
-
- <corners android:radius="20dp"/>
- <solid android:color="@color/tv_audio_recording_indicator_icon_background"/>
- <stroke android:width="1dp" android:color="@color/tv_audio_recording_indicator_stroke"/>
-
+ <solid android:color="?androidprv:attr/colorSurface" />
+ <corners
+ android:topLeftRadius="0dp"
+ android:topRightRadius="?android:attr/dialogCornerRadius"
+ android:bottomLeftRadius="0dp"
+ android:bottomRightRadius="?android:attr/dialogCornerRadius"
+ />
</shape>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/left_rounded_ripple.xml
similarity index 72%
copy from packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
copy to packages/SettingsLib/ActionButtonsPreference/res/drawable/left_rounded_ripple.xml
index a2bbd2b..1584b45 100644
--- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/left_rounded_ripple.xml
@@ -12,8 +12,10 @@
~ 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.
-->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item android:drawable="@drawable/half_rounded_left_bk"/>
+</ripple>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/right_rounded_ripple.xml
similarity index 72%
copy from packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
copy to packages/SettingsLib/ActionButtonsPreference/res/drawable/right_rounded_ripple.xml
index a2bbd2b..15f2b5c 100644
--- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/right_rounded_ripple.xml
@@ -12,8 +12,10 @@
~ 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.
-->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item android:drawable="@drawable/half_rounded_right_bk"/>
+</ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml
similarity index 72%
rename from packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml
rename to packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml
index 93f8724..a884ef1 100644
--- a/packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_bk.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2019 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.
@@ -16,10 +16,10 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
-
- <corners android:radius="20dp"/>
- <solid android:color="@color/tv_audio_recording_indicator_icon_background"/>
- <stroke android:width="1dp" android:color="@color/tv_audio_recording_indicator_stroke"/>
-
+ <solid android:color="?androidprv:attr/colorSurface" />
+ <corners
+ android:radius="?android:attr/dialogCornerRadius"
+ />
</shape>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_ripple.xml
similarity index 73%
copy from packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
copy to packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_ripple.xml
index a2bbd2b..90eefbe 100644
--- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/rounded_ripple.xml
@@ -12,8 +12,10 @@
~ 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.
-->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item android:drawable="@drawable/rounded_bk"/>
+</ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml
similarity index 72%
copy from packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml
copy to packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml
index 93f8724..67b5107 100644
--- a/packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_bk.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2019 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.
@@ -16,10 +16,10 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
-
- <corners android:radius="20dp"/>
- <solid android:color="@color/tv_audio_recording_indicator_icon_background"/>
- <stroke android:width="1dp" android:color="@color/tv_audio_recording_indicator_stroke"/>
-
+ <solid android:color="?androidprv:attr/colorSurface" />
+ <corners
+ android:radius="0dp"
+ />
</shape>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_ripple.xml
similarity index 73%
copy from packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
copy to packages/SettingsLib/ActionButtonsPreference/res/drawable/square_ripple.xml
index a2bbd2b..06be00d 100644
--- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/drawable/square_ripple.xml
@@ -12,8 +12,10 @@
~ 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.
-->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item android:drawable="@drawable/square_bk"/>
+</ripple>
\ No newline at end of file
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
index ba612d7..7c3f5a5 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
@@ -21,14 +21,13 @@
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:paddingHorizontal="8dp"
- android:orientation="horizontal"
- android:background="@drawable/settingslib_rounded_background">
+ android:orientation="horizontal">
<Button
android:id="@+id/button1"
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:layout_weight="1"/>
<View
@@ -43,7 +42,7 @@
android:id="@+id/button2"
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:layout_weight="1"/>
<View
@@ -58,7 +57,7 @@
android:id="@+id/button3"
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:layout_weight="1"/>
<View
@@ -73,6 +72,6 @@
android:id="@+id/button4"
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:layout_weight="1"/>
</LinearLayout>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/values/arrays.xml b/packages/SettingsLib/ActionButtonsPreference/res/values/arrays.xml
new file mode 100644
index 0000000..fe88845
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/values/arrays.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Do not translate. -->
+ <integer-array name="background_style1">
+ <item>@drawable/rounded_ripple</item>
+ </integer-array>
+
+ <!-- Do not translate. -->
+ <integer-array name="background_style2">
+ <item>@drawable/left_rounded_ripple</item>
+ <item>@drawable/right_rounded_ripple</item>
+ </integer-array>
+
+ <!-- Do not translate. -->
+ <integer-array name="background_style3">
+ <item>@drawable/left_rounded_ripple</item>
+ <item>@drawable/square_ripple</item>
+ <item>@drawable/right_rounded_ripple</item>
+ </integer-array>
+
+ <!-- Do not translate. -->
+ <integer-array name="background_style4">
+ <item>@drawable/left_rounded_ripple</item>
+ <item>@drawable/square_ripple</item>
+ <item>@drawable/square_ripple</item>
+ <item>@drawable/right_rounded_ripple</item>
+ </integer-array>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
index 9aa511d..4d3ca945 100644
--- a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
+++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -27,10 +28,11 @@
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
-import androidx.core.os.BuildCompat;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.utils.BuildCompatUtils;
+
import java.util.ArrayList;
import java.util.List;
@@ -53,12 +55,21 @@
public class ActionButtonsPreference extends Preference {
private static final String TAG = "ActionButtonPreference";
+ private static final boolean mIsAtLeastS = BuildCompatUtils.isAtLeastS();
+ private static final int SINGLE_BUTTON_STYLE = 1;
+ private static final int TWO_BUTTONS_STYLE = 2;
+ private static final int THREE_BUTTONS_STYLE = 3;
+ private static final int FOUR_BUTTONS_STYLE = 4;
private final ButtonInfo mButton1Info = new ButtonInfo();
private final ButtonInfo mButton2Info = new ButtonInfo();
private final ButtonInfo mButton3Info = new ButtonInfo();
private final ButtonInfo mButton4Info = new ButtonInfo();
private final List<ButtonInfo> mVisibleButtonInfos = new ArrayList<>(4);
+ private final List<Drawable> mBtnBackgroundStyle1 = new ArrayList<>(1);
+ private final List<Drawable> mBtnBackgroundStyle2 = new ArrayList<>(2);
+ private final List<Drawable> mBtnBackgroundStyle3 = new ArrayList<>(3);
+ private final List<Drawable> mBtnBackgroundStyle4 = new ArrayList<>(4);
private View mDivider1;
private View mDivider2;
@@ -88,15 +99,27 @@
private void init() {
setLayoutResource(R.layout.settingslib_action_buttons);
setSelectable(false);
+
+ final Resources res = getContext().getResources();
+ fetchDrawableArray(mBtnBackgroundStyle1, res.obtainTypedArray(R.array.background_style1));
+ fetchDrawableArray(mBtnBackgroundStyle2, res.obtainTypedArray(R.array.background_style2));
+ fetchDrawableArray(mBtnBackgroundStyle3, res.obtainTypedArray(R.array.background_style3));
+ fetchDrawableArray(mBtnBackgroundStyle4, res.obtainTypedArray(R.array.background_style4));
+ }
+
+ private void fetchDrawableArray(List<Drawable> drawableList, TypedArray typedArray) {
+ for (int i = 0; i < typedArray.length(); i++) {
+ drawableList.add(
+ getContext().getDrawable(typedArray.getResourceId(i, 0 /* defValue */)));
+ }
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- final boolean allowedDivider = !BuildCompat.isAtLeastS();
- holder.setDividerAllowedAbove(allowedDivider);
- holder.setDividerAllowedBelow(allowedDivider);
+ holder.setDividerAllowedAbove(!mIsAtLeastS);
+ holder.setDividerAllowedBelow(!mIsAtLeastS);
mButton1Info.mButton = (Button) holder.findViewById(R.id.button1);
mButton2Info.mButton = (Button) holder.findViewById(R.id.button2);
@@ -112,25 +135,95 @@
mButton3Info.setUpButton();
mButton4Info.setUpButton();
- // Add visible button into list only
- if (mButton1Info.isVisible()) {
+ // Clear info list to avoid duplicate setup.
+ if (!mVisibleButtonInfos.isEmpty()) {
+ mVisibleButtonInfos.clear();
+ }
+ updateLayout();
+ }
+
+ @Override
+ protected void notifyChanged() {
+ super.notifyChanged();
+
+ // Update buttons background and layout when notified and visible button list exist.
+ if (!mVisibleButtonInfos.isEmpty()) {
+ mVisibleButtonInfos.clear();
+ updateLayout();
+ }
+ }
+
+ private void updateLayout() {
+ // Add visible button into list only when platform version is newer than S.
+ if (mButton1Info.isVisible() && mIsAtLeastS) {
mVisibleButtonInfos.add(mButton1Info);
}
- if (mButton2Info.isVisible()) {
+ if (mButton2Info.isVisible() && mIsAtLeastS) {
mVisibleButtonInfos.add(mButton2Info);
}
- if (mButton3Info.isVisible()) {
+ if (mButton3Info.isVisible() && mIsAtLeastS) {
mVisibleButtonInfos.add(mButton3Info);
}
- if (mButton4Info.isVisible()) {
+ if (mButton4Info.isVisible() && mIsAtLeastS) {
mVisibleButtonInfos.add(mButton4Info);
}
+ final boolean isRtl = getContext().getResources().getConfiguration()
+ .getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+ switch (mVisibleButtonInfos.size()) {
+ case SINGLE_BUTTON_STYLE :
+ if (isRtl) {
+ setupRtlBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle1);
+ } else {
+ setupBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle1);
+ }
+ break;
+ case TWO_BUTTONS_STYLE :
+ if (isRtl) {
+ setupRtlBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle2);
+ } else {
+ setupBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle2);
+ }
+ break;
+ case THREE_BUTTONS_STYLE :
+ if (isRtl) {
+ setupRtlBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle3);
+ } else {
+ setupBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle3);
+ }
+ break;
+ case FOUR_BUTTONS_STYLE :
+ if (isRtl) {
+ setupRtlBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle4);
+ } else {
+ setupBackgrounds(mVisibleButtonInfos, mBtnBackgroundStyle4);
+ }
+ break;
+ default:
+ Log.e(TAG, "No visible buttons info, skip background settings.");
+ break;
+ }
+
setupDivider1();
setupDivider2();
setupDivider3();
}
+ private void setupBackgrounds(
+ List<ButtonInfo> buttonInfoList, List<Drawable> buttonBackgroundStyles) {
+ for (int i = 0; i < buttonBackgroundStyles.size(); i++) {
+ buttonInfoList.get(i).mButton.setBackground(buttonBackgroundStyles.get(i));
+ }
+ }
+
+ private void setupRtlBackgrounds(
+ List<ButtonInfo> buttonInfoList, List<Drawable> buttonBackgroundStyles) {
+ for (int i = buttonBackgroundStyles.size() - 1; i >= 0; i--) {
+ buttonInfoList.get(buttonBackgroundStyles.size() - 1 - i)
+ .mButton.setBackground(buttonBackgroundStyles.get(i));
+ }
+ }
+
/**
* Set the visibility state of button1.
*/
diff --git a/packages/SettingsLib/BannerMessagePreference/Android.bp b/packages/SettingsLib/BannerMessagePreference/Android.bp
index c6a9562..0f7a451 100644
--- a/packages/SettingsLib/BannerMessagePreference/Android.bp
+++ b/packages/SettingsLib/BannerMessagePreference/Android.bp
@@ -16,6 +16,7 @@
static_libs: [
"androidx.preference_preference",
"SettingsLibSettingsTheme",
+ "SettingsLibUtils",
],
sdk_version: "system_current",
diff --git a/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml b/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml
index 904b78c..6f504ef 100644
--- a/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml
@@ -15,7 +15,7 @@
limitations under the License.
-->
-<LinearLayout
+<com.android.settingslib.widget.BannerMessageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -23,6 +23,7 @@
style="@style/Banner.Preference.SettingsLib">
<RelativeLayout
+ android:id="@+id/top_row"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="8dp"
@@ -49,6 +50,7 @@
android:id="@+id/banner_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:paddingTop="0dp"
android:paddingBottom="4dp"
android:textAppearance="@style/Banner.Title.SettingsLib"/>
@@ -56,6 +58,7 @@
android:id="@+id/banner_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:paddingTop="0dp"
android:paddingBottom="4dp"
android:textAppearance="@style/Banner.Subtitle.SettingsLib"
android:visibility="gone"/>
@@ -87,4 +90,4 @@
android:layout_height="wrap_content"
style="@style/Banner.ButtonText.SettingsLib"/>
</LinearLayout>
-</LinearLayout>
\ No newline at end of file
+</com.android.settingslib.widget.BannerMessageView>
\ No newline at end of file
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-af/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-af/strings.xml
index ae3834d..d7e778f 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-af/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Weier"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-am/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-am/strings.xml
index ae3834d..6701dea 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-am/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"አሰናብት"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ar/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-ar/strings.xml
index ae3834d..0f1b9ac 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ar/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"إغلاق"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-as/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-as/strings.xml
index ae3834d..21dd94c 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-as/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"অগ্ৰাহ্য কৰক"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-az/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-az/strings.xml
index ae3834d..7f91eb4 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-az/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Qapadın"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml
index ae3834d..ca16c3d 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-b+sr+Latn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Odbacite"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-be/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-be/strings.xml
index ae3834d..b0980ea 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-be/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Адхіліць"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-bg/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-bg/strings.xml
index ae3834d..cccbf96 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-bg/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Отхвърляне"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml
index ae3834d..e0dfcf2 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"বাতিল করুন"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-bs/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-bs/strings.xml
index ae3834d..5e46c6c 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-bs/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Odbacivanje"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ca/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-ca/strings.xml
index ae3834d..81bb048 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ca/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignora"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-cs/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-cs/strings.xml
index ae3834d..ac7623e 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-cs/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Zavřít"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-da/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-da/strings.xml
index ae3834d..8c185d9 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-da/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Luk"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml
index ae3834d..006301b 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Schließen"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-el/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-el/strings.xml
index ae3834d..65843b2 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-el/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Παράβλεψη"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rAU/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-en-rAU/strings.xml
index ae3834d..418c1d5 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rAU/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rCA/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-en-rCA/strings.xml
index ae3834d..418c1d5 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rCA/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rGB/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-en-rGB/strings.xml
index ae3834d..418c1d5 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rGB/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rIN/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-en-rIN/strings.xml
index ae3834d..418c1d5 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rIN/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-en-rXC/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..e2dae5e
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-es-rUS/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-es-rUS/strings.xml
index ae3834d..4816be6 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-es-rUS/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Descartar"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-es/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-es/strings.xml
index ae3834d..5e820238 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-es/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Cerrar"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-et/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-et/strings.xml
index ae3834d..a688723 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-et/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Loobu"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-eu/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-eu/strings.xml
index ae3834d..64dd1c5 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-eu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Baztertu"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fa/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-fa/strings.xml
index ae3834d..bd8985f 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-fa/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"رد شدن"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fi/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-fi/strings.xml
index ae3834d..c384157 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-fi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ohita"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fr-rCA/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-fr-rCA/strings.xml
index ae3834d..dd5889c 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-fr-rCA/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Fermer"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-fr/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-fr/strings.xml
index ae3834d..dd5889c 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-fr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Fermer"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-gl/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-gl/strings.xml
index ae3834d..d787626 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-gl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignorar"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-gu/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-gu/strings.xml
index ae3834d..1fe4c5c 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-gu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"છોડી દો"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hi/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-hi/strings.xml
index ae3834d..f66ee7f 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-hi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"खारिज करें"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hr/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-hr/strings.xml
index ae3834d..f7e7cd0 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-hr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Odbaci"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hu/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-hu/strings.xml
index ae3834d..1551c84 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-hu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Bezárás"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-hy/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-hy/strings.xml
index ae3834d..e014cce 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-hy/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Փակել"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-in/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-in/strings.xml
index ae3834d..607e811 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-in/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Tutup"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-is/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-is/strings.xml
index ae3834d..4afc614 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-is/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Hunsa"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-it/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-it/strings.xml
index ae3834d..81bb048 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-it/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignora"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-iw/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-iw/strings.xml
index ae3834d..aa4c669 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-iw/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"סגירה"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ja/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-ja/strings.xml
index ae3834d..b42f6e6 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ja/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"閉じる"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ka/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-ka/strings.xml
index ae3834d..7bde8b6 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ka/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"უარყოფა"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-kk/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-kk/strings.xml
index ae3834d..01235e0 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-kk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Жабу"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-km/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-km/strings.xml
index ae3834d..4e14820 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-km/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ច្រានចោល"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-kn/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-kn/strings.xml
index ae3834d..b9a5420 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-kn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ವಜಾಗೊಳಿಸಿ"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ko/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-ko/strings.xml
index ae3834d..9b51699 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ko/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"닫기"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ky/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-ky/strings.xml
index ae3834d..affb8ec 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ky/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Жабуу"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-lo/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-lo/strings.xml
index ae3834d..7079f7c 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-lo/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ປິດໄວ້"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-lt/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-lt/strings.xml
index ae3834d..4cee14a 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-lt/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Atsisakyti"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-lv/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-lv/strings.xml
index ae3834d..120a762 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-lv/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Nerādīt"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-mk/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-mk/strings.xml
index ae3834d..76a4390 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-mk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Отфрли"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ml/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-ml/strings.xml
index ae3834d..5a4e14c 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ml/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ഡിസ്മിസ് ചെയ്യുക"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-mn/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-mn/strings.xml
index ae3834d..3974470 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-mn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Үл хэрэгсэх"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml
index ae3834d..4bd4485 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"डिसमिस करा"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ms/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-ms/strings.xml
index ae3834d..290323b 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ms/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ketepikan"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-my/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-my/strings.xml
index ae3834d..52ecc49 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-my/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ပယ်ရန်"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-nb/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-nb/strings.xml
index ae3834d..c1e39a4 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-nb/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Lukk"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml
index ae3834d..1510254 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"हटाउनुहोस्"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-nl/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-nl/strings.xml
index ae3834d..920349f 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-nl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Sluiten"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml
index ae3834d..36e7d3b 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ଖାରଜ କରନ୍ତୁ"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pa/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-pa/strings.xml
index ae3834d..250ef2e 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pa/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ਖਾਰਜ ਕਰੋ"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pl/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-pl/strings.xml
index ae3834d..9ad630a 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Zamknij"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rBR/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-pt-rBR/strings.xml
index ae3834d..80b70ae 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rBR/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dispensar"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rPT/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-pt-rPT/strings.xml
index ae3834d..d787626 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pt-rPT/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignorar"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-pt/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-pt/strings.xml
index ae3834d..80b70ae 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-pt/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Dispensar"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml
index ae3834d..18b6a0e 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ro/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Respingeți"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ru/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-ru/strings.xml
index ae3834d..b694657 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ru/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Закрыть"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-si/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-si/strings.xml
index ae3834d..d818cf7 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-si/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ඉවත ලන්න"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sk/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-sk/strings.xml
index ae3834d..4f59f85 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Zavrieť"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sl/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-sl/strings.xml
index ae3834d..1ca68bf 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Opusti"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sq/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-sq/strings.xml
index ae3834d..dbe7927 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sq/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Hiq"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sr/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-sr/strings.xml
index ae3834d..68a2d5b 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Одбаците"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sv/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-sv/strings.xml
index ae3834d..ef2df3c 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sv/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ignorera"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-sw/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-sw/strings.xml
index ae3834d..ebb0c02 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-sw/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Ondoa"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ta/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-ta/strings.xml
index ae3834d..9b175c7 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ta/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"மூடும்"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-te/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-te/strings.xml
index ae3834d..22a6f59 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-te/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"విస్మరించు"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-th/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-th/strings.xml
index ae3834d..6546bfa 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-th/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"ปิด"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-tl/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-tl/strings.xml
index ae3834d..9b944de 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-tl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"I-dismiss"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-tr/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-tr/strings.xml
index ae3834d..96d49e9 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-tr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Kapat"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-uk/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-uk/strings.xml
index ae3834d..f51b0e7 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-uk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Закрити"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ur/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-ur/strings.xml
index ae3834d..ad3fafb 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-ur/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"برخاست کریں"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-uz/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-uz/strings.xml
index ae3834d..1e24745 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-uz/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Yopish"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-vi/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-vi/strings.xml
index ae3834d..a30cdbf 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-vi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Đóng"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rCN/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-zh-rCN/strings.xml
index ae3834d..a8f36e4 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rCN/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"关闭"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rHK/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-zh-rHK/strings.xml
index ae3834d..b9ee658 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rHK/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"關閉"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rTW/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-zh-rTW/strings.xml
index ae3834d..b9ee658 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-zh-rTW/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"關閉"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/BannerMessagePreference/res/values-zu/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/BannerMessagePreference/res/values-zu/strings.xml
index ae3834d..80faa17 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/values-zu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="accessibility_banner_message_dismiss" msgid="5272928723898304168">"Cashisa"</string>
+</resources>
diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
index 9e39355..3a70d65 100644
--- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
+++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
@@ -35,10 +35,11 @@
import androidx.annotation.ColorRes;
import androidx.annotation.RequiresApi;
import androidx.annotation.StringRes;
-import androidx.core.os.BuildCompat;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
+import com.android.settingslib.utils.BuildCompatUtils;
+
/**
* Banner message is a banner displaying important information (permission request, page error etc),
* and provide actions for user to address. It requires a user action to be dismissed.
@@ -83,7 +84,7 @@
}
private static final String TAG = "BannerPreference";
- private static final boolean IS_AT_LEAST_S = BuildCompat.isAtLeastS();
+ private static final boolean IS_AT_LEAST_S = BuildCompatUtils.isAtLeastS();
private final BannerMessagePreference.ButtonInfo mPositiveButtonInfo =
new BannerMessagePreference.ButtonInfo();
diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java
new file mode 100644
index 0000000..5ca6fb6
--- /dev/null
+++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessageView.java
@@ -0,0 +1,111 @@
+/*
+ * 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.settingslib.widget;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.TouchDelegate;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import androidx.annotation.Nullable;
+
+/**
+ * The view providing {@link BannerMessagePreference}.
+ *
+ * <p>Callers should not instantiate this view directly but rather through adding a
+ * {@link BannerMessagePreference} to a {@code PreferenceScreen}.
+ */
+public class BannerMessageView extends LinearLayout {
+ private Rect mTouchTargetForDismissButton;
+
+ public BannerMessageView(Context context) {
+ super(context);
+ }
+
+ public BannerMessageView(Context context,
+ @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public BannerMessageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public BannerMessageView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ setupIncreaseTouchTargetForDismissButton();
+ }
+
+ private void setupIncreaseTouchTargetForDismissButton() {
+ if (mTouchTargetForDismissButton != null) {
+ // Already set up
+ return;
+ }
+
+ // The dismiss button is in the 'top row' RelativeLayout for positioning, but this element
+ // does not have enough space to provide large touch targets. We therefore set the top
+ // target on this view.
+ View topRow = findViewById(R.id.top_row);
+ View dismissButton = findViewById(R.id.banner_dismiss_btn);
+ if (topRow == null || dismissButton == null || dismissButton.getVisibility() != VISIBLE) {
+ return;
+ }
+
+ int minimum =
+ getResources()
+ .getDimensionPixelSize(R.dimen.settingslib_preferred_minimum_touch_target);
+ int width = dismissButton.getWidth();
+ int height = dismissButton.getHeight();
+ int widthIncrease = width < minimum ? minimum - width : 0;
+ int heightIncrease = height < minimum ? minimum - height : 0;
+
+ // Compute the hit rect of dismissButton within the local co-orindate reference of this view
+ // (rather than it's direct parent topRow).
+ Rect hitRectWithinTopRow = new Rect();
+ dismissButton.getHitRect(hitRectWithinTopRow);
+ Rect hitRectOfTopRowWithinThis = new Rect();
+ topRow.getHitRect(hitRectOfTopRowWithinThis);
+ mTouchTargetForDismissButton = new Rect();
+ mTouchTargetForDismissButton.left =
+ hitRectOfTopRowWithinThis.left + hitRectWithinTopRow.left;
+ mTouchTargetForDismissButton.right =
+ hitRectOfTopRowWithinThis.left + hitRectWithinTopRow.right;
+ mTouchTargetForDismissButton.top =
+ hitRectOfTopRowWithinThis.top + hitRectWithinTopRow.top;
+ mTouchTargetForDismissButton.bottom =
+ hitRectOfTopRowWithinThis.top + hitRectWithinTopRow.bottom;
+
+ // Adjust the touch target rect to apply the necessary increase in width and height.
+ mTouchTargetForDismissButton.left -=
+ widthIncrease % 2 == 1 ? (widthIncrease / 2) + 1 : widthIncrease / 2;
+ mTouchTargetForDismissButton.top -=
+ heightIncrease % 2 == 1 ? (heightIncrease / 2) + 1 : heightIncrease / 2;
+ mTouchTargetForDismissButton.right += widthIncrease / 2;
+ mTouchTargetForDismissButton.bottom += heightIncrease / 2;
+
+ setTouchDelegate(new TouchDelegate(mTouchTargetForDismissButton, dismissButton));
+ }
+
+}
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
index 55de377..6acd9ff 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml
@@ -21,8 +21,7 @@
android:id="@+id/content_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fitsSystemWindows="true"
- android:transitionGroup="true">
+ android:fitsSystemWindows="true">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
@@ -34,7 +33,7 @@
android:background="?android:attr/colorPrimary"
android:theme="@style/Theme.CollapsingToolbar.Settings">
- <com.android.settingslib.collapsingtoolbar.AdjustableToolbarLayout
+ <com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="@dimen/toolbar_one_line_height"
@@ -60,7 +59,7 @@
android:transitionName="shared_element_view"
app:layout_collapseMode="pin"/>
- </com.android.settingslib.collapsingtoolbar.AdjustableToolbarLayout>
+ </com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml
index 2a72a1a..63d397c 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml
@@ -17,6 +17,7 @@
<resources>
<style name="CollapsingToolbarTitle.Collapsed" parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textSize">20dp</item>
</style>
<style name="CollapsingToolbarTitle.Expanded" parent="CollapsingToolbarTitle.Collapsed">
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/AdjustableToolbarLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/AdjustableToolbarLayout.java
deleted file mode 100644
index 0e7e595..0000000
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/AdjustableToolbarLayout.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.collapsingtoolbar;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.google.android.material.appbar.CollapsingToolbarLayout;
-
-/**
- * A customized version of CollapsingToolbarLayout that can apply different font size based on the
- * line count of its title.
- */
-public class AdjustableToolbarLayout extends CollapsingToolbarLayout {
-
- private static final int TOOLBAR_MAX_LINE_NUMBER = 2;
-
- public AdjustableToolbarLayout(@NonNull Context context) {
- this(context, null);
-
- }
-
- public AdjustableToolbarLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public AdjustableToolbarLayout(@NonNull Context context, @Nullable AttributeSet attrs,
- int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- initCollapsingToolbar();
- }
-
- @SuppressWarnings("RestrictTo")
- private void initCollapsingToolbar() {
- this.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- v.removeOnLayoutChangeListener(this);
- final int count = getLineCount();
- if (count > TOOLBAR_MAX_LINE_NUMBER) {
- final ViewGroup.LayoutParams lp = getLayoutParams();
- lp.height = getResources()
- .getDimensionPixelSize(R.dimen.toolbar_three_lines_height);
- setScrimVisibleHeightTrigger(
- getResources().getDimensionPixelSize(
- R.dimen.scrim_visible_height_trigger_three_lines));
- setLayoutParams(lp);
- } else if (count == TOOLBAR_MAX_LINE_NUMBER) {
- final ViewGroup.LayoutParams lp = getLayoutParams();
- lp.height = getResources()
- .getDimensionPixelSize(R.dimen.toolbar_two_lines_height);
- setScrimVisibleHeightTrigger(
- getResources().getDimensionPixelSize(
- R.dimen.scrim_visible_height_trigger_two_lines));
- setLayoutParams(lp);
- }
- }
- });
- }
-}
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 e6ca2e0..fa4806d 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -23,8 +23,12 @@
import android.view.ViewGroup;
import android.widget.Toolbar;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+import androidx.fragment.app.FragmentActivity;
+import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.google.android.material.resources.TextAppearanceConfig;
@@ -32,9 +36,18 @@
* A base Activity that has a collapsing toolbar layout is used for the activities intending to
* enable the collapsing toolbar function.
*/
-public class CollapsingToolbarBaseActivity extends SettingsTransitionActivity {
+public class CollapsingToolbarBaseActivity extends FragmentActivity implements
+ AppBarLayout.OnOffsetChangedListener {
+ private static final int TOOLBAR_MAX_LINE_NUMBER = 2;
+ private static final int FULLY_EXPANDED_OFFSET = 0;
+ private static final String KEY_IS_TOOLBAR_COLLAPSED = "is_toolbar_collapsed";
+
+ @Nullable
private CollapsingToolbarLayout mCollapsingToolbarLayout;
+ @Nullable
+ private AppBarLayout mAppBarLayout;
+ private boolean mIsToolbarCollapsed;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -43,6 +56,14 @@
TextAppearanceConfig.setShouldLoadFontSynchronously(true);
super.setContentView(R.layout.collapsing_toolbar_base_layout);
mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar);
+ mAppBarLayout = findViewById(R.id.app_bar);
+ mAppBarLayout.addOnOffsetChangedListener(this);
+ if (savedInstanceState != null) {
+ mIsToolbarCollapsed = savedInstanceState.getBoolean(KEY_IS_TOOLBAR_COLLAPSED);
+ }
+
+ initCollapsingToolbar();
+ disableCollapsingToolbarLayoutScrollingBehavior();
final Toolbar toolbar = findViewById(R.id.action_bar);
setActionBar(toolbar);
@@ -101,10 +122,88 @@
return true;
}
+ @Override
+ public void onOffsetChanged(AppBarLayout appBarLayout, int offset) {
+ if (offset == FULLY_EXPANDED_OFFSET) {
+ mIsToolbarCollapsed = false;
+ } else {
+ mIsToolbarCollapsed = true;
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if (isChangingConfigurations()) {
+ outState.putBoolean(KEY_IS_TOOLBAR_COLLAPSED, mIsToolbarCollapsed);
+ }
+ }
+
/**
* Returns an instance of collapsing toolbar.
*/
+ @Nullable
public CollapsingToolbarLayout getCollapsingToolbarLayout() {
return mCollapsingToolbarLayout;
}
+
+ /**
+ * Return an instance of app bar.
+ */
+ @Nullable
+ public AppBarLayout getAppBarLayout() {
+ return mAppBarLayout;
+ }
+
+ private void disableCollapsingToolbarLayoutScrollingBehavior() {
+ if (mAppBarLayout == null) {
+ return;
+ }
+ final CoordinatorLayout.LayoutParams params =
+ (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
+ final AppBarLayout.Behavior behavior = new AppBarLayout.Behavior();
+ behavior.setDragCallback(
+ new AppBarLayout.Behavior.DragCallback() {
+ @Override
+ public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
+ return false;
+ }
+ });
+ params.setBehavior(behavior);
+ }
+
+ @SuppressWarnings("RestrictTo")
+ private void initCollapsingToolbar() {
+ if (mCollapsingToolbarLayout == null || mAppBarLayout == null) {
+ return;
+ }
+ mCollapsingToolbarLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ v.removeOnLayoutChangeListener(this);
+ if (mIsToolbarCollapsed) {
+ return;
+ }
+ final int count = mCollapsingToolbarLayout.getLineCount();
+ if (count > TOOLBAR_MAX_LINE_NUMBER) {
+ final ViewGroup.LayoutParams lp = mCollapsingToolbarLayout.getLayoutParams();
+ lp.height = getResources()
+ .getDimensionPixelSize(R.dimen.toolbar_three_lines_height);
+ mCollapsingToolbarLayout.setScrimVisibleHeightTrigger(
+ getResources().getDimensionPixelSize(
+ R.dimen.scrim_visible_height_trigger_three_lines));
+ mCollapsingToolbarLayout.setLayoutParams(lp);
+ } else if (count == TOOLBAR_MAX_LINE_NUMBER) {
+ final ViewGroup.LayoutParams lp = mCollapsingToolbarLayout.getLayoutParams();
+ lp.height = getResources()
+ .getDimensionPixelSize(R.dimen.toolbar_two_lines_height);
+ mCollapsingToolbarLayout.setScrimVisibleHeightTrigger(
+ getResources().getDimensionPixelSize(
+ R.dimen.scrim_visible_height_trigger_two_lines));
+ mCollapsingToolbarLayout.setLayoutParams(lp);
+ }
+ }
+ });
+ }
}
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseFragment.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseFragment.java
index c4c74ff..e702668 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseFragment.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseFragment.java
@@ -25,21 +25,33 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.fragment.app.Fragment;
+import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
/**
* A base fragment that has a collapsing toolbar layout for enabling the collapsing toolbar design.
*/
-public abstract class CollapsingToolbarBaseFragment extends Fragment {
+public abstract class CollapsingToolbarBaseFragment extends Fragment implements
+ AppBarLayout.OnOffsetChangedListener {
+
+ private static final int TOOLBAR_MAX_LINE_NUMBER = 2;
+ private static final int FULLY_EXPANDED_OFFSET = 0;
+ private static final String KEY_IS_TOOLBAR_COLLAPSED = "is_toolbar_collapsed";
@Nullable
+ private CoordinatorLayout mCoordinatorLayout;
+ @Nullable
private CollapsingToolbarLayout mCollapsingToolbarLayout;
+ @Nullable
+ private AppBarLayout mAppBarLayout;
@NonNull
private Toolbar mToolbar;
@NonNull
private FrameLayout mContentFrameLayout;
+ private boolean mIsToolbarCollapsed;
@Nullable
@Override
@@ -47,7 +59,17 @@
@Nullable Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.collapsing_toolbar_base_layout, container,
false);
+ mCoordinatorLayout = view.findViewById(R.id.content_parent);
mCollapsingToolbarLayout = view.findViewById(R.id.collapsing_toolbar);
+ mAppBarLayout = view.findViewById(R.id.app_bar);
+ if (mAppBarLayout != null) {
+ mAppBarLayout.addOnOffsetChangedListener(this);
+ }
+ if (savedInstanceState != null) {
+ mIsToolbarCollapsed = savedInstanceState.getBoolean(KEY_IS_TOOLBAR_COLLAPSED);
+ }
+ initCollapsingToolbar();
+ disableCollapsingToolbarLayoutScrollingBehavior();
mToolbar = view.findViewById(R.id.action_bar);
mContentFrameLayout = view.findViewById(R.id.content_frame);
return view;
@@ -60,6 +82,39 @@
requireActivity().setActionBar(mToolbar);
}
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if (getActivity().isChangingConfigurations()) {
+ outState.putBoolean(KEY_IS_TOOLBAR_COLLAPSED, mIsToolbarCollapsed);
+ }
+ }
+
+ @Override
+ public void onOffsetChanged(AppBarLayout appBarLayout, int offset) {
+ if (offset == FULLY_EXPANDED_OFFSET) {
+ mIsToolbarCollapsed = false;
+ } else {
+ mIsToolbarCollapsed = true;
+ }
+ }
+
+ /**
+ * Return an instance of CoordinatorLayout.
+ */
+ @Nullable
+ public CoordinatorLayout getCoordinatorLayout() {
+ return mCoordinatorLayout;
+ }
+
+ /**
+ * Return an instance of app bar.
+ */
+ @Nullable
+ public AppBarLayout getAppBarLayout() {
+ return mAppBarLayout;
+ }
+
/**
* Return the collapsing toolbar layout.
*/
@@ -75,4 +130,56 @@
public FrameLayout getContentFrameLayout() {
return mContentFrameLayout;
}
+
+ private void disableCollapsingToolbarLayoutScrollingBehavior() {
+ if (mAppBarLayout == null) {
+ return;
+ }
+ final CoordinatorLayout.LayoutParams params =
+ (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
+ final AppBarLayout.Behavior behavior = new AppBarLayout.Behavior();
+ behavior.setDragCallback(
+ new AppBarLayout.Behavior.DragCallback() {
+ @Override
+ public boolean canDrag(@NonNull AppBarLayout appBarLayout) {
+ return false;
+ }
+ });
+ params.setBehavior(behavior);
+ }
+
+ @SuppressWarnings("RestrictTo")
+ private void initCollapsingToolbar() {
+ if (mCollapsingToolbarLayout == null || mAppBarLayout == null) {
+ return;
+ }
+ mCollapsingToolbarLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ v.removeOnLayoutChangeListener(this);
+ if (mIsToolbarCollapsed) {
+ return;
+ }
+ final int count = mCollapsingToolbarLayout.getLineCount();
+ if (count > TOOLBAR_MAX_LINE_NUMBER) {
+ final ViewGroup.LayoutParams lp = mCollapsingToolbarLayout.getLayoutParams();
+ lp.height = getResources()
+ .getDimensionPixelSize(R.dimen.toolbar_three_lines_height);
+ mCollapsingToolbarLayout.setScrimVisibleHeightTrigger(
+ getResources().getDimensionPixelSize(
+ R.dimen.scrim_visible_height_trigger_three_lines));
+ mCollapsingToolbarLayout.setLayoutParams(lp);
+ } else if (count == TOOLBAR_MAX_LINE_NUMBER) {
+ final ViewGroup.LayoutParams lp = mCollapsingToolbarLayout.getLayoutParams();
+ lp.height = getResources()
+ .getDimensionPixelSize(R.dimen.toolbar_two_lines_height);
+ mCollapsingToolbarLayout.setScrimVisibleHeightTrigger(
+ getResources().getDimensionPixelSize(
+ R.dimen.scrim_visible_height_trigger_two_lines));
+ mCollapsingToolbarLayout.setLayoutParams(lp);
+ }
+ }
+ });
+ }
}
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java
index 8c2621d..3a7fe3b 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java
@@ -16,22 +16,8 @@
package com.android.settingslib.collapsingtoolbar;
-import static com.android.settingslib.transition.SettingsTransitionHelper.EXTRA_PAGE_TRANSITION_TYPE;
-
-import android.app.ActivityOptions;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.Window;
-import android.widget.Toolbar;
-
-import androidx.annotation.Nullable;
-import androidx.core.os.BuildCompat;
import androidx.fragment.app.FragmentActivity;
-import com.android.settingslib.transition.SettingsTransitionHelper;
-import com.android.settingslib.transition.SettingsTransitionHelper.TransitionType;
-
/**
* A base Activity for Settings-specific page transition. Activities extending it will get
* Settings transition applied.
@@ -39,57 +25,7 @@
public abstract class SettingsTransitionActivity extends FragmentActivity {
private static final String TAG = "SettingsTransitionActivity";
- private Toolbar mToolbar;
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- if (isSettingsTransitionEnabled()) {
- getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
- SettingsTransitionHelper.applyForwardTransition(this);
- SettingsTransitionHelper.applyBackwardTransition(this);
- }
-
- super.onCreate(savedInstanceState);
- }
-
- @Override
- public void setActionBar(@Nullable Toolbar toolbar) {
- super.setActionBar(toolbar);
-
- mToolbar = toolbar;
- }
-
- @Override
- public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
- final int transitionType = intent.getIntExtra(EXTRA_PAGE_TRANSITION_TYPE,
- TransitionType.TRANSITION_SHARED_AXIS);
- if (!isSettingsTransitionEnabled() ||
- transitionType == TransitionType.TRANSITION_NONE) {
- super.startActivityForResult(intent, requestCode, options);
- return;
- }
-
- super.startActivityForResult(intent, requestCode,
- createActivityOptionsBundleForTransition(options));
- }
-
protected boolean isSettingsTransitionEnabled() {
- return BuildCompat.isAtLeastS();
- }
-
- @Nullable
- private Bundle createActivityOptionsBundleForTransition(@Nullable Bundle options) {
- if (mToolbar == null) {
- Log.w(TAG, "setActionBar(Toolbar) is not called. Cannot apply settings transition!");
- return options;
- }
- final Bundle transitionOptions = ActivityOptions.makeSceneTransitionAnimation(this,
- mToolbar, "shared_element_view").toBundle();
- if (options == null) {
- return transitionOptions;
- }
- final Bundle mergedOptions = new Bundle(options);
- mergedOptions.putAll(transitionOptions);
- return mergedOptions;
+ return false;
}
}
diff --git a/packages/SettingsLib/FooterPreference/res/drawable/ic_info_outline_24.xml b/packages/SettingsLib/FooterPreference/res/drawable/settingslib_ic_info_outline_24.xml
similarity index 100%
rename from packages/SettingsLib/FooterPreference/res/drawable/ic_info_outline_24.xml
rename to packages/SettingsLib/FooterPreference/res/drawable/settingslib_ic_info_outline_24.xml
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-af/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-af/strings.xml
index ae3834d..c17f3ed 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-af/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Kom meer te wete"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-am/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-am/strings.xml
index ae3834d..02e6131 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-am/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"የበለጠ ለመረዳት"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-ar/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-ar/strings.xml
index ae3834d..1f279a6 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-ar/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"مزيد من المعلومات"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-as/strings.xml
similarity index 61%
rename from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
rename to packages/SettingsLib/FooterPreference/res/values-as/strings.xml
index ae3834d..a34b474 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-as/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"অধিক জানক"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-az/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-az/strings.xml
index ae3834d..b49036e 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-az/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Ətraflı məlumat"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml
index ae3834d..993ec9a 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-b+sr+Latn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saznajte više"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-be/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-be/strings.xml
index ae3834d..f9d6129 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-be/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Даведацца больш"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-bg/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-bg/strings.xml
index ae3834d..605663d 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-bg/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Научете повече"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-bn/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-bn/strings.xml
index ae3834d..c58142d 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-bn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"আরও জানুন"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-bs/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-bs/strings.xml
index ae3834d..993ec9a 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-bs/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saznajte više"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-ca/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-ca/strings.xml
index ae3834d..7abf10f 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-ca/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Més informació"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-cs/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-cs/strings.xml
index ae3834d..decbb68e 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-cs/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Další informace"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-da/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-da/strings.xml
index ae3834d..81d1c7c 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-da/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Få flere oplysninger"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-de/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-de/strings.xml
index ae3834d..fe885aa 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-de/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Weitere Informationen"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-el/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-el/strings.xml
index ae3834d..5a30833 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-el/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Μάθετε περισσότερα"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-en-rAU/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-en-rAU/strings.xml
index ae3834d..924d735 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rAU/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-en-rCA/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-en-rCA/strings.xml
index ae3834d..924d735 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rCA/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-en-rGB/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-en-rGB/strings.xml
index ae3834d..924d735 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rGB/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-en-rIN/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-en-rIN/strings.xml
index ae3834d..924d735 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rIN/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/res/values-en-rXC/strings.xml b/packages/SettingsLib/FooterPreference/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..bd12547
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Learn more"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-es-rUS/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-es-rUS/strings.xml
index ae3834d..f31d9ea 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-es-rUS/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Más información"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-es/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-es/strings.xml
index ae3834d..f31d9ea 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-es/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Más información"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-et/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-et/strings.xml
index ae3834d..78b65ed 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-et/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Lisateave"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-eu/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-eu/strings.xml
index ae3834d..cf7fa00 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-eu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Lortu informazio gehiago"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-fa/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-fa/strings.xml
index ae3834d..464c58e 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-fa/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"بیشتر بدانید"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-fi/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-fi/strings.xml
index ae3834d..856b962 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-fi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Lue lisää"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-fr-rCA/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-fr-rCA/strings.xml
index ae3834d..6d856ca 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-fr-rCA/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"En savoir plus"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-fr/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-fr/strings.xml
index ae3834d..6d856ca 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-fr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"En savoir plus"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-gl/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-gl/strings.xml
index ae3834d..cde57d8 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-gl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Máis información"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-gu/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-gu/strings.xml
index ae3834d..54249b8 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-gu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"વધુ જાણો"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-hi/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-hi/strings.xml
index ae3834d..95ae240 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-hi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ज़्यादा जानें"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-hr/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-hr/strings.xml
index ae3834d..993ec9a 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-hr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saznajte više"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-hu/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-hu/strings.xml
index ae3834d..ae3c948 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-hu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"További információ"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-hy/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-hy/strings.xml
index ae3834d..de9137b 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-hy/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Իմանալ ավելին"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-in/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-in/strings.xml
index ae3834d..4b5cb16 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-in/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Pelajari lebih lanjut"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-is/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-is/strings.xml
index ae3834d..111094c 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-is/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Nánar"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-it/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-it/strings.xml
index ae3834d..053c80c 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-it/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Scopri di più"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-iw/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-iw/strings.xml
index ae3834d..55b0187 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-iw/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"מידע נוסף"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-ja/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-ja/strings.xml
index ae3834d..3312cb4 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-ja/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"詳細"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-ka/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-ka/strings.xml
index ae3834d..67bb223 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-ka/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"შეიტყვეთ მეტი"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-kk/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-kk/strings.xml
index ae3834d..db11a76 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-kk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Толығырақ"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-km/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-km/strings.xml
index ae3834d..1977dd3 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-km/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ស្វែងយល់បន្ថែម"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-kn/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-kn/strings.xml
index ae3834d..47fa3d5 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-kn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-ko/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-ko/strings.xml
index ae3834d..d8d2200 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-ko/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"자세히 알아보기"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-ky/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-ky/strings.xml
index ae3834d..74c6a49 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-ky/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Кеңири маалымат"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-lo/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-lo/strings.xml
index ae3834d..2e4124b 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-lo/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ສຶກສາເພີ່ມເຕີມ"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-lt/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-lt/strings.xml
index ae3834d..2981c66 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-lt/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Sužinokite daugiau"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-lv/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-lv/strings.xml
index ae3834d..9766305 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-lv/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Uzzināt vairāk"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-mk/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-mk/strings.xml
index ae3834d..1f734c5 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-mk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Дознајте повеќе"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-ml/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-ml/strings.xml
index ae3834d..1cd466b 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-ml/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"കൂടുതലറിയുക"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-mn/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-mn/strings.xml
index ae3834d..8bac1eb 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-mn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Нэмэлт мэдээлэл авах"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-mr/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-mr/strings.xml
index ae3834d..4538720 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-mr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"अधिक जाणून घ्या"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-ms/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-ms/strings.xml
index ae3834d..cd1b17a 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-ms/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Ketahui lebih lanjut"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-my/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-my/strings.xml
index ae3834d..751a87a 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-my/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ပိုမိုလေ့လာရန်"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-nb/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-nb/strings.xml
index ae3834d..08de009 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-nb/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Finn ut mer"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-ne/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-ne/strings.xml
index ae3834d..ecfec36 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-ne/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"थप जान्नुहोस्"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-nl/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-nl/strings.xml
index ae3834d..1564081 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-nl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Meer informatie"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-or/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-or/strings.xml
index ae3834d..e7924d6 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-or/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ଅଧିକ ଜାଣନ୍ତୁ"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-pa/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-pa/strings.xml
index ae3834d..1ce2ef2 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-pa/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ਹੋਰ ਜਾਣੋ"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-pl/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-pl/strings.xml
index ae3834d..5709f3e 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-pl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Więcej informacji"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-pt-rBR/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-pt-rBR/strings.xml
index ae3834d..bc410ab 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-pt-rBR/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saiba mais"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-pt-rPT/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-pt-rPT/strings.xml
index ae3834d..bc410ab 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-pt-rPT/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saiba mais"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-pt/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-pt/strings.xml
index ae3834d..bc410ab 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-pt/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Saiba mais"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-ro/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-ro/strings.xml
index ae3834d..2b50117 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-ro/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Aflați mai multe"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-ru/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-ru/strings.xml
index ae3834d..bedde40 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-ru/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Подробнее…"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-si/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-si/strings.xml
index ae3834d..1a60601 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-si/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"තව දැන ගන්න"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-sk/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-sk/strings.xml
index ae3834d..c1008e5 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-sk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Ďalšie informácie"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-sl/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-sl/strings.xml
index ae3834d..79e0a73 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-sl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Več o tem"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-sq/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-sq/strings.xml
index ae3834d..0fea476c 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-sq/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Mëso më shumë"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-sr/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-sr/strings.xml
index ae3834d..9a73269 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-sr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Сазнајте више"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-sv/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-sv/strings.xml
index ae3834d..a78c3cb 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-sv/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Läs mer"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-sw/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-sw/strings.xml
index ae3834d..52b1732 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-sw/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Pata maelezo zaidi"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-ta/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-ta/strings.xml
index ae3834d..75fc7c1 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-ta/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"மேலும் அறிக"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-te/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-te/strings.xml
index ae3834d..6c8d679 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-te/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"మరింత తెలుసుకోండి"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-th/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-th/strings.xml
index ae3834d..025a2f0 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-th/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"ดูข้อมูลเพิ่มเติม"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-tl/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-tl/strings.xml
index ae3834d..4b6f830 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-tl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Matuto pa"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-tr/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-tr/strings.xml
index ae3834d..77d15130 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-tr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Daha fazla bilgi"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-uk/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-uk/strings.xml
index ae3834d..cec933d 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-uk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Докладніше"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-ur/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-ur/strings.xml
index ae3834d..1dceea7 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-ur/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"مزید جانیں"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-uz/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-uz/strings.xml
index ae3834d..5823949 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-uz/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Batafsil"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-vi/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-vi/strings.xml
index ae3834d..d6c4638 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-vi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Tìm hiểu thêm"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-zh-rCN/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-zh-rCN/strings.xml
index ae3834d..446c8ce 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-zh-rCN/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"了解详情"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-zh-rHK/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-zh-rHK/strings.xml
index ae3834d..8ab38c6 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-zh-rHK/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"瞭解詳情"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-zh-rTW/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-zh-rTW/strings.xml
index ae3834d..8ab38c6 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-zh-rTW/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"瞭解詳情"</string>
+</resources>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml b/packages/SettingsLib/FooterPreference/res/values-zu/strings.xml
similarity index 61%
copy from packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
copy to packages/SettingsLib/FooterPreference/res/values-zu/strings.xml
index ae3834d..b53eb85 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-zu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -13,13 +13,9 @@
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">
- <solid android:color="?androidprv:attr/colorSurface" />
- <corners
- android:radius="?android:attr/dialogCornerRadius"
- />
-</shape>
\ No newline at end of file
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Funda kabanzi"</string>
+</resources>
diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
index 005c75c..8216edf 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -149,7 +149,7 @@
private void init() {
setLayoutResource(R.layout.preference_footer);
if (getIcon() == null) {
- setIcon(R.drawable.ic_info_outline_24);
+ setIcon(R.drawable.settingslib_ic_info_outline_24);
}
setOrder(ORDER_FOOTER);
if (TextUtils.isEmpty(getKey())) {
diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp
index 23ee49e..76d1ea7 100644
--- a/packages/SettingsLib/MainSwitchPreference/Android.bp
+++ b/packages/SettingsLib/MainSwitchPreference/Android.bp
@@ -16,8 +16,9 @@
static_libs: [
"androidx.preference_preference",
"SettingsLibSettingsTheme",
+ "SettingsLibUtils",
],
sdk_version: "system_current",
- min_sdk_version: "21",
+ min_sdk_version: "28",
}
diff --git a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
index 2607529..5817f77 100644
--- a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
+++ b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml
@@ -19,7 +19,7 @@
package="com.android.settingslib.widget">
<uses-sdk
- android:minSdkVersion="21"
+ android:minSdkVersion="28"
android:targetSdkVersion="31"/>
</manifest>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_disabled.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_disabled.xml
deleted file mode 100644
index b41762f..0000000
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_disabled.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:top="@dimen/settingslib_switch_thumb_margin"
- android:left="@dimen/settingslib_switch_thumb_margin"
- android:right="@dimen/settingslib_switch_thumb_margin"
- android:bottom="@dimen/settingslib_switch_thumb_margin">
- <shape android:shape="oval">
- <size
- android:height="@dimen/settingslib_switch_thumb_size"
- android:width="@dimen/settingslib_switch_thumb_size"/>
- <solid
- android:color="@color/settingslib_thumb_off_color"
- android:alpha="?android:attr/disabledAlpha"/>
- </shape>
- </item>
-</layer-list>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_off.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_off.xml
deleted file mode 100644
index 8b69ad1..0000000
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_off.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:top="@dimen/settingslib_switch_thumb_margin"
- android:left="@dimen/settingslib_switch_thumb_margin"
- android:right="@dimen/settingslib_switch_thumb_margin"
- android:bottom="@dimen/settingslib_switch_thumb_margin">
- <shape android:shape="oval">
- <size
- android:height="@dimen/settingslib_switch_thumb_size"
- android:width="@dimen/settingslib_switch_thumb_size"/>
- <solid android:color="@color/settingslib_thumb_off_color"/>
- </shape>
- </item>
-</layer-list>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_on.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_on.xml
deleted file mode 100644
index 0f27fc2..0000000
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_on.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:top="@dimen/settingslib_switch_thumb_margin"
- android:left="@dimen/settingslib_switch_thumb_margin"
- android:right="@dimen/settingslib_switch_thumb_margin"
- android:bottom="@dimen/settingslib_switch_thumb_margin">
- <shape android:shape="oval">
- <size
- android:height="@dimen/settingslib_switch_thumb_size"
- android:width="@dimen/settingslib_switch_thumb_size"/>
- <solid android:color="@color/settingslib_state_on_color"/>
- </shape>
- </item>
-</layer-list>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_selector.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_selector.xml
deleted file mode 100644
index 06bb779..0000000
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_thumb_selector.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/settingslib_thumb_on" android:state_checked="true"/>
- <item android:drawable="@drawable/settingslib_thumb_off" android:state_checked="false"/>
- <item android:drawable="@drawable/settingslib_thumb_disabled" android:state_enabled="false"/>
-</selector>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_track_disabled_background.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_track_disabled_background.xml
deleted file mode 100644
index 15dfcb7..0000000
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_track_disabled_background.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle"
- android:width="@dimen/settingslib_switch_track_width"
- android:height="@dimen/settingslib_switch_track_height">
- <solid
- android:color="@color/settingslib_track_off_color"
- android:alpha="?android:attr/disabledAlpha"/>
- <corners android:radius="@dimen/settingslib_switch_track_radius"/>
-</shape>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_track_off_background.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_track_off_background.xml
deleted file mode 100644
index 4d79a6e..0000000
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_track_off_background.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle"
- android:width="@dimen/settingslib_switch_track_width"
- android:height="@dimen/settingslib_switch_track_height">
- <solid android:color="@color/settingslib_track_off_color"/>
- <corners android:radius="@dimen/settingslib_switch_track_radius"/>
-</shape>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_track_on_background.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_track_on_background.xml
deleted file mode 100644
index c12d012..0000000
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_track_on_background.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle"
- android:width="@dimen/settingslib_switch_track_width"
- android:height="@dimen/settingslib_switch_track_height">
- <solid android:color="@color/settingslib_track_on_color"/>
- <corners android:radius="@dimen/settingslib_switch_track_radius"/>
-</shape>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_track_selector.xml b/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_track_selector.xml
deleted file mode 100644
index a38c3b4..0000000
--- a/packages/SettingsLib/MainSwitchPreference/res/drawable/settingslib_track_selector.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/settingslib_track_on_background" android:state_checked="true"/>
- <item android:drawable="@drawable/settingslib_track_off_background" android:state_checked="false"/>
- <item android:drawable="@drawable/settingslib_track_disabled_background" android:state_enabled="false"/>
-</selector>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
index f9e9eab..2518a6d 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v31/settingslib_main_switch_bar.xml
@@ -60,9 +60,7 @@
android:layout_gravity="center_vertical"
android:focusable="false"
android:clickable="false"
- android:track="@drawable/settingslib_track_selector"
- android:thumb="@drawable/settingslib_thumb_selector"
- android:theme="@style/MainSwitch.Settingslib"/>
+ android:theme="@style/Switch.SettingsLib"/>
</LinearLayout>
</LinearLayout>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml b/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml
index 0c95a9e..57b8446 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml
@@ -16,7 +16,6 @@
-->
<resources xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <color name="settingslib_switchbar_background_color">@*android:color/material_grey_600</color>
<color name="settingslib_switchbar_switch_track_tint">#BFFFFFFF</color>
<color name="settingslib_switchbar_switch_thumb_tint">@android:color/white</color>
</resources>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
index a1cbcf72..a386adb 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
@@ -41,21 +41,6 @@
<!-- Radius of switch bar -->
<dimen name="settingslib_switch_bar_radius">28dp</dimen>
- <!-- Margin of switch thumb -->
- <dimen name="settingslib_switch_thumb_margin">4dp</dimen>
-
- <!-- Size of switch thumb -->
- <dimen name="settingslib_switch_thumb_size">20dp</dimen>
-
- <!-- Width of switch track -->
- <dimen name="settingslib_switch_track_width">52dp</dimen>
-
- <!-- Height of switch track -->
- <dimen name="settingslib_switch_track_height">28dp</dimen>
-
- <!-- Radius of switch track -->
- <dimen name="settingslib_switch_track_radius">35dp</dimen>
-
<!-- SwitchBar sub settings margin start / end -->
<dimen name="settingslib_switchbar_subsettings_margin_start">72dp</dimen>
<dimen name="settingslib_switchbar_subsettings_margin_end">16dp</dimen>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml b/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml
index 472025a..3924e30 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/styles.xml
@@ -23,9 +23,6 @@
<item name="android:textColor">@android:color/black</item>
</style>
- <style name="MainSwitch.Settingslib" parent="@android:style/Widget.Material.CompoundButton.Switch">
- <item name="android:switchMinWidth">@dimen/settingslib_min_switch_width</item>
- </style>
<style name="SwitchBar.Switch.Settingslib" parent="@android:style/Widget.Material.CompoundButton.Switch">
<item name="android:trackTint">@color/settingslib_switchbar_switch_track_tint</item>
diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
index 91e1b93..5f47be4 100644
--- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
+++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java
@@ -30,7 +30,8 @@
import android.widget.TextView;
import androidx.annotation.ColorInt;
-import androidx.core.os.BuildCompat;
+
+import com.android.settingslib.utils.BuildCompatUtils;
import java.util.ArrayList;
import java.util.List;
@@ -74,11 +75,11 @@
LayoutInflater.from(context).inflate(R.layout.settingslib_main_switch_bar, this);
- if (!BuildCompat.isAtLeastS()) {
+ if (!BuildCompatUtils.isAtLeastS()) {
final TypedArray a = context.obtainStyledAttributes(
new int[]{android.R.attr.colorAccent});
mBackgroundActivatedColor = a.getColor(0, 0);
- mBackgroundColor = context.getColor(R.color.settingslib_switchbar_background_color);
+ mBackgroundColor = context.getColor(R.color.material_grey_600);
a.recycle();
}
@@ -88,11 +89,12 @@
mFrameView = findViewById(R.id.frame);
mTextView = (TextView) findViewById(R.id.switch_text);
mSwitch = (Switch) findViewById(android.R.id.switch_widget);
- mBackgroundOn = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_on);
- mBackgroundOff = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_off);
- mBackgroundDisabled = getContext().getDrawable(
- R.drawable.settingslib_switch_bar_bg_disabled);
-
+ if (BuildCompatUtils.isAtLeastS()) {
+ mBackgroundOn = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_on);
+ mBackgroundOff = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_off);
+ mBackgroundDisabled = getContext().getDrawable(
+ R.drawable.settingslib_switch_bar_bg_disabled);
+ }
addOnSwitchChangeListener((switchView, isChecked) -> setChecked(isChecked));
setChecked(mSwitch.isChecked());
@@ -194,9 +196,7 @@
* Remove a listener for switch changes
*/
public void removeOnSwitchChangeListener(OnMainSwitchChangeListener listener) {
- if (mSwitchChangeListeners.contains(listener)) {
- mSwitchChangeListeners.remove(listener);
- }
+ mSwitchChangeListeners.remove(listener);
}
/**
@@ -207,7 +207,7 @@
mTextView.setEnabled(enabled);
mSwitch.setEnabled(enabled);
- if (BuildCompat.isAtLeastS()) {
+ if (BuildCompatUtils.isAtLeastS()) {
if (enabled) {
mFrameView.setBackground(isChecked() ? mBackgroundOn : mBackgroundOff);
} else {
@@ -226,7 +226,7 @@
}
private void setBackground(boolean isChecked) {
- if (!BuildCompat.isAtLeastS()) {
+ if (!BuildCompatUtils.isAtLeastS()) {
setBackgroundColor(isChecked ? mBackgroundActivatedColor : mBackgroundColor);
} else {
mFrameView.setBackground(isChecked ? mBackgroundOn : mBackgroundOff);
@@ -267,10 +267,12 @@
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
+ @Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
+ @Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
index 304c343..d993e44 100644
--- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
+++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java
@@ -22,6 +22,7 @@
import android.widget.AdapterView;
import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceClickListener;
import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.widget.settingsspinner.SettingsSpinner;
@@ -31,12 +32,12 @@
* This preference uses SettingsSpinner & SettingsSpinnerAdapter which provide default layouts for
* both view and drop down view of the Spinner.
*/
-public class SettingsSpinnerPreference extends Preference {
+public class SettingsSpinnerPreference extends Preference implements OnPreferenceClickListener {
private SettingsSpinnerAdapter mAdapter;
private AdapterView.OnItemSelectedListener mListener;
- private int mPosition; //Default 0 for internal shard storage.
- private boolean mIsClickable = true;
+ private int mPosition;
+ private boolean mShouldPerformClick;
/**
* Perform inflation from XML and apply a class-specific base style.
@@ -51,7 +52,7 @@
public SettingsSpinnerPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setLayoutResource(R.layout.settings_spinner_preference);
- setSelectable(false);
+ setOnPreferenceClickListener(this);
}
/**
@@ -64,7 +65,7 @@
public SettingsSpinnerPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setLayoutResource(R.layout.settings_spinner_preference);
- setSelectable(false);
+ setOnPreferenceClickListener(this);
}
/**
@@ -76,6 +77,13 @@
this(context, null);
}
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ mShouldPerformClick = true;
+ notifyChanged();
+ return true;
+ }
+
/** Sets adapter of the spinner. */
public <T extends SettingsSpinnerAdapter> void setAdapter(T adapter) {
mAdapter = adapter;
@@ -101,24 +109,19 @@
notifyChanged();
}
- /** Set clickable of the spinner. */
- public void setClickable(boolean isClickable) {
- if (mIsClickable == isClickable) {
- return;
- }
- mIsClickable = isClickable;
- notifyChanged();
- }
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
final SettingsSpinner spinner = (SettingsSpinner) holder.findViewById(R.id.spinner);
- spinner.setEnabled(mIsClickable);
- spinner.setClickable(mIsClickable);
spinner.setAdapter(mAdapter);
spinner.setSelection(mPosition);
spinner.setOnItemSelectedListener(mOnSelectedListener);
+ if (mShouldPerformClick) {
+ mShouldPerformClick = false;
+ // To show dropdown view.
+ spinner.performClick();
+ }
}
private final AdapterView.OnItemSelectedListener mOnSelectedListener =
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
index aed6338..b78da90 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml
@@ -34,7 +34,7 @@
<!-- Dialog accent color -->
<color name="settingslib_dialog_accent">@android:color/system_accent1_600</color>
<!-- Dialog background color -->
- <color name="settingslib_dialog_background">@android:color/system_neutral1_800</color>
+ <color name="settingslib_dialog_background">@*android:color/surface_light</color>
<!-- Dialog error color. -->
<color name="settingslib_dialog_colorError">#d93025</color> <!-- Red 600 -->
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
index acbf359..ddcc83e 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml
@@ -18,4 +18,5 @@
<resources>
<dimen name="app_preference_padding_start">20dp</dimen>
<dimen name="app_icon_min_width">52dp</dimen>
+ <dimen name="settingslib_preferred_minimum_touch_target">48dp</dimen>
</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
index 9610c94..46f1e03 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml
@@ -25,6 +25,7 @@
<style name="Switch.SettingsLib" parent="@android:style/Widget.Material.CompoundButton.Switch">
<item name="android:switchMinWidth">52dp</item>
+ <item name="android:minHeight">@dimen/settingslib_preferred_minimum_touch_target</item>
<item name="android:track">@drawable/settingslib_switch_track</item>
<item name="android:thumb">@drawable/settingslib_switch_thumb</item>
</style>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
index 6b18f28..8034710 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml
@@ -42,7 +42,7 @@
</style>
<style name="Theme.AlertDialog.SettingsLib" parent="@style/Theme.AppCompat.DayNight.Dialog.Alert">
- <item name="colorAccent">@*android:color/accent_device_default_light</item>
+ <item name="colorAccent">@color/settingslib_dialog_accent</item>
<item name="android:colorError">@color/settingslib_dialog_colorError</item>
<item name="android:colorBackground">@color/settingslib_dialog_background</item>
diff --git a/packages/SettingsLib/SettingsTransition/Android.bp b/packages/SettingsLib/SettingsTransition/Android.bp
index d8cd556..f06a9a7 100644
--- a/packages/SettingsLib/SettingsTransition/Android.bp
+++ b/packages/SettingsLib/SettingsTransition/Android.bp
@@ -11,7 +11,6 @@
name: "SettingsLibSettingsTransition",
srcs: ["src/**/*.java"],
- resource_dirs: ["res"],
static_libs: [
"com.google.android.material_material",
diff --git a/packages/SettingsLib/SettingsTransition/res/values/dimens.xml b/packages/SettingsLib/SettingsTransition/res/values/dimens.xml
deleted file mode 100644
index 0630ca8..0000000
--- a/packages/SettingsLib/SettingsTransition/res/values/dimens.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <dimen name="settings_shared_axis_x_slide_distance">96dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java b/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java
index a6eeb88..540f8c2c 100644
--- a/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java
+++ b/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java
@@ -16,21 +16,11 @@
package com.android.settingslib.transition;
-import androidx.annotation.IntDef;
import android.app.Activity;
-import android.content.Context;
-import android.util.Log;
-import android.view.Window;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import androidx.core.os.BuildCompat;
+import androidx.annotation.IntDef;
import androidx.fragment.app.Fragment;
-import com.google.android.material.transition.platform.FadeThroughProvider;
-import com.google.android.material.transition.platform.MaterialSharedAxis;
-import com.google.android.material.transition.platform.SlideDistanceProvider;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -59,31 +49,6 @@
public static final String EXTRA_PAGE_TRANSITION_TYPE = "page_transition_type";
private static final String TAG = "SettingsTransitionHelper";
- private static final long DURATION = 450L;
- private static final float FADE_THROUGH_THRESHOLD = 0.22F;
-
- private static MaterialSharedAxis createSettingsSharedAxis(Context context, boolean forward) {
- final MaterialSharedAxis transition = new MaterialSharedAxis(MaterialSharedAxis.X, forward);
- transition.excludeTarget(android.R.id.statusBarBackground, true);
- transition.excludeTarget(android.R.id.navigationBarBackground, true);
-
- final SlideDistanceProvider forwardDistanceProvider =
- (SlideDistanceProvider) transition.getPrimaryAnimatorProvider();
- final int distance = context.getResources().getDimensionPixelSize(
- R.dimen.settings_shared_axis_x_slide_distance);
- forwardDistanceProvider.setSlideDistance(distance);
- transition.setDuration(DURATION);
-
- final FadeThroughProvider fadeThroughProvider =
- (FadeThroughProvider) transition.getSecondaryAnimatorProvider();
- fadeThroughProvider.setProgressThreshold(FADE_THROUGH_THRESHOLD);
-
- final Interpolator interpolator =
- AnimationUtils.loadInterpolator(context, R.interpolator.fast_out_extra_slow_in);
- transition.setInterpolator(interpolator);
-
- return transition;
- }
/**
* Apply the forward transition to the {@link Activity}, including Exit Transition and Enter
@@ -92,23 +57,16 @@
* The Exit Transition takes effect when leaving the page, while the Enter Transition is
* triggered when the page is launched/entering.
*/
- public static void applyForwardTransition(Activity activity) {
- if (!isSettingsTransitionEnabled()) {
- return;
- }
- if (activity == null) {
- Log.w(TAG, "applyForwardTransition: Invalid activity!");
- return;
- }
- final Window window = activity.getWindow();
- if (window == null) {
- Log.w(TAG, "applyForwardTransition: Invalid window!");
- return;
- }
- final MaterialSharedAxis forward = createSettingsSharedAxis(activity, true);
- window.setExitTransition(forward);
- window.setEnterTransition(forward);
- }
+ public static void applyForwardTransition(Activity activity) {}
+
+ /**
+ * Apply the forward transition to the {@link Fragment}, including Exit Transition and Enter
+ * Transition.
+ *
+ * The Exit Transition takes effect when leaving the page, while the Enter Transition is
+ * triggered when the page is launched/entering.
+ */
+ public static void applyForwardTransition(Fragment fragment) {}
/**
* Apply the backward transition to the {@link Activity}, including Return Transition and
@@ -118,43 +76,7 @@
* to close. Reenter Transition will be used to move Views in to the scene when returning from a
* previously-started Activity.
*/
- public static void applyBackwardTransition(Activity activity) {
- if (!isSettingsTransitionEnabled()) {
- return;
- }
- if (activity == null) {
- Log.w(TAG, "applyBackwardTransition: Invalid activity!");
- return;
- }
- final Window window = activity.getWindow();
- if (window == null) {
- Log.w(TAG, "applyBackwardTransition: Invalid window!");
- return;
- }
- final MaterialSharedAxis backward = createSettingsSharedAxis(activity, false);
- window.setReturnTransition(backward);
- window.setReenterTransition(backward);
- }
-
- /**
- * Apply the forward transition to the {@link Fragment}, including Exit Transition and Enter
- * Transition.
- *
- * The Exit Transition takes effect when leaving the page, while the Enter Transition is
- * triggered when the page is launched/entering.
- */
- public static void applyForwardTransition(Fragment fragment) {
- if (!isSettingsTransitionEnabled()) {
- return;
- }
- if (fragment == null) {
- Log.w(TAG, "applyForwardTransition: Invalid fragment!");
- return;
- }
- final MaterialSharedAxis forward = createSettingsSharedAxis(fragment.getContext(), true);
- fragment.setExitTransition(forward);
- fragment.setEnterTransition(forward);
- }
+ public static void applyBackwardTransition(Activity activity) {}
/**
* Apply the backward transition to the {@link Fragment}, including Return Transition and
@@ -164,20 +86,5 @@
* to close. Reenter Transition will be used to move Views in to the scene when returning from a
* previously-started Fragment.
*/
- public static void applyBackwardTransition(Fragment fragment) {
- if (!isSettingsTransitionEnabled()) {
- return;
- }
- if (fragment == null) {
- Log.w(TAG, "applyBackwardTransition: Invalid fragment!");
- return;
- }
- final MaterialSharedAxis backward = createSettingsSharedAxis(fragment.getContext(), false);
- fragment.setReturnTransition(backward);
- fragment.setReenterTransition(backward);
- }
-
- private static boolean isSettingsTransitionEnabled() {
- return BuildCompat.isAtLeastS();
- }
+ public static void applyBackwardTransition(Fragment fragment) {}
}
diff --git a/packages/SettingsLib/Utils/AndroidManifest.xml b/packages/SettingsLib/Utils/AndroidManifest.xml
index 96d9e51..fd89676 100644
--- a/packages/SettingsLib/Utils/AndroidManifest.xml
+++ b/packages/SettingsLib/Utils/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.settingslib.widget">
+ package="com.android.settingslib.utils">
<uses-sdk android:minSdkVersion="21" />
diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java
new file mode 100644
index 0000000..44f6f54
--- /dev/null
+++ b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java
@@ -0,0 +1,61 @@
+/*
+ * 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.settingslib.utils;
+
+import android.os.Build.VERSION;
+
+/**
+ * An util class to check whether the current OS version is higher or equal to sdk version of
+ * device.
+ */
+public final class BuildCompatUtils {
+
+ /**
+ * Implementation of BuildCompat.isAtLeast*() suitable for use in Settings
+ *
+ * <p>This still should try using BuildCompat.isAtLeastR() as source of truth, but also checking
+ * for VERSION_SDK_INT and VERSION.CODENAME in case when BuildCompat implementation returned
+ * false. Note that both checks should be >= and not = to make sure that when Android version
+ * increases (i.e., from R to S), this does not stop working.
+ *
+ * <p>Supported configurations:
+ *
+ * <ul>
+ * <li>For current Android release: when new API is not finalized yet (CODENAME = "S", SDK_INT
+ * = 30|31)
+ * <li>For current Android release: when new API is finalized (CODENAME = "REL", SDK_INT = 31)
+ * <li>For next Android release (CODENAME = "T", SDK_INT = 30+)
+ * </ul>
+ *
+ * <p>Note that Build.VERSION_CODES.S cannot be used here until final SDK is available, because
+ * it is equal to Build.VERSION_CODES.CUR_DEVELOPMENT before API finalization.
+ *
+ * @return Whether the current OS version is higher or equal to S.
+ */
+ public static boolean isAtLeastS() {
+ if (VERSION.SDK_INT < 30) {
+ return false;
+ }
+
+ return (VERSION.CODENAME.equals("REL") && VERSION.SDK_INT >= 31)
+ || (VERSION.CODENAME.length() == 1
+ && VERSION.CODENAME.compareTo("S") >= 0
+ && VERSION.CODENAME.compareTo("Z") <= 0);
+ }
+
+ private BuildCompatUtils() {}
+}
diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java
index 4505dad..5dc0b72 100644
--- a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java
+++ b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java
@@ -22,7 +22,7 @@
import android.os.UserManager;
import android.util.Log;
-import com.android.settingslib.widget.R;
+import com.android.settingslib.utils.R;
public class AppUtils {
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 366cb06..8056a31 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Bynaam"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Voeg gas by"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Verwyder gas"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Stel gassessie terug"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gas"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Neem \'n foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Kies \'n prent"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Kies foto"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 8a5da97..42d9eba 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"ቅጽል ስም"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"እንግዳን አክል"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"እንግዳን አስወግድ"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"እንግዳን ዳግም አስጀምር"</string>
<string name="guest_nickname" msgid="6332276931583337261">"እንግዳ"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"ፎቶ አንሳ"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ምስል ይምረጡ"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ፎቶ ይምረጡ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 0eef43b..2df9ba7 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -511,8 +511,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"المنبّهات والتذكيرات"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"السماح بضبط المنبّهات والتذكيرات"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"المنبّهات والتذكيرات"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"عليك السماح لهذا التطبيق بضبط المنبّهات وتحديد مواعيد للإجراءات الحساسة زمنيًا. يسمح هذا الأذن بتشغيل التطبيق في الخلفية، ما قد يستهلك المزيد من شحن البطارية.\n\nإذا كان هذا الإذن غير مسموح به، لن تعمل الأحداث المستندة إلى وقت والمنبّهات الحالية التي يحدِّد هذا التطبيق موعدها."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"جدول زمني، جدولة، منبّه، تذكير، ساعة"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"تفعيل"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"تفعيل ميزة \"عدم الإزعاج\""</string>
@@ -571,7 +570,14 @@
<string name="user_nickname" msgid="262624187455825083">"اللقب"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"إضافة ضيف"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"إزالة جلسة الضيف"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"إعادة ضبط جلسة الضيف"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ضيف"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"التقاط صورة"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"اختيار صورة"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"اختيار صورة"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index b8796fe..87dc336 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -507,8 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"এলাৰ্ম আৰু ৰিমাইণ্ডাৰ"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"এলাৰ্ম আৰু ৰিমাইণ্ডাৰ ছেট কৰাৰ অনুমতি দিয়ক"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"এলাৰ্ম আৰু ৰিমাইণ্ডাৰ"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"এই এপ্টোক এলাৰ্ম ছেট কৰিবলৈ আৰু সময় সংবেদনশীল কাৰ্যৰ সময়সূচী নিৰ্ধাৰণ কৰিবলৈ দিয়ক। ই এপ্টোক নেপথ্যত চলি থকাৰ অনুমতি দিয়ে যাৰ ফলত অধিক বেটাৰী ব্যৱহাৰ হয়।\n\nএই অনুমতিটো অফ কৰা থাকিলে, ইতিমধ্যে ছেট কৰা এলাৰ্ম আৰু এই এপ্টোৱে সময়সূচী নিৰ্ধাৰণ কৰা সময় ভিত্তিক অনুষ্ঠানসমূহে কাম নকৰা হ’ব।"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"সময়সূচী, এলাৰ্ম, ৰিমাইণ্ডাৰ, ঘড়ী"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"অন কৰক"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"অসুবিধা নিদিব অন কৰক"</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"উপনাম"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ কৰক"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি আঁতৰাওক"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"অতিথিৰ ছেশ্বন ৰিছেট কৰক"</string>
<string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"এখন ফট’ তোলক"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"এখন প্ৰতিচ্ছবি বাছনি কৰক"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ফট’ বাছনি কৰক"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index a5a3321..eb0c74a 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Ləqəb"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Qonaq əlavə edin"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Qonağı silin"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Qonaq sessiyasını sıfırlayın"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Qonaq"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Foto çəkin"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Şəkil seçin"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Foto seçin"</string>
@@ -574,7 +581,7 @@
<string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiv"</string>
<string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string>
<string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"Bu dəyişikliyin tətbiq edilməsi üçün cihaz yenidən başladılmalıdır. İndi yenidən başladın və ya ləğv edin."</string>
- <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Simli qulaqlıq"</string>
+ <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"Naqilli qulaqlıq"</string>
<string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"Aktiv"</string>
<string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"Deaktiv"</string>
<string name="carrier_network_change_mode" msgid="4257621815706644026">"Operator şəbəkəsinin dəyişilməsi"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 7903230..52cf443 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -567,7 +567,14 @@
<string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Resetuj sesiju gosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Slikaj"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Izaberite sliku"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 0fb7178..b633841 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -509,8 +509,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Будзільнікі і напаміны"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Дазволіць усталёўваць будзільнікі і напаміны"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Будзільнікі і напаміны"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Дазвольце гэтай праграме ўключаць будзільнікі і задаваць час дзеянняў. З такім дазволам праграма можа працаваць у фонавым рэжыме і ў выніку хутчэй разраджаць акумулятар.\n\nКалі вы не ўключыце гэты дазвол, існуючыя будзільнікі і запланаваны праграмай час падзей не будуць працаваць."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"расклад, будзільнік, напамін, гадзіннік"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Уключыць"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Уключэнне рэжыму \"Не турбаваць\""</string>
@@ -569,7 +568,14 @@
<string name="user_nickname" msgid="262624187455825083">"Псеўданім"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Дадаць госця"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Выдаліць госця"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Скінуць гасцявы сеанс"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Госць"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Зрабіць фота"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Выбраць відарыс"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Выбраць фота"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 4d92282..4b3dbe4 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Добавяне на гост"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Премахване на госта"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Нулиране на сесията като гост"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Гост"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Правене на снимка"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Избиране на изображение"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Избиране на снимката"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index b7fdef2..eca40f9 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -507,8 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"অ্যালার্ম এবং রিমাইন্ডার"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"অ্যালার্ম এবং রিমাইন্ডার সেট করার অনুমতি দিন"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"অ্যালার্ম এবং রিমাইন্ডার"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"অ্যালার্ম এবং সময়ের মধ্যে শেষ করতে হবে এমন অ্যাকশনের শিডিউল সেট করতে এই অ্যাপকে অনুমতি দিন। এর ফলে ব্যাকগ্রাউন্ডে অ্যাপ চলতে পারে, যার জন্য আরও ব্যাটারির চার্জ খরচ হতে পারে।\n\nএই অনুমতি বন্ধ করা থাকলে, আগে থেকে থাকা অ্যালার্ম এবং অ্যাপের মাধ্যমে শিডিউল করা সময় ভিত্তিক ইভেন্টের রিমাইন্ডার কাজ করবে না।"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"শিডিউল, অ্যালার্ম, রিমাইন্ডার, ঘড়ি"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"চালু করুন"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'বিরক্ত করবে না\' মোড চালু করুন"</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"বিশেষ নাম"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ করুন"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি সরান"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"অতিথি সেশন রিসেট করুন"</string>
<string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"ফটো তুলুন"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"একটি ইমেজ বেছে নিন"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ফটো বেছে নিন"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index d3049b1..029d62e 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -567,7 +567,14 @@
<string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Poništi sesiju gosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Snimite fotografiju"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberite sliku"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Odabir fotografije"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index eab1cd2..47b95a5 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -507,7 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmes i recordatoris"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Permet la configuració d\'alarmes i recordatoris"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes i recordatoris"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permet que aquesta aplicació configuri alarmes i programi accions urgents. Això permet a l\'aplicació executar-se en segon pla i, per tant, és possible que consumeixi més bateria.\n\nSi aquest permís està desactivat, les alarmes i els esdeveniments urgents que ja hagi programat l\'aplicació no funcionaran."</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permet que aquesta aplicació configuri alarmes i programi accions. Això permet a l\'aplicació executar-se en segon pla i, per tant, és possible que consumeixi més bateria.\n\nSi aquest permís està desactivat, les alarmes i els esdeveniments que ja hagi programat l\'aplicació no funcionaran."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programació, alarma, recordatori, rellotge"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activa"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activa el mode No molestis"</string>
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Àlies"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Afegeix un convidat"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Suprimeix el convidat"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Restableix el convidat"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Convidat"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Fes una foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Tria una imatge"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Selecciona una foto"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 7376d7ee..8a16ae6 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -568,7 +568,14 @@
<string name="user_nickname" msgid="262624187455825083">"Přezdívka"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Přidat hosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Odstranit hosta"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Resetovat hosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Host"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Pořídit fotku"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrat obrázek"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Vybrat fotku"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 1dc2d14..30bf200 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Kaldenavn"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæst"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gæsten"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Nulstil gæstesession"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gæst"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Tag et billede"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Vælg et billede"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Vælg billede"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index f092a51..103d23b 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -507,8 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Wecker und Erinnerungen"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Erlauben, Wecker und Erinnerungen einzurichten"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Wecker und Erinnerungen"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Dieser App erlauben, Wecker zu stellen und zeitgebundene Aktionen zu planen. Dadurch läuft die App im Hintergrund. Dies kann den Akkuverbrauch erhöhen. \n\nWenn diese Berechtigung deaktiviert ist, funktionieren bereits gestellte Wecker und zeitgebundene Ereignisse, die von dieser App geplant sind, nicht wie erwartet."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planen, Wecker, Erinnerung, Uhr"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktivieren"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"„Bitte nicht stören“ aktivieren"</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Alias"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Gast hinzufügen"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Gast entfernen"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Gast zurücksetzen"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gast"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Foto machen"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Bild auswählen"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Foto auswählen"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 5d4301c..9a18268 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -507,8 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Ξυπνητήρια και ειδοποιήσεις"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Να επιτρέπεται ο ορισμός ξυπνητ. και υπενθυμίσεων"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Ξυπνητήρια και υπενθυμίσεις"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Επιτρέψτε σε αυτήν την εφαρμογή να ορίζει ξυπνητήρια και να προγραμματίζει ενέργειες που εξαρτώνται από τον χρόνο. Αυτό επιτρέπει στην εφαρμογή να εκτελείται στο παρασκήνιο και, ως εκ τούτου, μπορεί να καταναλώνει περισσότερη μπαταρία.\n\nΑν αυτή η άδεια δεν είναι ενεργή, τα υπάρχοντα ξυπνητήρια και συμβάντα βάσει χρόνου που έχουν προγραμματιστεί από αυτήν την εφαρμογή δεν θα λειτουργούν."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"χρονοδιάγραμμα, ξυπνητήρι, υπενθύμιση, ρολόι"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ενεργοποίηση"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ενεργοποίηση λειτουργίας \"Μην ενοχλείτε\""</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Ψευδώνυμο"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Προσθήκη επισκέπτη"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Κατάργηση επισκέπτη"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Επαναφορά περιόδου επισκέπτη"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Επισκέπτης"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Λήψη φωτογραφίας"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Επιλογή εικόνας"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Επιλογή φωτογραφίας"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 02317ea..d4ef073 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Reset guest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index f15db49..62e204d 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Reset guest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 02317ea..d4ef073 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Reset guest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 02317ea..d4ef073 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Reset guest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index ce5a6bc..b6acd89 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Reset guest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index d050ff8..bb3413f 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -457,11 +457,11 @@
<string name="power_charging_limited" msgid="7956120998372505295">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga limitada temporalmente"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
- <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápido"</string>
+ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápidamente"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carga lenta"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carga inalámbrica"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"No se está cargando."</string>
- <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Está conectado, pero no se está cargando"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado; no se está cargando"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlada por el administrador"</string>
<string name="disabled" msgid="8017887509554714950">"Inhabilitada"</string>
@@ -507,8 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmas y recordatorios"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Permitir configuración de alarmas y recordatorios"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmas y recordatorios"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite que esta app establezca alarmas y programe acciones para horarios específicos. De esta manera, la app puede ejecutarse en segundo plano, lo que podría aumentar el consumo de batería.\n\nSi se desactiva este permiso, no funcionarán las alarmas ni los eventos basados en el tiempo existentes que programe esta app."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programar, alarma, recordatorio, reloj"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activar"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activar No interrumpir"</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Sobrenombre"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Agregar invitado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Restablecer perfil de invitado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Tomar una foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Elegir una imagen"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 540a472..75892e2 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -457,11 +457,11 @@
<string name="power_charging_limited" msgid="7956120998372505295">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga limitada temporalmente"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
- <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápidamente"</string>
- <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Cargando lentamente"</string>
+ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carga rápida"</string>
+ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carga lenta"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Cargando sin cables"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"No se está cargando"</string>
- <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado, no se carga"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado pero sin cargar"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Controlada por el administrador"</string>
<string name="disabled" msgid="8017887509554714950">"Inhabilitada"</string>
@@ -507,7 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmas y recordatorios"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Permitir la programación de alarmas y recordatorios"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmas y recordatorios"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite que esta aplicación programe alarmas y otras acciones de carácter temporal. Este permite sirve para que la aplicación siga activa en segundo plano, lo que puede usar más batería.\n\nSi este permiso está desactivados, no funcionarán las alarmas ni los eventos con carácter temporal programados por esta aplicación."</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite que esta aplicación programe alarmas y otras acciones que se llevan a cabo a una hora determinada. Esto hace que la aplicación siga activa en segundo plano, lo que puede usar más batería.\n\nSi este permiso está desactivado, no funcionarán las alarmas ni los eventos que se activan a una hora determinada que programe esta aplicación."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programar, alarma, recordatorio, reloj"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activar"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activar el modo No molestar"</string>
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Apodo"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Añadir invitado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Restablecer invitado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Hacer foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Seleccionar una imagen"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index f740cc5..f7de768 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -507,7 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmid ja meeldetuletused"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Luba alarmide ja meeldetuletuste määramine"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmid ja meeldetuletused"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lubage sellel rakendusel määrata äratusi ja ajastada kiire tähtajaga toiminguid. See võimaldab rakendusel töötada taustal, mistõttu võib akukasutus olla suurem.\n\nKui see luba on välja lülitatud, siis olemasolevad äratused ja selle rakenduse ajastatud ajapõhised sündmused ei tööta."</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lubage sellel rakendusel määrata alarme ja ajastada kiire tähtajaga toiminguid. See võimaldab rakendusel töötada taustal, mistõttu võib akukasutus olla suurem.\n\nKui see luba on välja lülitatud, siis olemasolevad alarmid ja selle rakenduse ajastatud ajapõhised sündmused ei tööta."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ajakava, äratus, meeldetuletus, kell"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Lülita sisse"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Valiku Mitte segada sisselülitamine"</string>
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Hüüdnimi"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Lisa külaline"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Eemalda külaline"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Lähtesta külastajaseanss"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Külaline"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Pildistage"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Valige pilt"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Valige foto"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 06e1abd..08f4fe5 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -528,7 +528,7 @@
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio-gailu kableduna"</string>
<string name="help_label" msgid="3528360748637781274">"Laguntza eta iritziak"</string>
<string name="storage_category" msgid="2287342585424631813">"Biltegiratzea"</string>
- <string name="shared_data_title" msgid="1017034836800864953">"Partekatutako datuak"</string>
+ <string name="shared_data_title" msgid="1017034836800864953">"Datu partekatuak"</string>
<string name="shared_data_summary" msgid="5516326713822885652">"Ikusi eta aldatu partekatutako datuak"</string>
<string name="shared_data_no_blobs_text" msgid="3108114670341737434">"Ez dago erabiltzaile honen datu partekaturik."</string>
<string name="shared_data_query_failure_text" msgid="3489828881998773687">"Errore bat gertatu da datu partekatuak eskuratzean. Saiatu berriro."</string>
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Goitizena"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Gehitu gonbidatua"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Kendu gonbidatua"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Berrezarri gonbidatuentzako saioa"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gonbidatua"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Atera argazki bat"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Aukeratu irudi bat"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Hautatu argazki bat"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 92e624d..313031b 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"نام مستعار"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"افزودن مهمان"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"حذف مهمان"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"بازنشانی مهمان"</string>
<string name="guest_nickname" msgid="6332276931583337261">"مهمان"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"عکس گرفتن"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"انتخاب تصویر"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"انتخاب عکس"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 11bcb3a..40a3d21 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -507,8 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Herätykset ja muistutukset"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Salli herätysten ja muistutusten lisääminen"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Herätykset ja muistutukset"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Anna sovelluksen lisätä herätyksiä ja ajoittaa kiireellisiä tapahtumia. Näin sovellus voi toimia taustalla, mikä voi kuluttaa enemmän virtaa.\n\nIlman tätä lupaa sovelluksen ajoittamat herätykset ja aikaan perustuvat tapahtumat eivät toimi."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ajoitus, herätys, muistutus, kello"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Ota käyttöön"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Ota Älä häiritse ‑tila käyttöön"</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Lempinimi"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Lisää vieras"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Poista vieras"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Nollaa vieras"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Vieras"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Ota kuva"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Valitse kuva"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Valitse kuva"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index a622385..e877557 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Pseudo"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Réinitialiser la session Invité"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invité"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Sélectionner une image"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Sélectionnez une photo"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index c4797fc..0b0ee65 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -461,7 +461,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charge lente"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"En charge sans fil"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Pas en charge"</string>
- <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connecté, pas en charge"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connectée, pas en charge"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"Chargée"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Contrôlé par l\'administrateur"</string>
<string name="disabled" msgid="8017887509554714950">"Désactivée"</string>
@@ -507,7 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmes et rappels"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Autoriser à définir des alarmes et des rappels"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes et rappels"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Autorisez cette appli à définir des alarmes et à planifier des actions soumises à un délai. Cela lui permet de s\'exécuter en arrière-plan, ce qui peut consommer plus de batterie.\n\nSi cette autorisation est désactivée, les alarmes existantes et les événements basés sur l\'heure planifiés par cette appli ne fonctionneront pas."</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Autorisez cette appli à définir des alarmes et à programmer des actions à certaines heures. Elle s\'exécutera alors en arrière-plan, ce qui peut solliciter davantage la batterie.\n\nSi l\'autorisation est désactivée, les alarmes existantes et les événements programmés par l\'appli ne fonctionneront pas."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"définir, alarme, rappel, horloge"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activer"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activer le mode Ne pas déranger"</string>
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Pseudo"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Réinitialiser la session Invité"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invité"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Choisir une image"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Sélectionner une photo"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 0d009de..5cb0d2a 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -507,8 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmas e recordatorios"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Permitir axuste de alarmas e recordatorios"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmas e recordatorios"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite que esta aplicación defina alarmas e planifique accións que dependan da hora. Con este permiso, a aplicación pode executarse en segundo plano, o que pode provocar un maior consumo de batería.\n\nSe este permiso está desactivado, non funcionarán as alarmas que xa se definisen nin os eventos que dependan da hora planificados por esta aplicación."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planificar, alarma, recordatorio, reloxo"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activar"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activar modo Non molestar"</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Alcume"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Engadir convidado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Quitar convidado"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Restablecer sesión de convidado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Tirar foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Escoller imaxe"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index b8d8f35..d09a8e6 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -454,7 +454,7 @@
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
<string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"પૂર્ણ ચાર્જ થવામાં <xliff:g id="TIME">%1$s</xliff:g> બાકી છે"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - પૂર્ણ ચાર્જ થવામાં <xliff:g id="TIME">%2$s</xliff:g> બાકી છે"</string>
- <string name="power_charging_limited" msgid="7956120998372505295">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ હંગામી રૂપે પ્રતિબંધિત કરવામાં આવ્યું છે"</string>
+ <string name="power_charging_limited" msgid="7956120998372505295">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ હંગામીરૂપે પ્રતિબંધિત કરવામાં આવ્યું છે"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"અજાણ્યું"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"ચાર્જ થઈ રહ્યું છે"</string>
<string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ઝડપથી ચાર્જ થાય છે"</string>
@@ -507,8 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"અલાર્મ અને રિમાઇન્ડર"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"અલાર્મ અને રિમાન્ડરના સેટિંગની મંજૂરી આપો"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"અલાર્મ અને રિમાઇન્ડર"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"આ ઍપને અલાર્મ સેટ કરવા અને સમય પ્રતિ સંવેદનશીલ ક્રિયાઓ શેડ્યૂલ કરવા માટે મંજૂરી આપો. આ ઍપને બૅકગ્રાઉન્ડમાં ચાલવા દે છે, જેને કારણે બૅટરીનો વધુ વપરાશ થઈ શકે છે.\n\nજો આ પરવાનગી બંધ હોય, તો આ ઍપ દ્વારા શેડ્યૂલ કરવામાં આવેલા વર્તમાન અલાર્મ અને સમય આધારિત ઇવેન્ટ કામ કરશે નહીં."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"શેડ્યૂલ, અલાર્મ, રિમાઇન્ડર, ઘડિયાળ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ચાલુ કરો"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"ખલેલ પાડશો નહીં ચાલુ કરો"</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"ઉપનામ"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"અતિથિ ઉમેરો"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"અતિથિને કાઢી નાખો"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"અતિથિને રીસેટ કરો"</string>
<string name="guest_nickname" msgid="6332276931583337261">"અતિથિ"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"ફોટો લો"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"છબી પસંદ કરો"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ફોટો પસંદ કરો"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 8abbfcf..36cf9f2 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -507,7 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"अलार्म और रिमाइंडर"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"अलार्म और रिमाइंडर सेट करने की अनुमति दें"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"अलार्म और रिमाइंडर"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"इस ऐप्लिकेशन को अलार्म और तय समय पर होने वाली कार्रवाइयों के रिमाइंडर सेट करने की अनुमति दें. ऐसा करने से, ऐप्लिकेशन को बैकग्राउंड में चलने की अनुमति मिलती है. इससे, बैटरी ज़्यादा खर्च होती है.\n\nअगर आप यह अनुमति नहीं देते हैं, तो इस ऐप्लिकेशन की मदद से सेट किए गए अलार्म और तय समय पर होने वाली कार्रवाइयों के रिमाइंडर काम नहीं करेंगे."</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"इस ऐप्लिकेशन को अलार्म और तय समय पर होने वाली कार्रवाइयों के रिमाइंडर सेट करने की अनुमति दें. ऐसा करने से, ऐप्लिकेशन को बैकग्राउंड में चलने की अनुमति मिलती है. इससे बैटरी ज़्यादा खर्च होती है.\n\nअगर आप यह अनुमति नहीं देते हैं, तो इस ऐप्लिकेशन की मदद से सेट किए गए अलार्म और तय समय पर होने वाली कार्रवाइयों के रिमाइंडर काम नहीं करेंगे."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"शेड्यूल, अलार्म, रिमाइंडर, घड़ी"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"चालू करें"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'परेशान न करें\' चालू करें"</string>
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"प्रचलित नाम"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"मेहमान जोड़ें"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"मेहमान हटाएं"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"मेहमान के तौर पर ब्राउज़ करने का सेशन रीसेट करें"</string>
<string name="guest_nickname" msgid="6332276931583337261">"मेहमान"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"फ़ोटो खींचें"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"कोई इमेज चुनें"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"फ़ोटो चुनें"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 217ff15..43d640c 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -508,7 +508,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmi i podsjetnici"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Dopusti postavljanje alarma i podsjetnika"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmi i podsjetnici"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Aplikaciji omogućuje da postavlja alarme i zakazuje vremenski osjetljive radnje. To aplikaciji omogućuje da se izvodi u pozadini, pa je moguće dodatno trošenje baterije.\n\nAko je to dopuštenje isključeno, postojeći alarmi i događaji temeljeni na vremenu koji su zakazani putem ove aplikacije neće funkcionirati."</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Omogućite toj aplikaciji da postavlja alarme i zakazuje radnje u točno određeno vrijeme. To aplikaciji omogućuje da se izvodi u pozadini, pa je moguća dodatna potrošnja baterije.\n\nAko je to dopuštenje isključeno, postojeći alarmi i događaji zakazani putem te aplikacije neće funkcionirati."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"raspored, alarm, podsjetnik, sat"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Uključi"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Uključite opciju Ne uznemiravaj."</string>
@@ -567,7 +567,14 @@
<string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodavanje gosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Uklanjanje gosta"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Poništi gostujuću sesiju"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Fotografiraj"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Odabir slike"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index e75b0dd..fc4592d 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Becenév"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Vendég hozzáadása"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Vendég munkamenet eltávolítása"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Vendég munkamenet visszaállítása"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Vendég"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Fotó készítése"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Kép kiválasztása"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Fotó kiválasztása"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 44b322d..168ab43e 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Կեղծանուն"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Ավելացնել հյուր"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Հեռացնել հյուրին"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Վերակայել հյուրի աշխատաշրջանը"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Հյուր"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Լուսանկարել"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Ընտրել պատկեր"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Ընտրեք լուսանկար"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 4bb4724..b9b5dcb 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Tambahkan tamu"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Hapus tamu"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Reset tamu"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Tamu"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih gambar"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Pilih foto"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index b224330..0b5a44a 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -507,8 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Vekjarar og áminningar"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Leyfa stillingu vekjara og áminninga"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Vekjarar og áminningar"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Leyfa þessu forriti að stilla vekjara og áætla aðgerðir sem þurfa að eiga sér stað innan ákveðins tímaramma. Þetta leyfir forritinu að keyra í bakgrunninum sem getur notað meiri rafhlöðuorku.\n\nEf slökkt er á þessari heimild munu núverandi vekjarar og tímasettir viðburðir sem þetta forrit stillir ekki virka."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"áætlun, vekjari, áminning, klukka"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Kveikja"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Kveikja á „Ónáðið ekki“"</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Gælunafn"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Bæta gesti við"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Fjarlægja gest"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Endurstilla gestastillingu"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gestur"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Taka mynd"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Velja mynd"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Velja mynd"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 1d2295c..c07b510 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Aggiungi ospite"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Rimuovi ospite"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Reimposta sessione Ospite"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Ospite"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Scatta una foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Scegli un\'immagine"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Seleziona la foto"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 5c894a1..11613aa 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -509,7 +509,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"שעונים מעוררים ותזכורות"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"אישור להגדיר שעונים מעוררים ותזכורות"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"שעונים מעוררים ותזכורות"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ההגדרה הזו מתירה לאפליקציה להגדיר שעון מעורר ולתזמן פעולות דחופות. האפליקציה תוכל לפעול ברקע ובכך להגביר את צריכת הסוללה.\n\nאם ההרשאה מושבתת, ההתראות והאירועים מבוססי-הזמן שהוגדרו ותוזמנו על ידי האפליקציה לא יפעלו."</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ההרשאה הזו מתירה לאפליקציה להגדיר שעון מעורר ולתזמן פעולות דחופות. האפליקציה תוכל לפעול ברקע ובכך להגביר את צריכת הסוללה.\n\nאם ההרשאה מושבתת, ההתראות והאירועים מבוססי-הזמן שהוגדרו ותוזמנו על ידי האפליקציה לא יפעלו."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"תזמון, שעון מעורר, תזכורת, שעון"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"הפעלה"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"הפעלת מצב נא לא להפריע"</string>
@@ -568,7 +568,14 @@
<string name="user_nickname" msgid="262624187455825083">"כינוי"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"הוספת אורח"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"הסרת אורח/ת"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"איפוס הגלישה כאורח"</string>
<string name="guest_nickname" msgid="6332276931583337261">"אורח"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"צילום תמונה"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"לבחירת תמונה"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"בחירת תמונה"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 3f23c75e..0d98614 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -507,7 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"アラームとリマインダー"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"アラームとリマインダーの設定を許可する"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"アラームとリマインダー"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"アラームの設定や時間の制約があるアクションのスケジュールを、このアプリに許可します。これによりアプリがバックグラウンドで実行できるようになるため、バッテリーの使用量が増えることがあります。\n\nこの権限が OFF の場合、このアプリで設定された既存のアラームと時間ベースのイベントは機能しなくなります。"</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"アラームの設定や時間ベースのアクション設定を、このアプリに許可します。これによりアプリがバックグラウンドで実行できるようになるため、バッテリーの使用量が増えることがあります。\n\nこの権限が OFF の場合、このアプリで設定された既存のアラームと時間ベースのイベントは機能しなくなります。"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"スケジュール, アラーム, リマインダー, 時計"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ON にする"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"サイレント モードを ON にする"</string>
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"ニックネーム"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"ゲストを追加"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ゲストを削除"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"ゲストをリセット"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ゲスト"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"写真を撮る"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"画像を選択"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"写真を選択"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 4723771..821c98a 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"მეტსახელი"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"სტუმრის დამატება"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"სტუმრის ამოშლა"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"სტუმრის სესიის გადაყენება"</string>
<string name="guest_nickname" msgid="6332276931583337261">"სტუმარი"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"ფოტოს გადაღება"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"აირჩიეთ სურათი"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ფოტოს არჩევა"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index baae75b..89556da 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Лақап ат"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Қонақ қосу"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты жою"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Қонақ сеансын әдепкі күйге қайтару"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Қонақ"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Фотосуретке түсіру"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Сурет таңдау"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Фотосурет таңдау"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index fdbcba4..33902be 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -507,7 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"ម៉ោងរោទ៍ និងការរំលឹក"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"អនុញ្ញាតឱ្យកំណត់ម៉ោងរោទ៍ និងការរំលឹក"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ម៉ោងរោទ៍ និងការរំលឹក"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"អនុញ្ញាតឱ្យកម្មវិធីនេះកំណត់ម៉ោងរោទ៍ និងកំណត់កាលវិភាគសកម្មភាពដែលតម្រូវឱ្យទាន់ពេលវេលា។ សកម្មភាពនេះអនុញ្ញាតឱ្យកម្មវិធីនេះដំណើរការនៅផ្ទៃខាងក្រោយ ដែលអាចប្រើថ្មច្រើន។\n\nប្រសិនបើបិទការអនុញ្ញាតនេះ ម៉ោងរោទ៍ដែលមានស្រាប់ និងព្រឹត្តិការណ៍ផ្អែកលើពេលវេលាដែលកម្មវិធីនេះបានកំណត់កាលវិភាគនឹងមិនដំណើរការទេ។"</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"អនុញ្ញាតឱ្យកម្មវិធីនេះកំណត់ម៉ោងរោទ៍ និងកំណត់កាលវិភាគសកម្មភាពដែលតម្រូវឱ្យទាន់ពេលវេលា។ ការធ្វើបែបនេះអនុញ្ញាតឱ្យកម្មវិធីនេះដំណើរការនៅផ្ទៃខាងក្រោយ ដែលអាចប្រើថ្មច្រើនជាងមុន។\n\nប្រសិនបើបិទការអនុញ្ញាតនេះ ម៉ោងរោទ៍ដែលមានស្រាប់ និងព្រឹត្តិការណ៍ផ្អែកលើពេលវេលាដែលកំណត់ដោយកម្មវិធីនេះនឹងមិនដំណើរការទេ។"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"កាលវិភាគ ម៉ោងរោទ៍ ការរំលឹក នាឡិកា"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"បើក"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"បើកមុខងារកុំរំខាន"</string>
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"ឈ្មោះហៅក្រៅ"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"បញ្ចូលភ្ញៀវ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ដកភ្ញៀវចេញ"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"កំណត់ភ្ញៀវឡើងវិញ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ភ្ញៀវ"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"ថតរូប"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ជ្រើសរើសរូបភាព"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ជ្រើសរើសរូបថត"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 21dce27..d40f256 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -507,8 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"ಅಲಾರಾಮ್ಗಳು ಮತ್ತು ರಿಮೈಂಡರ್ಗಳು"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"ಅಲಾರಂಗಳು ಮತ್ತು ರಿಮೈಂಡರ್ಗಳನ್ನು ಹೊಂದಿಸಲು ಅನುಮತಿಸಿ"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ಅಲಾರಂಗಳು ಮತ್ತು ರಿಮೈಂಡರ್ಗಳು"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ಅಲಾರಂಗಳನ್ನು ಹೊಂದಿಸಲು ಮತ್ತು ಸಮಯ-ಸೂಕ್ಷ್ಮವಾದ ಕ್ರಿಯೆಗಳನ್ನು ನಿಗದಿಪಡಿಸಲು ಈ ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸಿ. ಇದು ಹಿನ್ನೆಲೆಯಲ್ಲಿ ರನ್ ಆಗಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ, ಅದರಿಂದ ಹೆಚ್ಚು ಬ್ಯಾಟರಿ ಬಳಕೆಯಾಗಬಹುದು.\n\nಈ ಅನುಮತಿ ಆಫ್ ಆಗಿದ್ದರೆ, ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಅಲಾರಂಗಳು ಮತ್ತು ಈ ಆ್ಯಪ್ ನಿಗದಿಪಡಿಸಿದ ಸಮಯ-ಸೂಕ್ಷ್ಮ ಈವೆಂಟ್ಗಳು ಕೆಲಸ ಮಾಡುವುದಿಲ್ಲ."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ವೇಳಾಪಟ್ಟಿ, ಅಲಾರಂ, ರಿಮೈಂಡರ್, ಗಡಿಯಾರ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ಆನ್ ಮಾಡಿ"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ ಅನ್ನು ಆನ್ ಮಾಡಿ"</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"ಅಡ್ಡ ಹೆಸರು"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"ಅತಿಥಿಯನ್ನು ಸೇರಿಸಿ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"ಅತಿಥಿಯನ್ನು ಮರುಹೊಂದಿಸಿ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ಅತಿಥಿ"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"ಫೋಟೋ ತೆಗೆದುಕೊಳ್ಳಿ"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ಚಿತ್ರವನ್ನು ಆರಿಸಿ"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ಫೋಟೋ ಆಯ್ಕೆಮಾಡಿ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 6e6a5a7..82f4d1e 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"닉네임"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"게스트 추가"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"게스트 삭제"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"게스트 재설정"</string>
<string name="guest_nickname" msgid="6332276931583337261">"게스트"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"사진 찍기"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"이미지 선택"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"사진 선택"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 140eb3b..3fcedb0 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -507,7 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Ойготкучтар жана эстеткичтер"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Ойготкуч жана эстеткичтерди коюуга уруксат берүү"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Ойготкучтар жана эстеткичтер"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Бул колдонмого ойготкучтарды коюуга жана башка аракеттерди графикке киргизүүгө уруксат берүү. Ушуну менен колдонмо фондо иштеп, батареяны көбүрөөк сарпташы мүмкүн.\n\nЭгер бул уруксат өчүрүлсө, колдонмодогу ойготкучтар жана графикке киргизилген башка аракеттер иштебейт."</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Бул колдонмого ойготкучтарды коюуга жана башка аракеттерди графикке киргизүүгө уруксат бересиз. Ушуну менен колдонмо фондо иштеп, батареяны көбүрөөк сарпташы мүмкүн.\n\nЭгер бул уруксат өчүрүлсө, колдонмодогу ойготкучтар жана графикке киргизилген башка аракеттер иштебейт."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"график, ойготкуч, эстеткич, саат"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Күйгүзүү"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\"Тынчымды алба\" режимин күйгүзүү"</string>
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Ылакап аты"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Конок кошуу"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Конокту өчүрүү"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Конок сеансын баштапкы абалга келтирүү"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Конок"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Сүрөткө тартуу"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Сүрөт тандаңыз"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Сүрөт тандаңыз"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index f4962f9..748f399 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -507,8 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"ໂມງປຸກ ແລະ ການແຈ້ງເຕືອນ"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"ອະນຸຍາດໃຫ້ຕັ້ງໂມງປຸກ ແລະ ການແຈ້ງເຕືອນ"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ໂມງປຸກ ແລະ ການແຈ້ງເຕືອນ"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ອະນຸຍາດໃຫ້ແອັບນີ້ຕັ້ງໂມງປຸກ ແລະ ກຳນົດເວລາຄຳສັ່ງທີ່ເນັ້ນເລື່ອງເວລາເປັນສຳຄັນໄດ້. ນີ້ຈະເຮັດໃຫ້ແອັບເຮັດວຽກໄດ້ໃນພື້ນຫຼັງ, ເຊິ່ງອາດໃຊ້ແບັດເຕີຣີຫຼາຍຂຶ້ນ.\n\nຫາກປິດການອະນຸຍາດນີ້ໄວ້, ໂມງປຸກທີ່ມີຢູ່ກ່ອນແລ້ວ ແລະ ເຫດການທີ່ອ້າງອີງເວລາທີ່ກຳນົດໄວ້ໂດຍແອັບນີ້ຈະບໍ່ສາມາດເຮັດວຽກໄດ້."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ກຳນົດເວລາ, ໂມງປຸກ, ການແຈ້ງເຕືອນ, ໂມງ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ເປີດ"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"ເປີດໂໝດຫ້າມລົບກວນ"</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"ຊື່ຫຼິ້ນ"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"ເພີ່ມແຂກ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ລຶບແຂກອອກ"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"ຣີເຊັດແຂກ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ແຂກ"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"ຖ່າຍຮູບ"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ເລືອກຮູບ"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ເລືອກຮູບ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index e920b31..d7d5f24 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -568,7 +568,14 @@
<string name="user_nickname" msgid="262624187455825083">"Slapyvardis"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Pridėti svečią"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Pašalinti svečią"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Iš naujo nustatyti svečią"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Svečias"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Fotografuoti"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Pasirinkti vaizdą"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Pasirinkti nuotrauką"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index c43d879..8f9ac01 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -567,7 +567,14 @@
<string name="user_nickname" msgid="262624187455825083">"Segvārds"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Pievienot viesi"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Noņemt viesi"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Atiestatīt viesa sesiju"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Viesis"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Uzņemt fotoattēlu"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Izvēlēties attēlu"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Atlasīt fotoattēlu"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 81f75c5..4ac7dba 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Прекар"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Додајте гостин"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Отстрани гостин"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Ресетирајте го гостинот"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Гостин"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Фотографирајте"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Одберете слика"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Изберете фотографија"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 9583b50..134ac9b 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"വിളിപ്പേര്"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"അതിഥിയെ ചേർക്കുക"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"അതിഥിയെ നീക്കം ചെയ്യുക"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"അതിഥിയെ റീസെറ്റ് ചെയ്യുക"</string>
<string name="guest_nickname" msgid="6332276931583337261">"അതിഥി"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"ഒരു ഫോട്ടോ എടുക്കുക"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ഒരു ചിത്രം തിരഞ്ഞെടുക്കുക"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ഫോട്ടോ തിരഞ്ഞെടുക്കുക"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 955937b..2023e7c 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Хоч"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Зочин нэмэх"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Зочин хасах"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Зочныг шинэчлэх"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Зочин"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Зураг авах"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Зураг сонгох"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Зураг сонгох"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 8ddb298..5f622a1 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -507,8 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"अलार्म आणि रिमाइंडर"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"अलार्म आणि रिमाइंडर सेट करण्याची अनुमती द्या"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"अलार्म आणि रिमाइंडर"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"या ॲपला अलार्म सेट करण्याची किंवा वेळेनुसार संवेदनशील असलेल्या कृती शेड्युल करण्याची अनुमती द्या. हे ॲपला बॅकग्राउंडमध्ये रन होऊ देते, ज्यामुळे जास्त बॅटरी वापरली जाऊ शकते.\n\nही परवानगी बंद असल्यास, सध्याचे अलार्म आणि या ॲपद्वारे शेड्युल केलेले वेळेवर आधारित इव्हेंट काम करणार नाहीत."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"शेड्युल, अलार्म, रिमाइंडर, घड्याळ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"सुरू करा"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"व्यत्यय आणू नका सुरू करा"</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"टोपणनाव"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"अतिथी जोडा"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"अतिथी काढून टाका"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"अतिथी सेशन रीसेट करा"</string>
<string name="guest_nickname" msgid="6332276931583337261">"अतिथी"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"फोटो काढा"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"इमेज निवडा"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"फोटो निवडा"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 13788c4..34f6343 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Tambah tetamu"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Alih keluar tetamu"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Tetapkan semula tetamu"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Tetamu"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih imej"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Pilih foto"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index dac55c5..22415ad 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"နာမည်ပြောင်"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"ဧည့်သည့် ထည့်ရန်"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ဧည့်သည်ကို ဖယ်ထုတ်ရန်"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"ဧည့်သည်ကို ပြင်ဆင်သတ်မှတ်ရန်"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ဧည့်သည်"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"ဓာတ်ပုံရိုက်ရန်"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ပုံရွေးရန်"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ဓာတ်ပုံရွေးရန်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 769b468..ece7965 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Kallenavn"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Legg til en gjest"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gjesten"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Tilbakestill gjest"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gjest"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Ta et bilde"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Velg et bilde"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Velg et bilde"</string>
diff --git a/packages/SettingsLib/res/values-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml
index 1fc6899..3699bf3 100644
--- a/packages/SettingsLib/res/values-ne/arrays.xml
+++ b/packages/SettingsLib/res/values-ne/arrays.xml
@@ -260,7 +260,7 @@
<item msgid="6506681373060736204">"बढीमा ४ प्रक्रियाहरू"</item>
</string-array>
<string-array name="usb_configuration_titles">
- <item msgid="3358668781763928157">"चार्ज हुँदै"</item>
+ <item msgid="3358668781763928157">"चार्ज हुँदै छ"</item>
<item msgid="7804797564616858506">"MTP (मिडिया स्थानान्तरण प्रोटोकल)"</item>
<item msgid="910925519184248772">"PTP (चित्र स्थानान्तरण प्रोटोकल)"</item>
<item msgid="3825132913289380004">"RNDIS (USB इथरनेट)"</item>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 0bf1a78..e6fc707 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -456,8 +456,8 @@
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - पूरा चार्ज हुन <xliff:g id="TIME">%2$s</xliff:g> लाग्ने छ"</string>
<string name="power_charging_limited" msgid="7956120998372505295">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिङ केही समयका लागि सीमित पारिएको छ"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
- <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हुँदै"</string>
- <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"द्रुत गतिमा चार्ज गरिँदै"</string>
+ <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हुँदै छ"</string>
+ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"द्रुत गतिमा चार्ज गरिँदै छ"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"बिस्तारै चार्ज गरिँदै"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"वायरलेस तरिकाले चार्ज गरिँदै छ"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"चार्ज भइरहेको छैन"</string>
@@ -507,8 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"अलार्म र रिमाइन्डरहरू"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"अलार्म तथा रिमाइन्डर सेट गर्न दिइयोस्"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"घडी तथा रिमाइन्डरहरू"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"यो एपलाई अलार्म सेट गर्ने र समयमै पूरा गर्नु पर्ने कारबाहीहरूको रुटिन बनाउने अनुमति दिनुहोस्। यो अनुमति दिइएको छ भने यो एप ब्याकग्राउन्डमा चल्छ र धेरै ब्याट्री खपत हुन्छ।\n\nयो अनुमति दिइएको छैन भने सेट गरिएका अलार्म बज्दैनन् र यो एपले तय गरेका गतिविधि चल्दैनन्।"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"समयतालिका, अलार्म, रिमाइन्डर, घडी"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"सक्रिय गर्नुहोस्"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"बाधा नपुऱ्याउनुहोस् नामक मोडलाई सक्रिय गर्नुहोस्"</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"उपनाम"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"अतिथि थप्नुहोस्"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"अतिथि हटाउनुहोस्"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"अतिथि सत्र रिसेट गर्नुहोस्"</string>
<string name="guest_nickname" msgid="6332276931583337261">"अतिथि"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"फोटो खिच्नुहोस्"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"कुनै फोटो छनौट गर्नुहोस्"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"फोटो चयन गर्नुहोस्"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index ca4308c..c13e8a9 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Bijnaam"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Gast toevoegen"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Gast verwijderen"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Gastsessie resetten"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gast"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Foto maken"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Afbeelding kiezen"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Foto selecteren"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 21bd1ad..a211fd4 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -507,8 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"ଆଲାରାମ୍ ଏବଂ ରିମାଇଣ୍ଡରଗୁଡ଼ିକ"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"ଆଲାରାମ ଓ ରିମାଇଣ୍ଡରଗୁଡ଼ିକ ସେଟ କରିବାକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ଆଲାରାମ୍ ଏବଂ ରିମାଇଣ୍ଡରଗୁଡ଼ିକ"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ଏହି ଆପକୁ ଆଲାରାମ୍ ସେଟ୍ କରିବାକୁ ଏବଂ ସମୟ-ସମ୍ବେଦନଶୀଳ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ସିଡୁଲ୍ କରିବାକୁ ଅନୁମତି ଦିଅନ୍ତୁ। ଏହା ଆପକୁ ପୃଷ୍ଠପଟରେ ଚାଲିବାକୁ ଦେଇଥାଏ, ଯାହା ଅଧିକ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରିପାରେ।\n\nଯଦି ଏହି ଅନୁମତି ବନ୍ଦ ଅଛି, ତେବେ ଏହି ଆପ୍ ଦ୍ୱାରା ସିଡୁଲ୍ କରାଯାଇଥିବା ପୂର୍ବରୁ ଥିବା ଆଲାରାମ୍ ଏବଂ ସମୟ-ଆଧାରିତ ଇଭେଣ୍ଟଗୁଡ଼ିକ କାମ କରିବ ନାହିଁ।"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ସିଡୁଲ୍, ଆଲାରାମ୍, ରିମାଇଣ୍ଡର୍, ଘଣ୍ଟା"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ଚାଲୁ କରନ୍ତୁ"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ଅନ୍ କରନ୍ତୁ"</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"ଡାକନାମ"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"ଅତିଥି ଯୋଗ କରନ୍ତୁ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ଅତିଥିଙ୍କୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"ଅତିଥି ସେସନକୁ ରିସେଟ୍ କରନ୍ତୁ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ଅତିଥି"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"ଗୋଟିଏ ଫଟୋ ଉଠାନ୍ତୁ"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ଏକ ଛବି ବାଛନ୍ତୁ"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ଫଟୋ ବାଛନ୍ତୁ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index aaa892a..969e34d 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -461,7 +461,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ਬਿਨਾਂ ਤਾਰ ਤੋਂ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
- <string name="battery_info_status_not_charging" msgid="3371084153747234837">"ਕਨੈਕਟ ਹੈ, ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"ਕਨੈਕਟ ਹੈ, ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਹੀ"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"ਚਾਰਜ ਹੋ ਗਈ"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਕੰਟਰੋਲ ਕੀਤੀ ਗਈ"</string>
<string name="disabled" msgid="8017887509554714950">"ਅਯੋਗ ਬਣਾਇਆ"</string>
@@ -507,8 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"ਅਲਾਰਮ ਅਤੇ ਰਿਮਾਈਂਡਰ"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"ਅਲਾਰਮ ਅਤੇ ਰਿਮਾਈਂਡਰ ਸੈੱਟ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"ਅਲਾਰਮ ਅਤੇ ਰਿਮਾਈਂਡਰ"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ਇਸ ਐਪ ਨੂੰ ਅਲਾਰਮ ਸੈੱਟ ਕਰਨ ਜਾਂ ਹੋਰ ਸਮਾਂ-ਸੰਵੇਦਨਸ਼ੀਲ ਕਾਰਵਾਈਆਂ ਨੂੰ ਨਿਯਤ ਕਰਨ ਦਿਓ। ਇਸ ਨਾਲ ਐਪ ਨੂੰ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚਲਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਮਿਲਦੀ ਹੈ, ਜਿਸ ਨਾਲ ਬੈਟਰੀ ਦੀ ਵਰਤੋਂ ਵੱਧ ਸਕਦੀ ਹੈ।\n\nਜੇ ਇਹ ਇਜਾਜ਼ਤ ਬੰਦ ਹੈ, ਤਾਂ ਮੌਜੂਦਾ ਅਲਾਰਮ ਅਤੇ ਇਸ ਐਪ ਰਾਹੀਂ ਨਿਯਤ ਕੀਤੇ ਸਮਾਂ-ਆਧਾਰਿਤ ਇਵੈਂਟ ਕੰਮ ਨਹੀਂ ਕਰਨਗੇ।"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ਸਮਾਂ-ਸੂਚੀ, ਅਲਾਰਮ, ਰਿਮਾਈਂਡਰ, ਘੜੀ"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ਚਾਲੂ ਕਰੋ"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਨੂੰ ਚਾਲੂ ਕਰੋ"</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"ਉਪਨਾਮ"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"ਮਹਿਮਾਨ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"ਮਹਿਮਾਨ ਹਟਾਓ"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"ਗੈਸਟ ਰੀਸੈੱਟ ਕਰੋ"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ਮਹਿਮਾਨ"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"ਇੱਕ ਫ਼ੋਟੋ ਖਿੱਚੋ"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ਕੋਈ ਚਿੱਤਰ ਚੁਣੋ"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ਫ਼ੋਟੋ ਚੁਣੋ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 99faea7..9b25f63 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -568,7 +568,14 @@
<string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gościa"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Usuń gościa"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Resetuj sesję gościa"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gość"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Zrób zdjęcie"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Wybierz obraz"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Wybierz zdjęcie"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 021a75e..f8eab0ca 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Apelido"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Redefinir sessão de visitante"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index cdea8b2..4f8c6d2 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Alcunha"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Repor convidado"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 021a75e..f8eab0ca 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Apelido"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Redefinir sessão de visitante"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index e02bada..75bf3aa 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -567,7 +567,14 @@
<string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Adăugați un invitat"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ștergeți invitatul"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Resetați sesiunea pentru invitați"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Invitat"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Faceți o fotografie"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Alegeți o imagine"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Selectați fotografia"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 923917b..b416b06 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -559,7 +559,7 @@
<string name="user_new_user_name" msgid="60979820612818840">"Новый пользователь"</string>
<string name="user_new_profile_name" msgid="2405500423304678841">"Новый профиль"</string>
<string name="user_info_settings_title" msgid="6351390762733279907">"Сведения о пользователе"</string>
- <string name="profile_info_settings_title" msgid="105699672534365099">"Информация о профиле"</string>
+ <string name="profile_info_settings_title" msgid="105699672534365099">"Данные профиля"</string>
<string name="user_need_lock_message" msgid="4311424336209509301">"Чтобы создать профиль с ограниченным доступом, необходимо предварительно настроить блокировку экрана для защиты приложений и личных данных"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"Включить блокировку"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"Сменить пользователя на <xliff:g id="USER_NAME">%s</xliff:g>"</string>
@@ -568,7 +568,14 @@
<string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Добавить гостя"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Удалить аккаунт гостя"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Сбросить гостевой сеанс"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Гость"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Сделать снимок"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Выбрать фото"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Выбрать фотографию"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 3c79bf3..ca920a1 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"අපනාමය"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"අමුත්තා එක් කරන්න"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"අමුත්තා ඉවත් කරන්න"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"ආගන්තුකයා යළි සකසන්න"</string>
<string name="guest_nickname" msgid="6332276931583337261">"අමුත්තා"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"ඡායාරූපයක් ගන්න"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"රූපයක් තෝරන්න"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ඡායාරූපය තෝරන්න"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 5be0d99..9c68851 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -568,7 +568,14 @@
<string name="user_nickname" msgid="262624187455825083">"Prezývka"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Pridať hosťa"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Odobrať hosťa"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Obnoviť reláciu hosťa"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Hosť"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Odfotiť"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrať obrázok"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Vybrať fotku"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 07e367b..5cba5cb 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -568,7 +568,14 @@
<string name="user_nickname" msgid="262624187455825083">"Vzdevek"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Dodajanje gosta"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Odstranitev gosta"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Ponastavi gosta"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Fotografiranje"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Izberi sliko"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Izbira fotografije"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 1f30a02..d38c361 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -457,7 +457,7 @@
<string name="power_charging_limited" msgid="7956120998372505295">"<xliff:g id="LEVEL">%1$s</xliff:g> - Karikimi përkohësisht i kufizuar"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"I panjohur"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"Po karikohet"</string>
- <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Po ngarkon me shpejtësi"</string>
+ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Karikim i shpejtë"</string>
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Po karikohet ngadalë"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Po karikohet pa tel"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"Nuk po karikohet"</string>
@@ -507,8 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmet dhe alarmet rikujtuese"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Lejo caktimin e alarmeve dhe alarmeve rikujtuese"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmet dhe alarmet rikujtuese"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lejo që ky aplikacion të caktojë alarmet dhe të planifikojë veprime që kanë një afat të caktuar. Kjo lejon që aplikacioni të ekzekutohet në sfond, gjë që mund të përdorë më shumë bateri.\n\nNëse kjo leje është caktuar si joaktive, alarmet ekzistuese dhe ngjarjet me bazë kohore të planifikuara nga ky apliikacion nuk do të funksionojnë."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"planifiko, alarm, alarm rikujtues, ora"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aktivizo"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Aktivizo \"Mos shqetëso\""</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Pseudonimi"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Shto të ftuar"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Hiq të ftuarin"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Rivendos vizitorin"</string>
<string name="guest_nickname" msgid="6332276931583337261">"I ftuar"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Bëj një fotografi"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Zgjidh një imazh"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Zgjidh një fotografi"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 11adbe5..c6dbd00 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -567,7 +567,14 @@
<string name="user_nickname" msgid="262624187455825083">"Надимак"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Додај госта"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Уклони госта"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Ресетуј сесију госта"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Гост"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Сликај"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Одабери слику"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Изаберите слику"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index f4b92af..124c0e7 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Smeknamn"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Lägg till gäst"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ta bort gäst"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Återställ gästsession"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Gäst"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Ta ett foto"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Välj en bild"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Välj foto"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 2625a414..fdfd5ea 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Jina wakilishi"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Ongeza mgeni"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Ondoa mgeni"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Badilisha kipindi cha mgeni"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Mgeni"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Piga picha"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Chagua picha"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Chagua picha"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 612fd70..69a0945 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -505,8 +505,7 @@
<string name="cancel" msgid="5665114069455378395">"ரத்துசெய்"</string>
<string name="okay" msgid="949938843324579502">"சரி"</string>
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"அலாரங்களும் நினைவூட்டல்களும்"</string>
- <!-- unknown quoting pattern: original -1, translation 1 -->
- <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"அலாரங்கள் &amp; நினைவூட்டல்களை அமைக்க அனுமதித்தல்"</string>
+ <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"அலாரங்கள் & நினைவூட்டல்களை அமைக்க அனுமதித்தல்"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"அலாரங்கள் & நினைவூட்டல்கள்"</string>
<string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"அலாரங்களை அமைக்கவும் குறிப்பிட்ட கால இடைவெளியில் செயல்களைத் திட்டமிடவும் இந்த ஆப்ஸை அனுமதிக்கும். இது ஆப்ஸ் பின்னணியில் இயங்குவதை அனுமதிக்கும், இதற்காக அதிக பேட்டரியைப் பயன்படுத்தக்கூடும்.\n\nஇந்த அனுமதி முடக்கப்பட்டிருந்தால் இந்த ஆப்ஸ் மூலம் திட்டமிடப்பட்ட ஏற்கெனவே அமைத்த அலாரங்களும் நேர அடிப்படையிலான நிகழ்வுகளும் வேலை செய்யாது."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"திட்டமிடல், அலாரம், நினைவூட்டல், கடிகாரம்"</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"புனைப்பெயர்"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"கெஸ்ட்டைச் சேர்"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"கெஸ்ட்டை அகற்று"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"கெஸ்ட் அமர்வை மீட்டமை"</string>
<string name="guest_nickname" msgid="6332276931583337261">"கெஸ்ட்"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"படமெடுங்கள்"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"படத்தைத் தேர்வுசெய்யுங்கள்"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"படத்தைத் தேர்ந்தெடுங்கள்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 7f596e3..d8da8da 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -507,8 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"అలారాలు, రిమైండర్లు"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"అలారాలు, రిమైండర్లను సెట్ చేయడానికి అనుమతించండి"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"అలారాలు & రిమైండర్లు"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"అలారాలను సెట్ చేయడానికి, సమయ-సునిశిత చర్యలను షెడ్యూల్ చేయడానికి ఈ యాప్ను అనుమతించండి. ఇది యాప్ను బ్యాక్గ్రౌండ్లో రన్ అవడానికి అనుమతిస్తుంది, ఇది ఎక్కువ బ్యాటరీని ఉపయోగించవచ్చు.\n\nఈ అనుమతిని ఆఫ్ చేస్తే, ఈ యాప్ ద్వారా షెడ్యూల్ చేసిన ఇప్పటికే ఉన్న అలారాలు, సమయ-ఆధారిత ఈవెంట్లు పనిచేయవు."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"షెడ్యూల్, అలారం, రిమైండర్, గడియారం"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ఆన్ చేయండి"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"అంతరాయం కలిగించవద్దును ఆన్ చేయండి"</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"మారుపేరు"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"గెస్ట్ను జోడించండి"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"గెస్ట్ను తీసివేయండి"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"గెస్ట్ సెషన్ను రీసెట్ చేయండి"</string>
<string name="guest_nickname" msgid="6332276931583337261">"గెస్ట్"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"ఒక ఫోటో తీయండి"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ఇమేజ్ను ఎంచుకోండి"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"ఫోటోను ఎంచుకోండి"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 3851ccb..ca0e376 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -452,7 +452,7 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7703677921000858479">"แท็บเล็ตอาจปิดเครื่องในไม่ช้า (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="4374784375644214578">"อุปกรณ์อาจปิดเครื่องในไม่ช้า (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"อีก <xliff:g id="TIME">%1$s</xliff:g> จึงจะเต็ม"</string>
+ <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"อีก <xliff:g id="TIME">%1$s</xliff:g>จึงจะเต็ม"</string>
<string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - อีก <xliff:g id="TIME">%2$s</xliff:g> จึงจะเต็ม"</string>
<string name="power_charging_limited" msgid="7956120998372505295">"<xliff:g id="LEVEL">%1$s</xliff:g> - จำกัดการชาร์จชั่วคราว"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"ไม่ทราบ"</string>
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"ชื่อเล่น"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"เพิ่มผู้ใช้ชั่วคราว"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"นำผู้ใช้ชั่วคราวออก"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"รีเซ็ตผู้เข้าร่วม"</string>
<string name="guest_nickname" msgid="6332276931583337261">"ผู้ใช้ชั่วคราว"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"ถ่ายรูป"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"เลือกรูปภาพ"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"เลือกรูปภาพ"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index a381c35..1c5092f 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Magdagdag ng bisita"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Alisin ang bisita"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"I-reset ang bisita"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Bisita"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Kumuha ng larawan"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Pumili ng larawan"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Pumili ng larawan"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 03efe82..42e7c5e 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -507,7 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmlar ve hatırlatıcılar"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Alarm ve hatırlatıcı ayarlanmasına izin ver"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmlar ve hatırlatıcılar"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Bu uygulamanın alarmlar kurmasına ve zamana bağlı işlemler programlamasına izin verin. Bu izin, uygulamanın arka planda çalışmasına olanak sağlayarak daha fazla pil harcanmasına neden olabilir.\n\nBu izin verilmezse bu uygulama tarafından programlanmış mevcut alarmlar ve zamana bağlı etkinlikler çalışmaz."</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Bu uygulamanın alarm kurmasına ve zamana bağlı işlemler programlamasına izin verin. Bu izin, uygulamanın arka planda çalışmasına olanak sağlayarak daha fazla pil harcanmasına neden olabilir.\n\nBu izin verilmezse bu uygulama tarafından programlanmış mevcut alarmlar ve zamana bağlı etkinlikler çalışmaz."</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"program, alarm, hatırlatıcı, saat"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Aç"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Rahatsız Etmeyin\'i açın"</string>
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Takma ad"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Misafir ekle"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Misafir oturumunu kaldır"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Misafir oturumunu sıfırla"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Misafir"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Fotoğraf çek"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Resim seç"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Fotoğraf seç"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index e737c7d..ba7a678 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -568,7 +568,14 @@
<string name="user_nickname" msgid="262624187455825083">"Псевдонім"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Додати гостя"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Видалити гостя"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Скинути сеанс у режимі \"Гість\""</string>
<string name="guest_nickname" msgid="6332276931583337261">"Гість"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Зробити фотографію"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Вибрати зображення"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Вибрати фотографію"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 45e9f4f..d0cc6de 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -507,8 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"الارمز اور یاد دہانیاں"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"الارمز اور یاد دہانیاں سیٹ کرنے کی اجازت دیں"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"الارمز اور یاد دہانیاں"</string>
- <!-- no translation found for alarms_and_reminders_footer_title (6302587438389079695) -->
- <skip />
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"اس ایپ کو الارمز سیٹ کرنے اور متعین وقت کے لحاظ سے حساس کارروائیوں کو شیڈول کرنے کی اجازت دیں۔ یہ ایپ کو پس منظر میں چلنے دیتا ہے، جس میں زیادہ بیٹری استعمال ہو سکتی ہے۔\n\n اگر یہ اجازت آف ہے تو موجودہ الارمز اور اس ایپ کے ذریعے شیڈول کردہ وقت پر مبنی ایونٹس کام نہیں کریں گے۔"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"شیڈول، الارم، یاد دہانی، گھڑی"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"آن کریں"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'ڈسٹرب نہ کریں\' کو آن کریں"</string>
@@ -567,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"عرفی نام"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"مہمان کو شامل کریں"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"مہمان کو ہٹائیں"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"مہمان کو ری سیٹ کریں"</string>
<string name="guest_nickname" msgid="6332276931583337261">"مہمان"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"ایک تصویر لیں"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"ایک تصویر منتخب کریں"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"تصویر منتخب کریں"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 96b8853..c433c95 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Nik"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Mehmon kiritish"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Mehmonni olib tashlash"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Mehmon seansini tiklash"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Mehmon"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Suratga olish"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Rasm tanlash"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Surat tanlash"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 4509430..f200467 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Biệt hiệu"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Thêm khách"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Xóa phiên khách"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Đặt lại phiên khách"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Khách"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Chụp ảnh"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Chọn một hình ảnh"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Chọn ảnh"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 3e6b3c2..9e7c410 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -429,10 +429,10 @@
<string name="accessibility_display_daltonizer_preference_subtitle" msgid="2333641630205214702">"调整设备上的颜色显示方式。此设置对以下情况有帮助:<br/><br/> <ol> <li>&nbsp;您想更准确地看颜色</li> <li>&nbsp;您想去除一些颜色,以便集中注意力</li> </ol>"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"已被“<xliff:g id="TITLE">%1$s</xliff:g>”覆盖"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
- <string name="power_remaining_duration_only" msgid="8264199158671531431">"大约还可使用 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
- <string name="power_discharging_duration" msgid="1076561255466053220">"大约还可使用 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
- <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"根据您的使用情况,大约还可使用 <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
- <string name="power_discharging_duration_enhanced" msgid="1800465736237672323">"根据您的使用情况,大约还可使用 <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_duration_only" msgid="8264199158671531431">"大约还可使用<xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration" msgid="1076561255466053220">"大约还可使用<xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+ <string name="power_remaining_duration_only_enhanced" msgid="2527842780666073218">"根据您的使用情况,大约还可使用<xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
+ <string name="power_discharging_duration_enhanced" msgid="1800465736237672323">"根据您的使用情况,大约还可使用<xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<!-- no translation found for power_remaining_duration_only_short (7438846066602840588) -->
<skip />
<string name="power_discharge_by_enhanced" msgid="563438403581662942">"根据您的使用情况,估计能用到<xliff:g id="TIME">%1$s</xliff:g>(目前电量为 <xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
@@ -452,8 +452,8 @@
<string name="power_remaining_duration_shutdown_imminent" product="tablet" msgid="7703677921000858479">"平板电脑可能即将关机 (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_remaining_duration_shutdown_imminent" product="device" msgid="4374784375644214578">"设备可能即将关机 (<xliff:g id="LEVEL">%1$s</xliff:g>)"</string>
<string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
- <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"还需 <xliff:g id="TIME">%1$s</xliff:g>充满"</string>
- <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需 <xliff:g id="TIME">%2$s</xliff:g>充满"</string>
+ <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"还需<xliff:g id="TIME">%1$s</xliff:g>充满"</string>
+ <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满"</string>
<string name="power_charging_limited" msgid="7956120998372505295">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充电暂时受限"</string>
<string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string>
<string name="battery_info_status_charging" msgid="4279958015430387405">"正在充电"</string>
@@ -461,7 +461,7 @@
<string name="battery_info_status_charging_slow" msgid="3190803837168962319">"正在慢速充电"</string>
<string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"正在无线充电"</string>
<string name="battery_info_status_discharging" msgid="6962689305413556485">"未在充电"</string>
- <string name="battery_info_status_not_charging" msgid="3371084153747234837">"已连接,尚未充电"</string>
+ <string name="battery_info_status_not_charging" msgid="3371084153747234837">"已连接,未充电"</string>
<string name="battery_info_status_full" msgid="1339002294876531312">"已充满电"</string>
<string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"由管理员控制"</string>
<string name="disabled" msgid="8017887509554714950">"已停用"</string>
@@ -507,7 +507,7 @@
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"闹钟和提醒"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"允许设置闹钟和提醒"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"闹钟和提醒"</string>
- <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"允许该应用设置闹钟以及安排在特定时间执行某些操作。如此一来,该应用将在后台运行,而这可能会消耗更多电池电量。\n\n如果您关闭了此权限,该应用设置的现有闹钟将不会响起,而且该应用安排在特定时间执行的现有活动也不会执行。"</string>
+ <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"允许该应用设置闹钟以及安排在特定时间执行某些操作。这项权限开启后,该应用将在后台运行,可能会消耗更多电池电量。\n\n如果您关闭此权限,该应用设置的现有闹钟将不会响起,而且该应用安排在特定时间执行的现有活动也不会执行。"</string>
<string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"设置, 闹钟, 提醒, 时钟, schedule, alarm, reminder, clock"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"开启"</string>
<string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"开启勿扰模式"</string>
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"昵称"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"添加访客"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"移除访客"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"重置访客会话"</string>
<string name="guest_nickname" msgid="6332276931583337261">"访客"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"拍摄照片"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"选择图片"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"选择照片"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 42d95b7..7c10c2c 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"暱稱"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"重設訪客"</string>
<string name="guest_nickname" msgid="6332276931583337261">"訪客"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"揀相"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 20fa187..583bf18 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"暱稱"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"重設訪客"</string>
<string name="guest_nickname" msgid="6332276931583337261">"訪客"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"選取相片"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index f49d3cc9..8d1feda 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -566,7 +566,14 @@
<string name="user_nickname" msgid="262624187455825083">"Isiteketiso"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Engeza isivakashi"</string>
<string name="guest_exit_guest" msgid="5908239569510734136">"Susa isihambeli"</string>
+ <string name="guest_reset_guest" msgid="6110013010356013758">"Setha kabusha isivakashi"</string>
<string name="guest_nickname" msgid="6332276931583337261">"Isihambeli"</string>
+ <!-- no translation found for guest_reset_guest_dialog_title (8047270010895437534) -->
+ <skip />
+ <!-- no translation found for guest_reset_guest_confirm_button (2989915693215617237) -->
+ <skip />
+ <!-- no translation found for guest_resetting (7822120170191509566) -->
+ <skip />
<string name="user_image_take_photo" msgid="467512954561638530">"Thatha isithombe"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Khetha isithombe"</string>
<string name="user_image_photo_selector" msgid="433658323306627093">"Khetha isithombe"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 603d093..6b840bd 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1422,9 +1422,16 @@
<string name="guest_new_guest">Add guest</string>
<!-- Label for exiting and removing the guest session in the user switcher [CHAR LIMIT=35] -->
<string name="guest_exit_guest">Remove guest</string>
+ <!-- Label for resetting guest session in the user switcher, which will remove all data from the current guest session [CHAR LIMIT=35] -->
+ <string name="guest_reset_guest">Reset guest</string>
<!-- Name for the guest user [CHAR LIMIT=35] -->
<string name="guest_nickname">Guest</string>
-
+ <!-- Title of the confirmation dialog to confirm resetting guest. [CHAR LIMIT=NONE] -->
+ <string name="guest_reset_guest_dialog_title">Reset guest?</string>
+ <!-- Label for button in confirmation dialog when resetting guest user [CHAR LIMIT=35] -->
+ <string name="guest_reset_guest_confirm_button">Reset</string>
+ <!-- Status message indicating the device is in the process of resetting the guest user. [CHAR_LIMIT=NONE] -->
+ <string name="guest_resetting">Resetting guest\u2026</string>
<!-- An option in a photo selection dialog to take a new photo [CHAR LIMIT=50] -->
<string name="user_image_take_photo">Take a photo</string>
<!-- An option in a photo selection dialog to choose a pre-existing image [CHAR LIMIT=50] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java
index a537394..0b3a519 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminController.java
@@ -16,8 +16,10 @@
package com.android.settingslib.enterprise;
+import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
+import android.content.DialogInterface;
import androidx.annotation.Nullable;
@@ -54,4 +56,13 @@
* Updates the enforced admin
*/
void updateEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin, @UserIdInt int adminUserId);
+
+ /**
+ * Returns a listener for handling positive button clicks
+ */
+ @Nullable
+ default DialogInterface.OnClickListener getPositiveButtonListener(@NonNull Context context,
+ @NonNull RestrictedLockUtils.EnforcedAdmin enforcedAdmin) {
+ return null;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
index da42e33..44cafb1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
@@ -20,6 +20,11 @@
import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.ParentalControlsUtilsInternal;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
/**
* A factory that returns the relevant instance of {@link ActionDisabledByAdminController}.
@@ -30,10 +35,28 @@
* Returns the relevant instance of {@link ActionDisabledByAdminController}.
*/
public static ActionDisabledByAdminController createInstance(Context context,
- DeviceAdminStringProvider stringProvider) {
- return isFinancedDevice(context)
- ? new FinancedDeviceActionDisabledByAdminController(stringProvider)
- : new ManagedDeviceActionDisabledByAdminController(stringProvider);
+ String restriction, DeviceAdminStringProvider stringProvider) {
+ if (doesBiometricRequireParentalConsent(context, restriction)) {
+ return new BiometricActionDisabledByAdminController(stringProvider);
+ } else if (isFinancedDevice(context)) {
+ return new FinancedDeviceActionDisabledByAdminController(stringProvider);
+ } else {
+ return new ManagedDeviceActionDisabledByAdminController(stringProvider);
+ }
+ }
+
+ /**
+ * @return true if the restriction == UserManager.DISALLOW_BIOMETRIC and parental consent
+ * is required.
+ */
+ private static boolean doesBiometricRequireParentalConsent(Context context,
+ String restriction) {
+ if (!TextUtils.equals(UserManager.DISALLOW_BIOMETRIC, restriction)) {
+ return false;
+ }
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ return ParentalControlsUtilsInternal.parentConsentRequired(context, dpm,
+ BiometricAuthenticator.TYPE_ANY_BIOMETRIC, new UserHandle(UserHandle.myUserId()));
}
private static boolean isFinancedDevice(Context context) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
new file mode 100644
index 0000000..814d5d2
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminController.java
@@ -0,0 +1,71 @@
+/*
+ * 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.settingslib.enterprise;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.android.settingslib.RestrictedLockUtils;
+
+public class BiometricActionDisabledByAdminController extends BaseActionDisabledByAdminController {
+
+ private static final String TAG = "BiometricActionDisabledByAdminController";
+
+ // These MUST not change, as they are the stable API between here and device admin specified
+ // by the component below.
+ private static final String ACTION_LEARN_MORE = "android.settings.LEARN_MORE";
+ private static final String EXTRA_FROM_BIOMETRIC_SETUP = "from_biometric_setup";
+
+ BiometricActionDisabledByAdminController(
+ DeviceAdminStringProvider stringProvider) {
+ super(stringProvider);
+ }
+
+ @Override
+ public void setupLearnMoreButton(Context context) {
+
+ }
+
+ @Override
+ public String getAdminSupportTitle(@Nullable String restriction) {
+ return mStringProvider.getDisabledBiometricsParentConsentTitle();
+ }
+
+ @Override
+ public CharSequence getAdminSupportContentString(Context context,
+ @Nullable CharSequence supportMessage) {
+ return mStringProvider.getDisabledBiometricsParentConsentContent();
+ }
+
+ @Override
+ public DialogInterface.OnClickListener getPositiveButtonListener(@NonNull Context context,
+ @NonNull RestrictedLockUtils.EnforcedAdmin enforcedAdmin) {
+ return (dialog, which) -> {
+ Log.d(TAG, "Positive button clicked, component: " + enforcedAdmin.component);
+ final Intent intent = new Intent(ACTION_LEARN_MORE)
+ .setComponent(enforcedAdmin.component)
+ .putExtra(EXTRA_FROM_BIOMETRIC_SETUP, true)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ };
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/DeviceAdminStringProvider.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/DeviceAdminStringProvider.java
index c47d789..b83837e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/DeviceAdminStringProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/DeviceAdminStringProvider.java
@@ -72,4 +72,14 @@
* a financed device.
*/
String getDisabledByPolicyTitleForFinancedDevice();
+
+ /**
+ * Returns the dialog title for when biometrics require parental consent.
+ */
+ String getDisabledBiometricsParentConsentTitle();
+
+ /**
+ * Returns the dialog contents for when biometrics require parental consent.
+ */
+ String getDisabledBiometricsParentConsentContent();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java b/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
index 549bc8a..ebdfbea 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
@@ -33,6 +33,7 @@
import com.android.launcher3.icons.BaseIconFactory;
import com.android.settingslib.R;
+import com.android.settingslib.Utils;
/**
* Factory for creating normalized conversation icons.
@@ -99,7 +100,7 @@
try {
final ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser(
packageName, PackageManager.GET_META_DATA, userId);
- badge = mIconDrawableFactory.getBadgedIcon(appInfo, userId);
+ badge = Utils.getBadgedIcon(mContext, appInfo);
} catch (PackageManager.NameNotFoundException e) {
badge = mPackageManager.getDefaultActivityIcon();
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
index b0c5314..53a382a 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/SettingsSpinnerPreferenceTest.java
@@ -89,24 +89,4 @@
assertThat(mSpinnerPreference.getSelectedItem())
.isEqualTo(mSpinner.getAdapter().getItem(1));
}
-
- @Test
- public void onBindViewHolder_setClickableTrue_isClickableTrue() {
- mSpinnerPreference.setClickable(true);
-
- mSpinnerPreference.onBindViewHolder(mViewHolder);
-
- assertThat(mSpinner.isClickable()).isTrue();
- assertThat(mSpinner.isEnabled()).isTrue();
- }
-
- @Test
- public void onBindViewHolder_setClickableFalse_isClickableFalse() {
- mSpinnerPreference.setClickable(false);
-
- mSpinnerPreference.onBindViewHolder(mViewHolder);
-
- assertThat(mSpinner.isClickable()).isFalse();
- assertThat(mSpinner.isEnabled()).isFalse();
- }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
new file mode 100644
index 0000000..766c2f5
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.settingslib.enterprise;
+
+import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCED_ADMIN;
+import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCEMENT_ADMIN_USER_ID;
+import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.DEFAULT_DEVICE_ADMIN_STRING_PROVIDER;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.UserHandle;
+
+import com.android.settingslib.RestrictedLockUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class BiometricActionDisabledByAdminControllerTest {
+
+ private final ActionDisabledByAdminControllerTestUtils mTestUtils =
+ new ActionDisabledByAdminControllerTestUtils();
+ private final BiometricActionDisabledByAdminController mController =
+ new BiometricActionDisabledByAdminController(DEFAULT_DEVICE_ADMIN_STRING_PROVIDER);
+
+ @Before
+ public void setUp() {
+ mController.initialize(mTestUtils.createLearnMoreButtonLauncher());
+ mController.updateEnforcedAdmin(ENFORCED_ADMIN, ENFORCEMENT_ADMIN_USER_ID);
+ }
+
+ @Test
+ public void buttonClicked() {
+ Context context = mock(Context.class);
+ ComponentName componentName = mock(ComponentName.class);
+ RestrictedLockUtils.EnforcedAdmin enforcedAdmin = new RestrictedLockUtils.EnforcedAdmin(
+ componentName, new UserHandle(UserHandle.myUserId()));
+
+ DialogInterface.OnClickListener listener =
+ mController.getPositiveButtonListener(context, enforcedAdmin);
+ assertNotNull("Biometric Controller must supply a non-null listener", listener);
+ listener.onClick(mock(DialogInterface.class), 0 /* which */);
+
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(context).startActivity(intentCaptor.capture());
+ assertEquals("android.settings.LEARN_MORE",
+ intentCaptor.getValue().getAction());
+ assertTrue("from_biometric_setup", intentCaptor.getValue()
+ .getBooleanExtra("from_biometric_setup", false));
+ assertEquals(componentName, intentCaptor.getValue().getComponent());
+ }
+
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java
index be3e9fc..99e13c3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/FakeDeviceAdminStringProvider.java
@@ -30,6 +30,8 @@
static final String DEFAULT_DISABLED_BY_POLICY_CONTENT = "default_disabled_by_policy_content";
static final String DEFAULT_DISABLED_BY_POLICY_TITLE_FINANCED_DEVICE =
"default_disabled_by_policy_title_financed_device";
+ static final String DEFAULT_BIOMETRIC_TITLE = "biometric_title";
+ static final String DEFAULT_BIOMETRIC_CONTENTS = "biometric_contents";
static final DeviceAdminStringProvider DEFAULT_DEVICE_ADMIN_STRING_PROVIDER =
new FakeDeviceAdminStringProvider(/* url = */ null);
@@ -88,4 +90,15 @@
public String getDisabledByPolicyTitleForFinancedDevice() {
return DEFAULT_DISABLED_BY_POLICY_TITLE_FINANCED_DEVICE;
}
+
+ @Override
+ public String getDisabledBiometricsParentConsentTitle() {
+ return DEFAULT_BIOMETRIC_TITLE;
+ }
+
+ @Override
+ public String getDisabledBiometricsParentConsentContent() {
+ return DEFAULT_BIOMETRIC_CONTENTS;
+ }
+
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java
index 6670ed3..0a48f19 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java
@@ -20,7 +20,10 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.robolectric.Robolectric.setupActivity;
+import static org.robolectric.Shadows.shadowOf;
+import android.app.Activity;
import android.content.Context;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
@@ -44,8 +47,8 @@
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
import org.robolectric.shadows.ShadowDrawable;
+import org.robolectric.shadows.ShadowTouchDelegate;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
@@ -58,6 +61,9 @@
private boolean mClickListenerCalled = false;
private final View.OnClickListener mClickListener = v -> mClickListenerCalled = true;
+ private final int mMinimumTargetSize =
+ RuntimeEnvironment.application.getResources()
+ .getDimensionPixelSize(R.dimen.settingslib_preferred_minimum_touch_target);
private static final int TEST_STRING_RES_ID =
R.string.accessibility_banner_message_dismiss;
@@ -81,6 +87,23 @@
}
@Test
+ public void onBindViewHolder_andOnLayoutView_dismissButtonTouchDelegate_isCorrectSize() {
+ assumeAndroidS();
+ mBannerPreference.setTitle("Title");
+ mBannerPreference.setDismissButtonOnClickListener(mClickListener);
+
+ mBannerPreference.onBindViewHolder(mHolder);
+ setupActivity(Activity.class).setContentView(mRootView);
+
+ assertThat(mRootView.getTouchDelegate()).isNotNull();
+ ShadowTouchDelegate delegate = shadowOf(mRootView.getTouchDelegate());
+ assertThat(delegate.getBounds().width()).isAtLeast(mMinimumTargetSize);
+ assertThat(delegate.getBounds().height()).isAtLeast(mMinimumTargetSize);
+ assertThat(delegate.getDelegateView())
+ .isEqualTo(mRootView.findViewById(R.id.banner_dismiss_btn));
+ }
+
+ @Test
public void onBindViewHolder_whenSummarySet_shouldSetSummary() {
mBannerPreference.setSummary("test");
@@ -157,7 +180,7 @@
mBannerPreference.onBindViewHolder(mHolder);
ImageView mIcon = mRootView.findViewById(R.id.banner_icon);
- ShadowDrawable shadowDrawable = Shadows.shadowOf(mIcon.getDrawable());
+ ShadowDrawable shadowDrawable = shadowOf(mIcon.getDrawable());
assertThat(shadowDrawable.getCreatedFromResId())
.isEqualTo(R.drawable.settingslib_ic_cross);
}
@@ -168,7 +191,7 @@
mBannerPreference.onBindViewHolder(mHolder);
ImageView mIcon = mRootView.findViewById(R.id.banner_icon);
- ShadowDrawable shadowDrawable = Shadows.shadowOf(mIcon.getDrawable());
+ ShadowDrawable shadowDrawable = shadowOf(mIcon.getDrawable());
assertThat(shadowDrawable.getCreatedFromResId()).isEqualTo(R.drawable.ic_warning);
}
@@ -478,11 +501,15 @@
private void assumeAndroidR() {
ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 30);
+ ReflectionHelpers.setStaticField(Build.VERSION.class, "CODENAME", "R");
+ ReflectionHelpers.setStaticField(BannerMessagePreference.class, "IS_AT_LEAST_S", false);
// Reset view holder to use correct layout.
}
private void assumeAndroidS() {
ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 31);
+ ReflectionHelpers.setStaticField(Build.VERSION.class, "CODENAME", "S");
+ ReflectionHelpers.setStaticField(BannerMessagePreference.class, "IS_AT_LEAST_S", true);
// Re-inflate view to update layout.
setUpViewHolder();
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 959b5ca..491f0d9 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -419,6 +419,9 @@
<!-- Permission required for running networking unit tests -->
<uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" />
+ <!-- Permission required for CTS test - CtsHostsideNetworkTests -->
+ <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
+
<!-- Permissions required for CTS test - TunerTest -->
<uses-permission android:name="android.permission.ACCESS_TV_DESCRAMBLER" />
<uses-permission android:name="android.permission.ACCESS_TV_TUNER" />
@@ -433,6 +436,8 @@
<!-- Permissions required for CTS test - TVInputManagerTest -->
<uses-permission android:name="android.permission.ACCESS_TUNED_INFO" />
<uses-permission android:name="android.permission.TV_INPUT_HARDWARE" />
+ <uses-permission android:name="com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS" />
+ <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA"/>
<!-- Permission needed for CTS test - PrivilegedLocationPermissionTest -->
<uses-permission android:name="android.permission.LOCATION_HARDWARE" />
diff --git a/packages/Shell/res/values-az/strings.xml b/packages/Shell/res/values-az/strings.xml
index 15853c2..23a1ad7 100644
--- a/packages/Shell/res/values-az/strings.xml
+++ b/packages/Shell/res/values-az/strings.xml
@@ -35,7 +35,7 @@
<string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Zip faylı üçün baq hesabat detalları əlavə edilmədi"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"adsız"</string>
<string name="bugreport_info_action" msgid="2158204228510576227">"Detallar"</string>
- <string name="bugreport_screenshot_action" msgid="8677781721940614995">"displey görüntüsü"</string>
+ <string name="bugreport_screenshot_action" msgid="8677781721940614995">"Skrinşot"</string>
<string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Displey görüntüsü uğurla çəkildi."</string>
<string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Displey görüntüsü əlçatan deyil."</string>
<string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Baq hesabatı <xliff:g id="ID">#%d</xliff:g> detalları"</string>
diff --git a/packages/Shell/res/values-iw/strings.xml b/packages/Shell/res/values-iw/strings.xml
index b975521..816fe3b 100644
--- a/packages/Shell/res/values-iw/strings.xml
+++ b/packages/Shell/res/values-iw/strings.xml
@@ -29,7 +29,7 @@
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"יש להקיש כדי לשתף את הדוח על הבאג ללא צילום מסך, או להמתין להשלמת צילום המסך"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"יש להקיש כדי לשתף את הדוח על הבאג ללא צילום מסך, או להמתין להשלמת צילום המסך"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"דוחות על באגים כוללים נתונים מקובצי היומן השונים במערכת, שעשויים לכלול נתונים הנחשבים רגישים (כגון שימוש באפליקציות ונתוני מיקום). כדאי לשתף דוחות על באגים רק עם אפליקציות ואנשים מהימנים."</string>
- <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"אל תציגו זאת שוב"</string>
+ <string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"אין צורך להציג זאת שוב"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"דוחות על באגים"</string>
<string name="bugreport_unreadable_text" msgid="586517851044535486">"לא ניתן היה לקרוא את קובץ הדוח על הבאג"</string>
<string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"לא ניתן היה להוסיף את פרטי הדוח על הבאג לקובץ ה-zip"</string>
diff --git a/packages/Shell/res/values-mr/strings.xml b/packages/Shell/res/values-mr/strings.xml
index a957184..89b49a2 100644
--- a/packages/Shell/res/values-mr/strings.xml
+++ b/packages/Shell/res/values-mr/strings.xml
@@ -18,30 +18,30 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3701846017049540910">"शेल"</string>
<string name="bugreport_notification_channel" msgid="2574150205913861141">"बग रीपोर्ट"</string>
- <string name="bugreport_in_progress_title" msgid="4311705936714972757">"बग रीपोर्ट <xliff:g id="ID">#%d</xliff:g> तयार केला जात आहे"</string>
- <string name="bugreport_finished_title" msgid="4429132808670114081">"बग रीपोर्ट <xliff:g id="ID">#%d</xliff:g> कॅप्चर केला"</string>
- <string name="bugreport_updating_title" msgid="4423539949559634214">"दोष अहवालामध्ये तपशील जोडत आहे"</string>
+ <string name="bugreport_in_progress_title" msgid="4311705936714972757">"बग रिपोर्ट <xliff:g id="ID">#%d</xliff:g> तयार केला जात आहे"</string>
+ <string name="bugreport_finished_title" msgid="4429132808670114081">"बग रिपोर्ट <xliff:g id="ID">#%d</xliff:g> कॅप्चर केला"</string>
+ <string name="bugreport_updating_title" msgid="4423539949559634214">"बग रिपोर्टमध्ये तपशील जोडत आहे"</string>
<string name="bugreport_updating_wait" msgid="3322151947853929470">"कृपया प्रतीक्षा करा..."</string>
- <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"फोनवर बग रीपोर्ट लवकरच दिसेल"</string>
+ <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"फोनवर बग रिपोर्ट लवकरच दिसेल"</string>
<string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"तुमचा बग रीपोर्ट शेअर करण्यासाठी निवडा"</string>
- <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"तुमचा बग रीपोर्ट शेअर करण्यासाठी टॅप करा"</string>
+ <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"तुमचा बग रिपोर्ट शेअर करण्यासाठी टॅप करा"</string>
<string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"तुमचा बग रीपोर्ट स्क्रीनशॉटशिवाय शेअर करण्यासाठी टॅप करा किंवा स्क्रीनशॉट पूर्ण होण्याची प्रतीक्षा करा"</string>
<string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"स्क्रीनशॉट शिवाय तुमचा बग रीपोर्ट शेअर करण्यासाठी टॅप करा किंवा समाप्त करण्यासाठी स्क्रीनशॉटची प्रतीक्षा करा"</string>
<string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"स्क्रीनशॉट शिवाय तुमचा बग रीपोर्ट शेअर करण्यासाठी टॅप करा किंवा समाप्त करण्यासाठी स्क्रीनशॉटची प्रतीक्षा करा"</string>
<string name="bugreport_confirm" msgid="5917407234515812495">"बग रीपोर्टांमध्ये तुम्ही संवेदनशील (अॅप-वापर आणि स्थान डेटा यासारखा) डेटा म्हणून विचार करता त्या डेटाच्या समावेशासह सिस्टीमच्या विविध लॉग फायलींमधील डेटा असतो. ज्या लोकांवर आणि अॅपवर तुमचा विश्वास आहे केवळ त्यांच्यासह हा बग रीपोर्ट शेअर करा."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"पुन्हा दर्शवू नका"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"बग रीपोर्ट"</string>
- <string name="bugreport_unreadable_text" msgid="586517851044535486">"बग रीपोर्ट फाइल वाचणे शक्य झाले नाही"</string>
- <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"झिप फाइल मध्ये बग रीपोर्ट तपशील जोडणे शक्य झाले नाही"</string>
+ <string name="bugreport_unreadable_text" msgid="586517851044535486">"बग रिपोर्ट फाइल वाचणे शक्य झाले नाही"</string>
+ <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"झिप फाइल मध्ये बग रिपोर्ट तपशील जोडणे शक्य झाले नाही"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"अनामित"</string>
<string name="bugreport_info_action" msgid="2158204228510576227">"तपशील"</string>
<string name="bugreport_screenshot_action" msgid="8677781721940614995">"स्क्रीनशॉट"</string>
<string name="bugreport_screenshot_taken" msgid="5684211273096253120">"स्क्रीनशॉट यशस्वीरित्या घेतला."</string>
<string name="bugreport_screenshot_failed" msgid="5853049140806834601">"स्क्रीनशॉट घेणे शक्य झाले नाही."</string>
- <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"बग रीपोर्ट <xliff:g id="ID">#%d</xliff:g> तपशील"</string>
+ <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"बग रिपोर्ट <xliff:g id="ID">#%d</xliff:g> तपशील"</string>
<string name="bugreport_info_name" msgid="4414036021935139527">"फाईलनाव"</string>
<string name="bugreport_info_title" msgid="2306030793918239804">"दोष शीर्षक"</string>
<string name="bugreport_info_description" msgid="5072835127481627722">"दोष सारांश"</string>
<string name="save" msgid="4781509040564835759">"सेव्ह करा"</string>
- <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"बग रीपोर्ट शेअर करा"</string>
+ <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"बग रिपोर्ट शेअर करा"</string>
</resources>
diff --git a/packages/StatementService/Android.bp b/packages/StatementService/Android.bp
index a0d8ac9..ff1a756 100644
--- a/packages/StatementService/Android.bp
+++ b/packages/StatementService/Android.bp
@@ -22,8 +22,7 @@
android_app {
name: "StatementService",
- // Removed because Errorprone doesn't work with Kotlin, can fix up in the future
- // defaults: ["platform_app_defaults"],
+ defaults: ["platform_app_defaults"],
srcs: [
"src/**/*.java",
"src/**/*.kt",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 8c092ae..604310a 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -454,6 +454,14 @@
android:finishOnCloseSystemDialogs="true">
</activity>
+ <!-- started from SensoryPrivacyService -->
+ <activity android:name=".sensorprivacy.television.TvUnblockSensorActivity"
+ android:exported="true"
+ android:permission="android.permission.MANAGE_SENSOR_PRIVACY"
+ android:theme="@style/BottomSheet"
+ android:finishOnCloseSystemDialogs="true">
+ </activity>
+
<!-- started from UsbDeviceSettingsManager -->
<activity android:name=".usb.UsbAccessoryUriActivity"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 8bc3d22..ac9298d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -159,7 +159,6 @@
// If we expect an animation, post a timeout to cancel it in case the remote animation is
// never started.
if (willAnimate) {
- keyguardHandler.disableKeyguardBlurs()
runner.postTimeout()
// Hide the keyguard using the launch animation instead of the default unlock animation.
@@ -220,8 +219,8 @@
/** Hide the keyguard and animate using [runner]. */
fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner)
- /** Disable window blur so they don't overlap with the window launch animation **/
- fun disableKeyguardBlurs()
+ /** Enable/disable window blur so they don't overlap with the window launch animation **/
+ fun setBlursDisabledForAppLaunch(disabled: Boolean)
}
/**
@@ -238,7 +237,9 @@
* during the animation.
*/
@JvmStatic
- fun fromView(view: View): Controller = GhostedViewLaunchAnimatorController(view)
+ fun fromView(view: View, cujType: Int? = null): Controller {
+ return GhostedViewLaunchAnimatorController(view, cujType)
+ }
}
/**
@@ -489,6 +490,7 @@
animator.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
Log.d(TAG, "Animation started")
+ keyguardHandler.setBlursDisabledForAppLaunch(true)
controller.onLaunchAnimationStart(isExpandingFullyAbove)
// Add the drawable to the launch container overlay. Overlays always draw
@@ -499,6 +501,7 @@
override fun onAnimationEnd(animation: Animator?) {
Log.d(TAG, "Animation ended")
+ keyguardHandler.setBlursDisabledForAppLaunch(false)
iCallback?.invoke()
controller.onLaunchAnimationEnd(isExpandingFullyAbove)
launchContainerOverlay.remove(windowBackgroundLayer)
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index 4b655a1..ffb7ab4 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -14,6 +14,7 @@
import android.view.ViewGroup
import android.view.ViewGroupOverlay
import android.widget.FrameLayout
+import com.android.internal.jank.InteractionJankMonitor
import kotlin.math.min
/**
@@ -29,7 +30,10 @@
*/
open class GhostedViewLaunchAnimatorController(
/** The view that will be ghosted and from which the background will be extracted. */
- private val ghostedView: View
+ private val ghostedView: View,
+
+ /** The [InteractionJankMonitor.CujType] associated to this animation. */
+ private val cujType: Int? = null
) : ActivityLaunchAnimator.Controller {
/** The container to which we will add the ghost view and expanding background. */
override var launchContainer = ghostedView.rootView as ViewGroup
@@ -125,6 +129,8 @@
val matrix = ghostView?.animationMatrix ?: Matrix.IDENTITY_MATRIX
matrix.getValues(initialGhostViewMatrixValues)
+
+ cujType?.let { InteractionJankMonitor.getInstance().begin(ghostedView, it) }
}
override fun onLaunchAnimationProgress(
@@ -167,6 +173,8 @@
}
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+ cujType?.let { InteractionJankMonitor.getInstance().end(it) }
+
backgroundDrawable?.wrapped?.alpha = startBackgroundAlpha
GhostView.removeGhost(ghostedView)
diff --git a/packages/SystemUI/docs/qs-tiles.md b/packages/SystemUI/docs/qs-tiles.md
index 89c28a0..efcb2de 100644
--- a/packages/SystemUI/docs/qs-tiles.md
+++ b/packages/SystemUI/docs/qs-tiles.md
@@ -306,6 +306,7 @@
* Add a case to the `switch` with a unique String spec for the chosen tile.
5. In [SystemUI/res/values/config.xml](/packages/SystemUI/res/values/config.xml), modify `quick_settings_tiles_stock` and add the spec defined in the previous step. If necessary, add it also to `quick_settings_tiles_default`. The first one contains a list of all the tiles that SystemUI knows how to create (to show to the user in the customization screen). The second one contains only the default tiles that the user will experience on a fresh boot or after they reset their tiles.
6. In [SystemUI/res/values/tiles_states_strings.xml](/packages/SystemUI/res/values/tiles_states_strings.xml), add a new array for your tile. The name has to be `tile_states_<spec>`. Use a good description to help the translators.
+7. In [`SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt`](/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt), add a new element to the map in `SubtitleArrayMapping` corresponding to the resource created in the previous step.
#### Abstract methods in QSTileImpl
diff --git a/packages/SystemUI/res-keyguard/values-az/strings.xml b/packages/SystemUI/res-keyguard/values-az/strings.xml
index 91c2014..8f8d1c5 100644
--- a/packages/SystemUI/res-keyguard/values-az/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-az/strings.xml
@@ -38,7 +38,7 @@
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Enerji yığır"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sürətlə enerji yığır"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Yavaş enerji yığır"</string>
- <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Şarj müvəqqəti olaraq məhdudlaşdırılıb"</string>
+ <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Şarj müvəqqəti məhdudlaşdırılıb"</string>
<string name="keyguard_low_battery" msgid="1868012396800230904">"Adapteri qoşun."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Kilidi açmaq üçün Menyu düyməsinə basın."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Şəbəkə kilidlidir"</string>
diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml
index 1c99e53..8550164 100644
--- a/packages/SystemUI/res-keyguard/values-eu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml
@@ -22,16 +22,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="514691256816366517">"Teklatu-babeslea"</string>
<string name="keyguard_password_enter_pin_code" msgid="8582296866585566671">"Idatzi PIN kodea"</string>
- <string name="keyguard_password_enter_puk_code" msgid="3813154965969758868">"Idatzi SIM txartelaren PUKa eta PIN berria"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="3813154965969758868">"Idatzi SIMaren PUKa eta PIN kode berria"</string>
<string name="keyguard_password_enter_puk_prompt" msgid="3529260761374385243">"SIM txartelaren PUK kodea"</string>
- <string name="keyguard_password_enter_pin_prompt" msgid="2304037870481240781">"SIM txartelaren PIN berria"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="2304037870481240781">"SIMaren PIN kode berria"</string>
<string name="keyguard_password_entry_touch_hint" msgid="6180028658339706333"><font size="17">"Pasahitza idazteko, sakatu hau"</font></string>
<string name="keyguard_password_enter_password_code" msgid="7393393239623946777">"Idatzi desblokeatzeko pasahitza"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="3692259677395250509">"Idatzi desblokeatzeko PINa"</string>
<string name="keyguard_enter_your_pin" msgid="5429932527814874032">"Idatzi PINa"</string>
<string name="keyguard_enter_your_pattern" msgid="351503370332324745">"Marraztu eredua"</string>
<string name="keyguard_enter_your_password" msgid="7225626204122735501">"Idatzi pasahitza"</string>
- <string name="keyguard_password_wrong_pin_code" msgid="3514267777289393046">"PINa ez da zuzena."</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="3514267777289393046">"PIN kodea okerra da."</string>
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Txartelak ez du balio."</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Kargatuta"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hari gabe kargatzen"</string>
@@ -128,8 +128,8 @@
<string name="kg_fingerprint_not_recognized" msgid="5982606907039479545">"Ez da ezagutu"</string>
<string name="kg_face_not_recognized" msgid="7903950626744419160">"Ez da ezagutu"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="7730152526369857818">
- <item quantity="other">Idatzi SIM txartelaren PIN kodea. <xliff:g id="NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu.</item>
- <item quantity="one">Idatzi SIM txartelaren PIN kodea. <xliff:g id="NUMBER_0">%d</xliff:g> saiakera geratzen zaizu; oker idatziz gero, operadoreari eskatu beharko diozu gailua desblokeatzeko.</item>
+ <item quantity="other">Idatzi SIMaren PINa. <xliff:g id="NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu.</item>
+ <item quantity="one">Idatzi SIMaren PINa. <xliff:g id="NUMBER_0">%d</xliff:g> saiakera geratzen zaizu; oker idatziz gero, operadoreari eskatu beharko diozu gailua desblokeatzeko.</item>
</plurals>
<plurals name="kg_password_default_puk_message" formatted="false" msgid="571308542462946935">
<item quantity="other">Desgaitu egin da SIM txartela. Aurrera egiteko, idatzi PUK kodea. <xliff:g id="_NUMBER_1">%d</xliff:g> saiakera geratzen zaizkizu SIM txartela betiko erabilgaitz geratu aurretik. Xehetasunak lortzeko, jarri operadorearekin harremanetan.</item>
diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml
index 149b313..0a036c8 100644
--- a/packages/SystemUI/res-keyguard/values-fa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml
@@ -85,7 +85,7 @@
<string name="kg_login_too_many_attempts" msgid="4519957179182578690">"تلاشهای زیادی برای کشیدن الگو صورت گرفته است"</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"پین خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه تایپ کردید. \n\nپس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"گذرواژه خود را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه تایپ کردید. \n\nپس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"الگوی باز کردن قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدید. \n\nلطفاً پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"الگوی بازگشایی قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. \n\nلطفاً پساز <xliff:g id="NUMBER_1">%2$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
<string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"کد پین سیمکارت اشتباه است، اکنون برای باز کردن قفل دستگاهتان باید با شرکت مخابراتی تماس بگیرید."</string>
<plurals name="kg_password_wrong_pin_code" formatted="false" msgid="7030584350995485026">
<item quantity="one">کد پین سیمکارت اشتباه است، <xliff:g id="NUMBER_1">%d</xliff:g> بار دیگر میتوانید تلاش کنید.</item>
diff --git a/packages/SystemUI/res-keyguard/values-gu/strings.xml b/packages/SystemUI/res-keyguard/values-gu/strings.xml
index af43cf7..a41cce7 100644
--- a/packages/SystemUI/res-keyguard/values-gu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gu/strings.xml
@@ -38,7 +38,7 @@
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ચાર્જિંગ"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ઝડપથી ચાર્જિંગ"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ધીમેથી ચાર્જિંગ"</string>
- <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ચાર્જિંગ હંગામી રૂપે પ્રતિબંધિત કરવામાં આવ્યું છે"</string>
+ <string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ચાર્જિંગ હંગામીરૂપે પ્રતિબંધિત કરવામાં આવ્યું છે"</string>
<string name="keyguard_low_battery" msgid="1868012396800230904">"તમારું ચાર્જર કનેક્ટ કરો."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"અનલૉક કરવા માટે મેનૂ દબાવો."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"નેટવર્ક લૉક થયું"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ne/strings.xml b/packages/SystemUI/res-keyguard/values-ne/strings.xml
index b307544f..0692aef 100644
--- a/packages/SystemUI/res-keyguard/values-ne/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ne/strings.xml
@@ -36,7 +36,7 @@
<string name="keyguard_charged" msgid="5478247181205188995">"चार्ज भयो"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • तारविनै चार्ज गर्दै"</string>
<string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्ज गरिँदै"</string>
- <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • द्रुत गतिमा चार्ज गरिँदै"</string>
+ <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • द्रुत गतिमा चार्ज गरिँदै छ"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • मन्द गतिमा चार्ज गरिँदै"</string>
<string name="keyguard_plugged_in_charging_limited" msgid="6091488837901216962">"<xliff:g id="PERCENTAGE">%s</xliff:g> • चार्जिङ केही समयका लागि सीमित पारिएको छ"</string>
<string name="keyguard_low_battery" msgid="1868012396800230904">"तपाईंको चार्जर जोड्नुहोस्।"</string>
diff --git a/packages/SystemUI/res-product/values-fa/strings.xml b/packages/SystemUI/res-product/values-fa/strings.xml
index 52fa2d8..cd98ef6 100644
--- a/packages/SystemUI/res-product/values-fa/strings.xml
+++ b/packages/SystemUI/res-product/values-fa/strings.xml
@@ -38,8 +38,8 @@
<string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"<xliff:g id="NUMBER_0">%1$d</xliff:g> تلاش ناموفق برای باز کردن قفل تلفن داشتهاید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر، نمایه کاری پاک میشود که با آن همه دادههای نمایه حذف میشود."</string>
<string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"<xliff:g id="NUMBER">%d</xliff:g> تلاش ناموفق برای باز کردن قفل رایانه لوحی داشتهاید. نمایه کاری پاک میشود که با آن همه دادههای نمایه حذف میشود."</string>
<string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"<xliff:g id="NUMBER">%d</xliff:g> تلاش ناموفق برای باز کردن قفل تلفن داشتهاید. نمایه کاری پاک میشود که با آن همه دادههای نمایه حذف میشود."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"شما الگوی باز کردن قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. بعد از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته میشود که با استفاده از یک حساب ایمیل قفل رایانه لوحی خود را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"شما الگوی باز کردن قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته میشود که با استفاده از یک حساب ایمیل قفل تلفن را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"الگوی بازگشایی قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. بعداز <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته میشود که بااستفاده از یک حساب ایمیل قفل رایانه لوحیتان را باز کنید.\n\n لطفاً پساز <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"الگوی بازگشایی قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیدهاید. پساز <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته میشود که بااستفاده از یک حساب ایمیل قفل تلفن را باز کنید.\n\n لطفاً پساز <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
<string name="global_action_lock_message" product="default" msgid="7092460751050168771">"برای گزینههای بیشتر، قفل تلفن را باز کنید"</string>
<string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"برای گزینههای بیشتر، قفل رایانه لوحی را باز کنید"</string>
<string name="global_action_lock_message" product="device" msgid="3165224897120346096">"برای گزینههای بیشتر، قفل دستگاه را باز کنید"</string>
diff --git a/packages/SystemUI/res/anim/tv_bottom_sheet_button_state_list_animator.xml b/packages/SystemUI/res/anim/tv_bottom_sheet_button_state_list_animator.xml
new file mode 100644
index 0000000..fc3b4ed
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_bottom_sheet_button_state_list_animator.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true">
+ <set>
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="scaleX"
+ android:valueFrom="1.0"
+ android:valueTo="@dimen/bottom_sheet_button_selection_scaled"
+ android:valueType="floatType"/>
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="scaleY"
+ android:valueFrom="1.0"
+ android:valueTo="@dimen/bottom_sheet_button_selection_scaled"
+ android:valueType="floatType"/>
+ </set>
+ </item>
+ <item android:state_focused="false">
+ <set>
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="scaleX"
+ android:valueFrom="@dimen/bottom_sheet_button_selection_scaled"
+ android:valueTo="1.0"
+ android:valueType="floatType"/>
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="scaleY"
+ android:valueFrom="@dimen/bottom_sheet_button_selection_scaled"
+ android:valueTo="1.0"
+ android:valueType="floatType"/>
+ </set>
+ </item>
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/packages/SystemUI/res/anim/tv_bottom_sheet_enter.xml
similarity index 68%
copy from packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
copy to packages/SystemUI/res/anim/tv_bottom_sheet_enter.xml
index a2bbd2b..cace36d 100644
--- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
+++ b/packages/SystemUI/res/anim/tv_bottom_sheet_enter.xml
@@ -12,8 +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.
-->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:interpolator/decelerate_quint">
+ <translate android:fromYDelta="100%"
+ android:toYDelta="0"
+ android:duration="900"/>
+</set>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/packages/SystemUI/res/anim/tv_bottom_sheet_exit.xml
similarity index 68%
copy from packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
copy to packages/SystemUI/res/anim/tv_bottom_sheet_exit.xml
index a2bbd2b..f7efe7cd 100644
--- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
+++ b/packages/SystemUI/res/anim/tv_bottom_sheet_exit.xml
@@ -12,8 +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.
-->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:interpolator/decelerate_quint">
+ <translate android:fromYDelta="0"
+ android:toYDelta="100%"
+ android:duration="500"/>
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/tv_privacy_chip_collapse.xml b/packages/SystemUI/res/anim/tv_privacy_chip_collapse.xml
new file mode 100644
index 0000000..e6ceeb9
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_privacy_chip_collapse.xml
@@ -0,0 +1,41 @@
+<?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.
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:ordering="together"
+ android:interpolator="@interpolator/tv_privacy_chip_collapse_interpolator"
+ android:duration="@integer/privacy_chip_animation_millis">
+ <objectAnimator
+ android:propertyName="height"
+ android:valueTo="@dimen/privacy_chip_dot_size"
+ android:valueType="floatType"/>
+ <objectAnimator
+ android:propertyName="marginEnd"
+ android:valueTo="@dimen/privacy_chip_dot_margin_horizontal"
+ android:valueType="floatType"/>
+ <objectAnimator
+ android:propertyName="radius"
+ android:valueTo="@dimen/privacy_chip_dot_radius"
+ android:valueType="floatType"/>
+ <objectAnimator
+ android:propertyName="dotAlpha"
+ android:valueTo="255"
+ android:valueType="intType"/>
+ <objectAnimator
+ android:propertyName="bgAlpha"
+ android:valueTo="255"
+ android:valueType="intType"/>
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/tv_privacy_chip_expand.xml b/packages/SystemUI/res/anim/tv_privacy_chip_expand.xml
new file mode 100644
index 0000000..4a510ae
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_privacy_chip_expand.xml
@@ -0,0 +1,41 @@
+<?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.
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:ordering="together"
+ android:interpolator="@interpolator/tv_privacy_chip_expand_interpolator"
+ android:duration="@integer/privacy_chip_animation_millis">
+ <objectAnimator
+ android:propertyName="height"
+ android:valueTo="@dimen/privacy_chip_height"
+ android:valueType="floatType"/>
+ <objectAnimator
+ android:propertyName="marginEnd"
+ android:valueTo="0"
+ android:valueType="floatType"/>
+ <objectAnimator
+ android:propertyName="radius"
+ android:valueTo="@dimen/privacy_chip_radius"
+ android:valueType="floatType"/>
+ <objectAnimator
+ android:propertyName="dotAlpha"
+ android:valueTo="255"
+ android:valueType="intType"/>
+ <objectAnimator
+ android:propertyName="bgAlpha"
+ android:valueTo="0"
+ android:valueType="intType"/>
+</set>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/packages/SystemUI/res/anim/tv_privacy_chip_fade_in.xml
similarity index 60%
copy from packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
copy to packages/SystemUI/res/anim/tv_privacy_chip_fade_in.xml
index a2bbd2b..701489a 100644
--- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
+++ b/packages/SystemUI/res/anim/tv_privacy_chip_fade_in.xml
@@ -12,8 +12,14 @@
~ 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.
-->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:ordering="together"
+ android:interpolator="@interpolator/tv_privacy_chip_collapse_interpolator"
+ android:duration="@integer/privacy_chip_animation_millis">
+ <objectAnimator
+ android:propertyName="dotAlpha"
+ android:valueTo="255"
+ android:valueType="intType"/>
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/tv_privacy_chip_fade_out.xml b/packages/SystemUI/res/anim/tv_privacy_chip_fade_out.xml
new file mode 100644
index 0000000..fa13471
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_privacy_chip_fade_out.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.
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:ordering="together"
+ android:interpolator="@interpolator/tv_privacy_chip_collapse_interpolator"
+ android:duration="@integer/privacy_chip_animation_millis">
+ <objectAnimator
+ android:propertyName="dotAlpha"
+ android:valueTo="0"
+ android:valueType="intType"/>
+ <objectAnimator
+ android:propertyName="bgAlpha"
+ android:valueTo="0"
+ android:valueType="intType"/>
+</set>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
similarity index 66%
copy from packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
copy to packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
index a2bbd2b..9b0bae0 100644
--- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
+++ b/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
@@ -12,8 +12,10 @@
~ 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.
-->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"
+ android:color="@color/bottom_sheet_button_background_color_focused"/>
+ <item android:color="@color/bottom_sheet_button_background_color_unfocused"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/packages/SystemUI/res/color/bottom_sheet_button_text_color.xml
similarity index 67%
copy from packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
copy to packages/SystemUI/res/color/bottom_sheet_button_text_color.xml
index a2bbd2b..05248f1 100644
--- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
+++ b/packages/SystemUI/res/color/bottom_sheet_button_text_color.xml
@@ -12,8 +12,10 @@
~ 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.
-->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"
+ android:color="@color/bottom_sheet_button_text_color_focused"/>
+ <item android:color="@color/bottom_sheet_button_text_color_unfocused"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/media_player_solid_button_bg.xml b/packages/SystemUI/res/color/media_player_solid_button_bg.xml
index 96685ab..69c9711 100644
--- a/packages/SystemUI/res/color/media_player_solid_button_bg.xml
+++ b/packages/SystemUI/res/color/media_player_solid_button_bg.xml
@@ -17,5 +17,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:color="?androidprv:attr/colorAccentTertiary"/>
+ <item android:color="?androidprv:attr/colorAccentPrimary"/>
</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/packages/SystemUI/res/drawable/bottom_sheet_background.xml
similarity index 69%
copy from packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
copy to packages/SystemUI/res/drawable/bottom_sheet_background.xml
index a2bbd2b..87850a0 100644
--- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
+++ b/packages/SystemUI/res/drawable/bottom_sheet_background.xml
@@ -12,8 +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.
-->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <solid android:color="@color/bottom_sheet_background_color"/>
+ <corners android:radius="@dimen/bottom_sheet_corner_radius"/>
+</shape>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.xml
similarity index 69%
copy from packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
copy to packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.xml
index a2bbd2b..cd2aa9c 100644
--- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
+++ b/packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.xml
@@ -12,8 +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.
-->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <solid android:color="@color/bottom_sheet_background_color_with_blur"/>
+ <corners android:radius="@dimen/bottom_sheet_corner_radius"/>
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml b/packages/SystemUI/res/drawable/bottom_sheet_button_background.xml
similarity index 69%
copy from packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml
copy to packages/SystemUI/res/drawable/bottom_sheet_button_background.xml
index 93f8724..585a6bc 100644
--- a/packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml
+++ b/packages/SystemUI/res/drawable/bottom_sheet_button_background.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2019 The Android Open Source Project
+ ~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -14,12 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
-
- <corners android:radius="20dp"/>
- <solid android:color="@color/tv_audio_recording_indicator_icon_background"/>
- <stroke android:width="1dp" android:color="@color/tv_audio_recording_indicator_stroke"/>
-
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <solid android:color="@color/bottom_sheet_button_background_color"/>
+ <corners android:radius="@dimen/bottom_sheet_button_corner_radius"/>
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_avatar_with_badge.xml b/packages/SystemUI/res/drawable/ic_avatar_with_badge.xml
new file mode 100644
index 0000000..b96ca0f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_avatar_with_badge.xml
@@ -0,0 +1,36 @@
+<?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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:width="50dp"
+ android:height="50dp"
+ android:viewportWidth="50"
+ android:viewportHeight="50">
+ <path
+ android:pathData="M0,24C0,10.7452 10.7452,0 24,0V0C37.2548,0 48,10.7452 48,24V24C48,37.2548 37.2548,48 24,48V48C10.7452,48 0,37.2548 0,24V24Z"
+ android:fillColor="?androidprv:attr/colorAccentSecondary"/>
+ <path
+ android:pathData="M31.2003,19.2C31.2003,23.1764 27.9767,26.4 24.0003,26.4C20.0238,26.4 16.8003,23.1764 16.8003,19.2C16.8003,15.2235 20.0238,12 24.0003,12C27.9767,12 31.2003,15.2235 31.2003,19.2ZM28.8003,19.2C28.8003,21.851 26.6513,24 24.0003,24C21.3493,24 19.2003,21.851 19.2003,19.2C19.2003,16.549 21.3493,14.4 24.0003,14.4C26.6513,14.4 28.8003,16.549 28.8003,19.2Z"
+ android:fillColor="@color/people_tile_background"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M24.0003,30C16.231,30 9.6114,34.5941 7.0898,41.0305C7.7041,41.6404 8.3512,42.2174 9.0282,42.7585C10.9059,36.8492 16.7964,32.4 24.0003,32.4C31.2042,32.4 37.0947,36.8492 38.9724,42.7585C39.6494,42.2174 40.2965,41.6404 40.9108,41.0305C38.3892,34.5941 31.7696,30 24.0003,30Z"
+ android:fillColor="@color/people_tile_background"/>
+ <path
+ android:pathData="M40,40m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
+ android:fillColor="?androidprv:attr/colorAccentTertiary"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/rounded_bg_full.xml b/packages/SystemUI/res/drawable/rounded_bg_full.xml
index e0d3f63..967c57b 100644
--- a/packages/SystemUI/res/drawable/rounded_bg_full.xml
+++ b/packages/SystemUI/res/drawable/rounded_bg_full.xml
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
- <solid android:color="?android:attr/colorBackgroundFloating" />
+ <solid android:color="?androidprv:attr/colorSurface" />
<corners
android:bottomLeftRadius="?android:attr/dialogCornerRadius"
android:topLeftRadius="?android:attr/dialogCornerRadius"
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/packages/SystemUI/res/interpolator/tv_privacy_chip_collapse_interpolator.xml
similarity index 77%
copy from packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
copy to packages/SystemUI/res/interpolator/tv_privacy_chip_collapse_interpolator.xml
index a2bbd2b..4298124 100644
--- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
+++ b/packages/SystemUI/res/interpolator/tv_privacy_chip_collapse_interpolator.xml
@@ -12,8 +12,11 @@
~ 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.
-->
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
+ android:controlX1="0.4"
+ android:controlY1="1.00"
+ android:controlX2="0.12"
+ android:controlY2="1.00"/>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/packages/SystemUI/res/interpolator/tv_privacy_chip_expand_interpolator.xml
similarity index 77%
rename from packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
rename to packages/SystemUI/res/interpolator/tv_privacy_chip_expand_interpolator.xml
index a2bbd2b..ed44715 100644
--- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
+++ b/packages/SystemUI/res/interpolator/tv_privacy_chip_expand_interpolator.xml
@@ -12,8 +12,11 @@
~ 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.
-->
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
+ android:controlX1="0.12"
+ android:controlY1="1.00"
+ android:controlX2="0.4"
+ android:controlY2="1.00"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_actions_change_panel.xml b/packages/SystemUI/res/layout/global_actions_change_panel.xml
index dffb0f0..bc9c203 100644
--- a/packages/SystemUI/res/layout/global_actions_change_panel.xml
+++ b/packages/SystemUI/res/layout/global_actions_change_panel.xml
@@ -14,8 +14,18 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<ImageView
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/global_actions_change_button"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content">
+ <TextView
+ android:id="@+id/global_actions_change_message"
+ android:layout_width="wrap_content"
+ android:visibility="gone"
+ android:layout_height="wrap_content"
+ android:text="@string/global_actions_change_description" />
+ <ImageView
+ android:id="@+id/global_actions_change_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index 93bd581..c92b10c 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -19,12 +19,15 @@
android:id="@+id/global_screenshot_frame"
android:theme="@style/Screenshot"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:importantForAccessibility="no">
<ImageView
android:id="@+id/screenshot_scrolling_scrim"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:visibility="gone"/>
+ android:visibility="gone"
+ android:clickable="true"
+ android:importantForAccessibility="no"/>
<ImageView
android:id="@+id/global_screenshot_actions_background"
android:layout_height="@dimen/screenshot_bg_protection_height"
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index 4765b44..3f4baaf 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -99,6 +99,7 @@
app:handleThickness="@dimen/screenshot_crop_handle_thickness"
app:handleColor="?androidprv:attr/colorAccentPrimary"
app:scrimColor="@color/screenshot_crop_scrim"
+ app:containerBackgroundColor="?android:colorBackgroundFloating"
tools:background="?android:colorBackground"
tools:minHeight="100dp"
tools:minWidth="100dp" />
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
index b563633..16c03e1 100644
--- a/packages/SystemUI/res/layout/media_output_list_item.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item.xml
@@ -81,6 +81,7 @@
android:visibility="gone"/>
<SeekBar
android:id="@+id/volume_seekbar"
+ style="@*android:style/Widget.DeviceDefault.SeekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"/>
diff --git a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
index 645dba4..c7e54d4 100644
--- a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
+++ b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
@@ -184,7 +184,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginEnd="@dimen/qs_media_info_spacing"
+ android:layout_marginEnd="@dimen/qs_media_action_spacing"
android:layout_marginBottom="@dimen/qs_media_padding"
app:layout_constrainedWidth="true"
app:layout_constraintWidth_min="48dp"
@@ -209,8 +209,8 @@
android:background="@drawable/qs_media_light_source"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_info_spacing"
- android:layout_marginEnd="@dimen/qs_media_info_spacing"
+ 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_constrainedWidth="true"
app:layout_constraintWidth_min="48dp"
@@ -233,7 +233,7 @@
android:background="@drawable/qs_media_light_source"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_info_spacing"
+ android:layout_marginStart="@dimen/qs_media_action_spacing"
android:layout_marginEnd="@dimen/qs_media_padding"
android:layout_marginBottom="@dimen/qs_media_padding"
app:layout_constrainedWidth="true"
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index c341f73..c0d353b 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -238,7 +238,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginEnd="@dimen/qs_media_info_spacing"
+ android:layout_marginEnd="@dimen/qs_media_action_spacing"
android:layout_marginBottom="@dimen/qs_media_padding"
app:layout_constrainedWidth="true"
app:layout_constraintWidth_min="48dp"
@@ -263,8 +263,8 @@
android:background="@drawable/qs_media_light_source"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_info_spacing"
- android:layout_marginEnd="@dimen/qs_media_info_spacing"
+ 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_constrainedWidth="true"
app:layout_constraintWidth_min="48dp"
@@ -287,7 +287,7 @@
android:background="@drawable/qs_media_light_source"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/qs_media_info_spacing"
+ android:layout_marginStart="@dimen/qs_media_action_spacing"
android:layout_marginEnd="@dimen/qs_media_padding"
android:layout_marginBottom="@dimen/qs_media_padding"
app:layout_constrainedWidth="true"
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index 42f14f3..6bc0138 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -64,10 +64,12 @@
android:layout_gravity="center"
android:padding="8dp"
android:gravity="center_vertical|start"
- android:textSize="15sp"
android:ellipsize="end"
- android:maxLines="1"
- style="@style/TextAppearance.NotificationImportanceChannel" />
+ android:maxLines="2"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+ android:textSize="16sp"
+ />
<Switch
android:id="@+id/toggle"
@@ -80,18 +82,13 @@
<View
android:layout_width="match_parent"
android:layout_height="1dp"
- android:background="?android:attr/colorAccent"
+ android:background="@*android:color/background_device_default_light"
/>
<!-- ChannelRows get added dynamically -->
</com.android.systemui.statusbar.notification.row.ChannelEditorListView>
- <!-- divider view -->
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="?android:attr/colorAccent"
- />
+
<RelativeLayout
android:id="@+id/bottom_actions"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/notif_half_shelf_row.xml b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
index 87de286..245d157 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf_row.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf_row.xml
@@ -21,61 +21,76 @@
android:layout_height="wrap_content"
android:padding="8dp"
android:clickable="true"
- android:orientation="horizontal"
- android:foreground="?android:attr/selectableItemBackground" >
-
- <!-- This is where an icon would go *if we wanted one* **wink** -->
- <Space
- android:id="@+id/icon"
- android:layout_height="48dp"
- android:layout_width="48dp"
- android:layout_gravity="center_vertical"
- android:padding="8dp"
- />
-
- <RelativeLayout
- android:id="@+id/description_container"
- android:layout_height="wrap_content"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_gravity="center_vertical"
- android:gravity="left|center_vertical"
- android:orientation="vertical"
+ android:orientation="vertical"
+ android:foreground="?android:attr/selectableItemBackground"
>
- <TextView
- android:id="@+id/channel_name"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:paddingBottom="0dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:gravity="center_vertical|start"
- android:textSize="14sp"
- android:ellipsize="end"
- android:maxLines="1"
- style="@style/TextAppearance.NotificationImportanceChannel"
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <!-- This is where an icon would go *if we wanted one* **wink** -->
+ <Space
+ android:id="@+id/icon"
+ android:layout_height="48dp"
+ android:layout_width="48dp"
+ android:layout_gravity="center_vertical"
+ android:padding="8dp"
/>
- <TextView
- android:id="@+id/channel_description"
+ <RelativeLayout
+ android:id="@+id/description_container"
android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:gravity="center_vertical|start"
- android:textSize="14sp"
- android:ellipsize="end"
- android:maxLines="1"
- android:layout_below="@id/channel_name"
- style="@style/TextAppearance.NotificationImportanceApp"
- />
- </RelativeLayout>
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ android:gravity="left|center_vertical"
+ android:orientation="vertical"
+ >
+ <TextView
+ android:id="@+id/channel_name"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingBottom="0dp"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:gravity="center_vertical|start"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:fontFamily="@*android:string/config_headlineFontFamily"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="16sp"
+ />
- <Switch
- android:id="@+id/toggle"
- android:layout_height="48dp"
- android:layout_width="wrap_content"
- android:layout_gravity="center_vertical"
- android:padding="8dp"
+ <TextView
+ android:id="@+id/channel_description"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:gravity="center_vertical|start"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:layout_below="@id/channel_name"
+ android:fontFamily="@*android:string/config_bodyFontFamily"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="14sp"
+ />
+ </RelativeLayout>
+
+ <Switch
+ android:id="@+id/toggle"
+ android:layout_height="48dp"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:padding="8dp"
+ />
+ </LinearLayout>
+ <!-- divider view -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height=".5dp"
+ android:background="@*android:color/background_device_default_light"
/>
</com.android.systemui.statusbar.notification.row.ChannelRow>
diff --git a/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml b/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml
index 2a4a21f..e87bf61 100644
--- a/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml
+++ b/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml
@@ -48,13 +48,13 @@
<Button
style="?android:attr/buttonBarButtonStyle"
- android:id="@+id/okay_button"
+ android:id="@+id/got_it_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/rounded_bg_full_large_radius"
android:onClick="dismissActivity"
- android:text="@string/okay"
+ android:text="@string/got_it"
android:textColor="?android:attr/textColorPrimary"
android:layout_marginBottom="60dp"
android:layout_alignParentBottom="true" />
@@ -62,7 +62,7 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_above="@id/okay_button"
+ android:layout_above="@id/got_it_button"
android:layout_below="@id/select_conversation"
android:layout_centerInParent="true"
android:clipToOutline="true">
@@ -72,7 +72,7 @@
android:layout_height="100dp"
android:layout_gravity="center"
android:background="@drawable/rounded_bg_full_large_radius"
- android:layout_above="@id/okay_button">
+ android:layout_above="@id/got_it_button">
<include layout="@layout/people_space_placeholder_layout" />
</LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/people_space_initial_layout.xml b/packages/SystemUI/res/layout/people_space_initial_layout.xml
index c57ec34..9892041 100644
--- a/packages/SystemUI/res/layout/people_space_initial_layout.xml
+++ b/packages/SystemUI/res/layout/people_space_initial_layout.xml
@@ -22,7 +22,8 @@
<LinearLayout
android:background="@drawable/people_space_tile_view_card"
- android:id="@+id/item"
+ android:clipToOutline="true"
+ android:id="@android:id/background"
android:orientation="horizontal"
android:gravity="center"
android:layout_gravity="top"
diff --git a/packages/SystemUI/res/layout/people_space_placeholder_layout.xml b/packages/SystemUI/res/layout/people_space_placeholder_layout.xml
index 061b0d9..c728bee 100644
--- a/packages/SystemUI/res/layout/people_space_placeholder_layout.xml
+++ b/packages/SystemUI/res/layout/people_space_placeholder_layout.xml
@@ -22,26 +22,40 @@
<LinearLayout
android:background="@drawable/people_space_tile_view_card"
- android:id="@+id/item"
+ android:clipToOutline="true"
+ android:id="@android:id/background"
android:orientation="horizontal"
android:gravity="center"
- android:layout_gravity="top"
+ android:layout_gravity="center"
android:paddingVertical="8dp"
- android:paddingHorizontal="16dp"
+ android:paddingHorizontal="2dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <TextView
+ android:layout_weight="6"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"/>
<LinearLayout
+ android:layout_weight="34"
android:orientation="vertical"
- android:paddingEnd="20dp"
- android:gravity="start|bottom"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
-
+ android:gravity="start|center_vertical"
+ android:layout_gravity="start|center_vertical"
+ android:layout_width="0dp"
+ android:layout_height="match_parent">
+ <TextView
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
<ImageView
- android:background="@drawable/ic_person"
- android:layout_width="48dp"
- android:layout_height="48dp" />
+ android:layout_weight="1"
+ android:gravity="start"
+ android:layout_gravity="start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_avatar_with_badge"
+ android:adjustViewBounds="true"
+ android:scaleType="centerInside" />
<TextView
android:id="@+id/name"
@@ -54,16 +68,25 @@
android:ellipsize="end"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
+ <TextView
+ android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
</LinearLayout>
<TextView
- android:text="@string/empty_status"
- android:textColor="?android:attr/textColorPrimary"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"
- android:textSize="12sp"
- android:layout_width="wrap_content"
+ android:layout_weight="52"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:maxLines="3"
- android:ellipsize="end" />
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:text="@string/empty_status"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="12sp" />
+ <TextView
+ android:layout_weight="6"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_space_widget_item.xml b/packages/SystemUI/res/layout/people_space_widget_item.xml
index 17d0c56..492d3ab 100644
--- a/packages/SystemUI/res/layout/people_space_widget_item.xml
+++ b/packages/SystemUI/res/layout/people_space_widget_item.xml
@@ -21,7 +21,8 @@
android:orientation="vertical">
<LinearLayout
android:background="@drawable/people_space_tile_view_card"
- android:id="@+id/item"
+ android:clipToOutline="true"
+ android:id="@android:id/background"
android:orientation="vertical"
android:padding="4dp"
android:layout_marginBottom="2dp"
diff --git a/packages/SystemUI/res/layout/people_tile_empty_layout.xml b/packages/SystemUI/res/layout/people_tile_empty_layout.xml
index 8e9ebc6..f115002 100644
--- a/packages/SystemUI/res/layout/people_tile_empty_layout.xml
+++ b/packages/SystemUI/res/layout/people_tile_empty_layout.xml
@@ -14,9 +14,10 @@
~ limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/item"
+ android:id="@android:id/background"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:background="@drawable/people_tile_empty_background"
+ android:clipToOutline="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/packages/SystemUI/res/layout/people_tile_large_empty.xml b/packages/SystemUI/res/layout/people_tile_large_empty.xml
index d4a8e15..f2a3922 100644
--- a/packages/SystemUI/res/layout/people_tile_large_empty.xml
+++ b/packages/SystemUI/res/layout/people_tile_large_empty.xml
@@ -14,8 +14,9 @@
~ limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/item"
+ android:id="@android:id/background"
android:background="@drawable/people_space_tile_view_card"
+ android:clipToOutline="true"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/people_tile_large_with_content.xml b/packages/SystemUI/res/layout/people_tile_large_with_content.xml
index 3f78fe7..6da17bc 100644
--- a/packages/SystemUI/res/layout/people_tile_large_with_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_large_with_content.xml
@@ -126,6 +126,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/people_space_content_background"
+ android:clipToOutline="true"
android:gravity="center"
android:scaleType="centerCrop" />
diff --git a/packages/SystemUI/res/layout/people_tile_large_with_notification_content.xml b/packages/SystemUI/res/layout/people_tile_large_with_notification_content.xml
index 60ff68e..c18a59a 100644
--- a/packages/SystemUI/res/layout/people_tile_large_with_notification_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_large_with_notification_content.xml
@@ -15,7 +15,7 @@
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@drawable/people_space_tile_view_card"
- android:id="@+id/item"
+ android:id="@android:id/background"
android:clipToOutline="true"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:layout_gravity="center"
diff --git a/packages/SystemUI/res/layout/people_tile_large_with_status_content.xml b/packages/SystemUI/res/layout/people_tile_large_with_status_content.xml
index cbc6ea8..508a255 100644
--- a/packages/SystemUI/res/layout/people_tile_large_with_status_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_large_with_status_content.xml
@@ -15,7 +15,7 @@
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@drawable/people_space_tile_view_card"
- android:id="@+id/item"
+ android:id="@android:id/background"
android:clipToOutline="true"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:layout_gravity="center"
diff --git a/packages/SystemUI/res/layout/people_tile_medium_empty.xml b/packages/SystemUI/res/layout/people_tile_medium_empty.xml
index ebb61c9..4a18683 100644
--- a/packages/SystemUI/res/layout/people_tile_medium_empty.xml
+++ b/packages/SystemUI/res/layout/people_tile_medium_empty.xml
@@ -21,7 +21,8 @@
android:orientation="vertical">
<LinearLayout
android:background="@drawable/people_space_tile_view_card"
- android:id="@+id/item"
+ android:clipToOutline="true"
+ android:id="@android:id/background"
android:gravity="center"
android:paddingHorizontal="16dp"
android:orientation="horizontal"
diff --git a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
index 0a5bf1d..9314685 100644
--- a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
@@ -18,8 +18,9 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
- android:id="@+id/item"
+ android:id="@android:id/background"
android:background="@drawable/people_space_tile_view_card"
+ android:clipToOutline="true"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -83,6 +84,7 @@
android:id="@+id/image"
android:gravity="center"
android:background="@drawable/people_space_content_background"
+ android:clipToOutline="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
diff --git a/packages/SystemUI/res/layout/people_tile_small.xml b/packages/SystemUI/res/layout/people_tile_small.xml
index 553b8a43..44e68e5 100644
--- a/packages/SystemUI/res/layout/people_tile_small.xml
+++ b/packages/SystemUI/res/layout/people_tile_small.xml
@@ -20,11 +20,12 @@
android:layout_height="match_parent">
<LinearLayout
- android:id="@+id/item"
+ android:id="@android:id/background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="@drawable/people_space_tile_view_card"
+ android:clipToOutline="true"
android:orientation="vertical"
android:paddingHorizontal="4dp"
android:paddingTop="6dp"
diff --git a/packages/SystemUI/res/layout/people_tile_suppressed_layout.xml b/packages/SystemUI/res/layout/people_tile_suppressed_layout.xml
index b151c60..4820a35 100644
--- a/packages/SystemUI/res/layout/people_tile_suppressed_layout.xml
+++ b/packages/SystemUI/res/layout/people_tile_suppressed_layout.xml
@@ -14,9 +14,10 @@
~ limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/item"
+ android:id="@android:id/background"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:background="@drawable/people_tile_suppressed_background"
+ android:clipToOutline="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/packages/SystemUI/res/layout/people_tile_with_suppression_detail_content_horizontal.xml b/packages/SystemUI/res/layout/people_tile_with_suppression_detail_content_horizontal.xml
new file mode 100644
index 0000000..f7e12eb
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_tile_with_suppression_detail_content_horizontal.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+~ Copyright (C) 2021 The Android Open Source Project
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:theme="@android:style/Theme.DeviceDefault.DayNight"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:id="@android:id/background"
+ android:background="@drawable/people_tile_suppressed_background"
+ android:clipToOutline="true"
+ android:padding="8dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:gravity="start"
+ android:id="@+id/text_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:singleLine="false"
+ android:text="@string/empty_status"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/content_text_size_for_medium" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/people_tile_with_suppression_detail_content_vertical.xml b/packages/SystemUI/res/layout/people_tile_with_suppression_detail_content_vertical.xml
new file mode 100644
index 0000000..c488d890
--- /dev/null
+++ b/packages/SystemUI/res/layout/people_tile_with_suppression_detail_content_vertical.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+~ Copyright (C) 2021 The Android Open Source Project
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:theme="@android:style/Theme.DeviceDefault.DayNight"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:id="@android:id/background"
+ android:background="@drawable/people_tile_suppressed_background"
+ android:clipToOutline="true"
+ android:padding="8dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+ <ImageView
+ android:id="@+id/person_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:gravity="center"
+ android:id="@+id/text_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:singleLine="false"
+ android:text="@string/empty_status"
+ android:layout_marginTop="@dimen/padding_between_suppressed_layout_items"
+ android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="@dimen/content_text_size_for_large" />
+
+ <ImageView
+ android:id="@+id/predefined_icon"
+ android:tint="?android:attr/textColorSecondary"
+ android:layout_marginTop="@dimen/padding_between_suppressed_layout_items"
+ android:layout_width="@dimen/regular_predefined_icon"
+ android:layout_height="@dimen/regular_predefined_icon"
+ tools:ignore="UseAppTint" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/people_tile_work_profile_quiet_layout.xml b/packages/SystemUI/res/layout/people_tile_work_profile_quiet_layout.xml
index 25ab5a6..1ccfb07 100644
--- a/packages/SystemUI/res/layout/people_tile_work_profile_quiet_layout.xml
+++ b/packages/SystemUI/res/layout/people_tile_work_profile_quiet_layout.xml
@@ -15,9 +15,10 @@
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/item"
+ android:id="@android:id/background"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:background="@drawable/people_tile_suppressed_background"
+ android:clipToOutline="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index c88703d..5b9ca1b 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -59,11 +59,16 @@
android:visibility="gone"
/>
- <LinearLayout
+ <FrameLayout
android:id="@+id/rightLayout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:gravity="center_vertical|end"
+ android:gravity="end"
+ >
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical|end"
>
<com.android.systemui.statusbar.phone.StatusIconContainer
android:id="@+id/statusIcons"
@@ -80,4 +85,6 @@
android:paddingEnd="2dp" />
</LinearLayout>
+ </FrameLayout>
+
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_bottom_sheet.xml b/packages/SystemUI/res/layout/tv_bottom_sheet.xml
new file mode 100644
index 0000000..b69cdc7
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_bottom_sheet.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/bottom_sheet"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ android:minHeight="@dimen/bottom_sheet_min_height"
+ android:paddingHorizontal="@dimen/bottom_sheet_padding_horizontal"
+ android:paddingVertical="@dimen/bottom_sheet_padding_vertical">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="80dp"
+ android:gravity="center"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+id/bottom_sheet_icon"
+ android:layout_width="@dimen/bottom_sheet_icon_size"
+ android:layout_height="@dimen/bottom_sheet_icon_size"
+ android:layout_gravity="center_vertical"
+ android:tint="@color/bottom_sheet_icon_color"/>
+ <ImageView
+ android:id="@+id/bottom_sheet_second_icon"
+ android:layout_width="@dimen/bottom_sheet_icon_size"
+ android:layout_height="@dimen/bottom_sheet_icon_size"
+ android:layout_marginStart="@dimen/bottom_sheet_icon_margin"
+ android:layout_gravity="center_vertical"
+ android:tint="@color/bottom_sheet_icon_color"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/bottom_sheet_padding_horizontal"
+ android:layout_weight="1"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/bottom_sheet_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/bottom_sheet_title_margin_bottom"
+ android:textAppearance="@style/BottomSheet.TitleText"/>
+
+ <TextView
+ android:id="@+id/bottom_sheet_body"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/bottom_sheet_details_margin_bottom"
+ android:textAppearance="@style/BottomSheet.BodyText" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="@dimen/bottom_sheet_actions_width"
+ android:layout_height="match_parent"
+ android:gravity="center">
+ <Button
+ android:id="@+id/bottom_sheet_positive_button"
+ style="@style/BottomSheet.ActionItem" />
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="@dimen/bottom_sheet_actions_spacing" />
+ <Button
+ android:id="@+id/bottom_sheet_negative_button"
+ style="@style/BottomSheet.ActionItem" />
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml
index dff148b..6218a5e 100644
--- a/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml
+++ b/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2019 The Android Open Source Project
+ ~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -20,16 +20,25 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:padding="12dp"
- android:layout_gravity="center">
+ android:gravity="center"
+ android:animateLayoutChanges="false"
+ android:padding="@dimen/privacy_chip_margin">
+
+ <ImageView
+ android:id="@+id/chip_drawable"
+ android:layout_width="51dp"
+ android:layout_height="@dimen/privacy_chip_height"
+ android:minWidth="@dimen/privacy_chip_dot_bg_width"
+ android:minHeight="@dimen/privacy_chip_dot_bg_height"
+ android:layout_gravity="top|end" />
<LinearLayout
android:id="@+id/icons_container"
- android:background="@drawable/tv_rect_shadow_rounded"
- android:padding="@dimen/privacy_chip_icon_padding"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:orientation="horizontal"/>
-
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_gravity="center_vertical|end"
+ android:animateLayoutChanges="true"
+ android:paddingHorizontal="@dimen/privacy_chip_padding_horizontal" />
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index ee89b97..c9256ae 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -46,7 +46,6 @@
android:id="@+id/volume_row_slider_frame"
android:layout_width="match_parent"
android:layout_height="@dimen/volume_row_slider_height">
- <include layout="@layout/volume_dnd_icon"/>
<SeekBar
android:id="@+id/volume_row_slider"
android:paddingLeft="0dp"
@@ -63,6 +62,7 @@
android:background="@null"
android:layoutDirection="ltr"
android:rotation="270" />
+ <include layout="@layout/volume_dnd_icon"/>
</FrameLayout>
<com.android.keyguard.AlphaOptimizedImageButton
diff --git a/packages/SystemUI/res/layout/volume_dnd_icon.xml b/packages/SystemUI/res/layout/volume_dnd_icon.xml
index 10c1472..56587b9 100644
--- a/packages/SystemUI/res/layout/volume_dnd_icon.xml
+++ b/packages/SystemUI/res/layout/volume_dnd_icon.xml
@@ -18,12 +18,14 @@
android:id="@+id/dnd_icon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="6dp">
+ android:layout_gravity="bottom"
+ android:layout_marginTop="6dp"
+ android:layout_marginBottom="6dp">
<ImageView
android:layout_width="14dp"
android:layout_height="14dp"
- android:layout_gravity="right|top"
+ android:layout_gravity="center"
android:src="@*android:drawable/ic_qs_dnd"
android:tint="?android:attr/textColorTertiary"/>
</FrameLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/packages/SystemUI/res/transition/tv_privacy_chip_collapse.xml
similarity index 74%
copy from packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
copy to packages/SystemUI/res/transition/tv_privacy_chip_collapse.xml
index a2bbd2b..f22e8ef 100644
--- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
+++ b/packages/SystemUI/res/transition/tv_privacy_chip_collapse.xml
@@ -12,8 +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.
-->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
+ <fade android:fadingMode="fade_in" />
+ <changeBounds/>
+</transitionSet>
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/packages/SystemUI/res/transition/tv_privacy_chip_expand.xml
similarity index 74%
copy from packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
copy to packages/SystemUI/res/transition/tv_privacy_chip_expand.xml
index a2bbd2b..059ebc8 100644
--- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
+++ b/packages/SystemUI/res/transition/tv_privacy_chip_expand.xml
@@ -12,8 +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.
-->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
+ <changeBounds/>
+ <fade android:fadingMode="fade_out" />
+</transitionSet>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 2149371..89e841e 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Vergroot \'n deel van die skerm"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Wissel"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Toeganklikheidknoppie het die toeganklikheidgebaar vervang\n\n"<annotation id="link">"Bekyk instellings"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Jy kan van die toeganklikheidsgebaar na \'n -knoppie oorskakel\n\n"<annotation id="link">"Instellings"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Skuif knoppie na kant om dit tydelik te versteek"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Beweeg na links bo"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Beweeg na regs bo"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Sien onlangse boodskappe, gemiste oproepe en statusopdaterings"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Gesprek"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> het \'n boodskap gestuur"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> het \'n prent gestuur"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Kon nie jou batterymeter lees nie"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index fe3cc8e..5d94dba 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"የማያ ገጹን ክፍል አጉላ"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ማብሪያ/ማጥፊያ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"የተደራሽነት አዝራር የተደራሽነት ምልክትን ተክቷል\n\n"<annotation id="link">" ቅንብሮችን አሳይ"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"ከተደራሽነት ምልክቱ ወደ አንድ አዝራር መቀየር ይችላሉ።\n\n"<annotation id="link">"ከተደ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ለጊዜው ለመደበቅ አዝራሩን ወደ ጠርዝ ያንቀሳቅሱ"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ወደ ላይኛው ግራ አንቀሳቅስ"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ወደ ላይኛው ቀኝ አንቀሳቅስ"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"የቅርብ ጊዜ መልዕክቶችን፣ ያመለጡ ጥሪዎች እና፣ የሁኔታ ዝመናዎችን ይመልከቱ"</string>
<string name="people_tile_title" msgid="6589377493334871272">"ውይይት"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> መልዕክት ልኳል"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> ምስል ልኳል"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"የባትሪ መለኪያዎን የማንበብ ችግር"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 83b483e..fb6cba1 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -1054,6 +1054,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"تكبير جزء من الشاشة"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"تبديل"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"تم استبدال \"زر أدوات تسهيل الاستخدام\" بإيماءة تسهيل الاستخدام.\n\n"<annotation id="link">"الاطّلاع على الإعدادات"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"يمكنك التبديل من إيماءة تسهيل الاستخدام إلى استخدام زرّ\n\n"<annotation id="link">"الإعدادات"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"يمكنك نقل الزر إلى الحافة لإخفائه مؤقتًا."</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"نقل إلى أعلى يمين الشاشة"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"نقل إلى أعلى يسار الشاشة"</string>
@@ -1167,6 +1168,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"+<xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="people_tile_description" msgid="8154966188085545556">"عرض أحدث الرسائل والمكالمات الفائتة والتغييرات في الحالة"</string>
<string name="people_tile_title" msgid="6589377493334871272">"محادثة"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"تم إرسال رسالة من <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"تم إرسال صورة من <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"حدثت مشكلة أثناء قراءة مقياس مستوى شحن البطارية."</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 50d866d..4892730 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্ৰীনৰ কিছু অংশ বিবৰ্ধন কৰক"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ছুইচ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"সাধ্য সুবিধাৰ বুটামটোৱে সাধ্য সুবিধাৰ নিৰ্দেশ সলনি কৰিছে\n\n"<annotation id="link">"ছেটিং চাওক"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"আপুনি সাধ্য-সুবিধাৰ নিৰ্দেশটোৰ পৰা এটা বুটামলৈ সলনি কৰিব পাৰে\n\n"<annotation id="link">"ছেটিং"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"বুটামটোক সাময়িকভাৱে লুকুৱাবলৈ ইয়াক একেবাৰে কাষলৈ লৈ যাওক"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"শীৰ্ষৰ বাওঁফালে নিয়ক"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"শীৰ্ষৰ সোঁফালে নিয়ক"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"শেহতীয়া বাৰ্তা, মিছড্ কল আৰু স্থিতিৰ আপডে’ট চাওক"</string>
<string name="people_tile_title" msgid="6589377493334871272">"বাৰ্তালাপ"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g>এ এটা বাৰ্তা পঠিয়াইছে"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g>এ এখন প্ৰতিচ্ছবি পঠিয়াইছে"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"আপোনাৰ বেটাৰী মিটাৰ পঢ়োঁতে সমস্যা হৈছে"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 9784d53..a67a41a 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -89,7 +89,7 @@
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Skrinşot çəkməyə tətbiq və ya təşkilat tərəfindən icazə verilmir"</string>
<string name="screenshot_edit_label" msgid="8754981973544133050">"Redaktə edin"</string>
<string name="screenshot_edit_description" msgid="3333092254706788906">"Skrinşota düzəliş edin"</string>
- <string name="screenshot_scroll_label" msgid="2930198809899329367">"Daha çoxunu əhatə edin"</string>
+ <string name="screenshot_scroll_label" msgid="2930198809899329367">"Genişləndirin"</string>
<string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ekran şəklini ötürün"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Ekran şəklinə önbaxış"</string>
<string name="screenshot_top_boundary_pct" msgid="2520148599096479332">"Yuxarı sərhəd <xliff:g id="PERCENT">%1$d</xliff:g> faiz"</string>
@@ -100,7 +100,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran çəkilişi emal edilir"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekranın video çəkimi ərzində silinməyən bildiriş"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Yazmağa başlanılsın?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"Yazarkən Android Sistemi ekranınızda görünən və ya cihazınızda göstərilən istənilən həssas məlumatı qeydə ala bilər. Buraya parollar, ödəniş məlumatı, fotolar, mesajlar və audio daxildir."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"Ekranda görünən və ya cihazda oxudulan şəxsi məlumat (parol, bank hesabı, mesaj, fotoşəkil və sair) videoyazıya düşə bilər."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Audio yazın"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Cihaz audiosu"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Cihazınızdan gələn musiqi, zənglər və zəng melodiyaları kimi səslər"</string>
@@ -109,7 +109,7 @@
<string name="screenrecord_start" msgid="330991441575775004">"Başlayın"</string>
<string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"Ekran yazılır"</string>
<string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"Ekran və audio yazılır"</string>
- <string name="screenrecord_taps_label" msgid="1595690528298857649">"Ekranda toxunuşları göstərin"</string>
+ <string name="screenrecord_taps_label" msgid="1595690528298857649">"Ekrana toxunuş göstərilsin"</string>
<string name="screenrecord_stop_text" msgid="6549288689506057686">"Dayandırmaq üçün toxunun"</string>
<string name="screenrecord_stop_label" msgid="72699670052087989">"Dayandırın"</string>
<string name="screenrecord_pause_label" msgid="6004054907104549857">"Dayandırın"</string>
@@ -605,15 +605,15 @@
<string name="accessibility_output_chooser" msgid="7807898688967194183">"Çıxış cihazına keçin"</string>
<string name="screen_pinning_title" msgid="9058007390337841305">"Tətbiq bərkidilib"</string>
<string name="screen_pinning_description" msgid="8699395373875667743">"Sancaq götürülənə qədər bu görünəcək. Sancağı götürmək üçün Geri və İcmal düymələrinə basıb saxlayın."</string>
- <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"Sancaq götürülənə qədər bu görünəcək. Sancağı götürmək üçün Geri və Əsas səhifə düymələrinə basıb saxlayın."</string>
- <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Bu, onu çıxarana qədər görünəcək. Çıxarmaq üçün yuxarı sürüşdürün & basıb saxlayın."</string>
- <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"Sancaq götürülənə qədər bu görünəcək. Sancağı götürmək üçün Geri düyməsinə basıb saxlayın."</string>
- <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Sancaq götürülənə qədər bu görünəcək. Sancağı götürmək üçün Əsas səhifə düyməsinə basıb saxlayın."</string>
- <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Şəxsi məlumatlar (məsələn, kontaktlar və e-poçt məzmunu) əlçatan ola bilər."</string>
+ <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"\"Geri\" və \"Əsas ekran\" düymələrinin davamlı basılması ilə çıxarılana qədər tətbiq göz önündə qalır."</string>
+ <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Yuxarı sürüşdürülüb saxlanılana ilə çıxarılana qədər tətbiq göz önündə qalır."</string>
+ <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"\"İcmal\" düyməsinin davamlı basılması ilə çıxarılana qədər tətbiq göz önündə qalır."</string>
+ <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"\"Əsas ekran\" düyməsinin davamlı basılması ilə çıxarılana qədər tətbiq göz önündə qalır."</string>
+ <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Şəxsi məlumatlar (məsələn, kontaktlar və e-poçt məzmunu) ekranda görünə bilər."</string>
<string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"Bərkidilmiş tətbiq digər tətbiqləri aça bilər."</string>
<string name="screen_pinning_toast" msgid="8177286912533744328">"Bu tətbiqi çıxarmaq üçün Geri və İcmal düymələrinə basıb saxlayın"</string>
<string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"Bu tətbiqi çıxarmaq üçün Geri və Əsas ekran düymələrinə basıb saxlayın"</string>
- <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Bu tətbiqi çıxarmaq üçün yuxarı sürüşdürüb saxlayın"</string>
+ <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Tətbiqi çıxarmaq üçün yuxarı sürüşdürüb saxlayın"</string>
<string name="screen_pinning_positive" msgid="3285785989665266984">"Anladım!"</string>
<string name="screen_pinning_negative" msgid="6882816864569211666">"Yox, çox sağ olun"</string>
<string name="screen_pinning_start" msgid="7483998671383371313">"Tətbiq bərkidildi"</string>
@@ -1033,7 +1033,8 @@
<string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Tam ekranı böyüdün"</string>
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran hissəsinin böyüdülməsi"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Dəyişdirici"</string>
- <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Əlçatımlılıq düyməsi əlçatımlılıq jestini əvəz etdi\n\n"<annotation id="link">"Ayarlara baxın"</annotation></string>
+ <string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Jest xüsusi imkanlar düyməsinə dəyişdirildi\n\n"<annotation id="link">"Ayarlara baxın"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Əlçatımlılıq jestindən düymə\n\n"<annotation id="link">"Ayarlarına"</annotation>" keçə bilərsiniz"</string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Düyməni müvəqqəti gizlətmək üçün kənara çəkin"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Yuxarıya sola köçürün"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Yuxarıya sağa köçürün"</string>
@@ -1043,7 +1044,7 @@
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Kənara daşıyıb göstərin"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"keçirin"</string>
<string name="quick_controls_title" msgid="7095074621086860062">"Əsas səhifə nizamlayıcıları"</string>
- <string name="controls_providers_title" msgid="6879775889857085056">"Nizamlayıcıları əlavə etmək üçün tətbiq seçin"</string>
+ <string name="controls_providers_title" msgid="6879775889857085056">"Kontrol əlavə etmək üçün tətbiq seçin"</string>
<plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380">
<item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> nizamlayıcı əlavə edilib.</item>
<item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> nizamlayıcı əlavə edilib.</item>
@@ -1057,8 +1058,8 @@
<string name="accessibility_control_move" msgid="8980344493796647792">"<xliff:g id="NUMBER">%d</xliff:g> mövqeyinə keçirin"</string>
<string name="controls_favorite_default_title" msgid="967742178688938137">"Nizamlayıcılar"</string>
<string name="controls_favorite_subtitle" msgid="6481675111056961083">"Sürətli Ayarlardan giriş üçün nizamlayıcıları seçin"</string>
- <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Nizamlayıcıları yenidən tənzimləmək üçün tutub sürüşdürün"</string>
- <string name="controls_favorite_removed" msgid="5276978408529217272">"Bütün nizamlayıcılar silindi"</string>
+ <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Vidcetləri daşıyaraq yerini dəyişin"</string>
+ <string name="controls_favorite_removed" msgid="5276978408529217272">"Kontrol vidcetləri silindi"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Dəyişikliklər yadda saxlanmadı"</string>
<string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Digər tətbiqlərə baxın"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Nizamlayıcıları yükləmək mümkün olmadı. <xliff:g id="APP">%s</xliff:g> tətbiqinə toxunaraq tətbiq ayarlarının dəyişmədiyinə əmin olun."</string>
@@ -1100,8 +1101,8 @@
<string name="controls_error_failed" msgid="960228639198558525">"Xəta, yenidən cəhd edin"</string>
<string name="controls_in_progress" msgid="4421080500238215939">"Davam edir"</string>
<string name="controls_added_tooltip" msgid="5866098408470111984">"Yeni nizamlayıcılara baxmaq üçün Sürətli Ayarları açın"</string>
- <string name="controls_menu_add" msgid="4447246119229920050">"Nizamlayıcılar əlavə edin"</string>
- <string name="controls_menu_edit" msgid="890623986951347062">"Nizamlayıcıları redaktə edin"</string>
+ <string name="controls_menu_add" msgid="4447246119229920050">"Vidcet əlavə edin"</string>
+ <string name="controls_menu_edit" msgid="890623986951347062">"Vidcetlərə düzəliş edin"</string>
<string name="media_output_dialog_add_output" msgid="5642703238877329518">"Nəticələri əlavə edin"</string>
<string name="media_output_dialog_group" msgid="5571251347877452212">"Qrup"</string>
<string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 cihaz seçilib"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Son mesajlar, buraxılmış zənglər və status güncəlləmələrinə baxın"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Söhbət"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> mesaj göndərdi"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> şəkil göndərdi"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Batareya ölçüsünü oxuyarkən problem yarandı"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 677bfb2..3cbe6ba 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -1039,6 +1039,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Uvećajte deo ekrana"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Pređi"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Dugme Pristupačnost je zamenilo pokret za pristupačnost\n\n"<annotation id="link">"Prikaži podešavanja"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Možete da pređete sa pokreta za pristupačnost na dugme\n\n"<annotation id="link">"Podešavanja"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Pomerite dugme do ivice da biste ga privremeno sakrili"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Premesti gore levo"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Premesti gore desno"</string>
@@ -1149,6 +1150,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Pogledajte nedavne poruke, propuštene pozive i ažuriranja statusa"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Konverzacija"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> šalje poruku"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> šalje sliku"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem sa očitavanjem merača baterije"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 6576a92..3e06a68 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -1044,6 +1044,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Павялічыць частку экрана"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Пераключальнік"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Замест жэста спецыяльных магчымасцей будзе выкарыстоўвацца кнопка\n\n"<annotation id="link">"Праглядзець налады"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Вы можаце пераключыцца з жэста спецыяльных магчымасцей на кнопку\n\n"<annotation id="link">"Налады"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Каб часова схаваць кнопку, перамясціце яе на край"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Перамясціць лявей і вышэй"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Перамясціць правей і вышэй"</string>
@@ -1155,6 +1156,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Глядзець нядаўнія паведамленні, прапушчаныя выклікі і абнаўленні стану"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Размова"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"Карыстальнік <xliff:g id="NAME">%1$s</xliff:g> адправіў паведамленне"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"Карыстальнік <xliff:g id="NAME">%1$s</xliff:g> адправіў відарыс"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Праблема з чытаннем індыкатара зараду акумулятара"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index d51bc7b..fafc9ee 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увеличаване на част от екрана"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Превключване"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Жестът за достъпност бе заменен от бутон\n\n"<annotation id="link">"Преглед на настройките"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Вместо жеста за достъпност можете да използвате бутон\n\n"<annotation id="link">"Настройки"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Преместете бутона до края, за да го скриете временно"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Преместване горе вляво"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Преместване горе вдясно"</string>
@@ -1143,6 +1144,7 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"Над <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Преглеждайте скорошни съобщения, пропуснати обаждания и информация за състоянието"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Разговор"</string>
+ <string name="paused_by_dnd" msgid="7856941866433556428">"Поставено на пауза от режима „Не безпокойте“"</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> изпрати съобщение"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> изпрати изображение"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Възникна проблем при четенето на данните за нивото на батерията"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index b402402..7c51625 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্রিনের কিছুটা অংশ বড় করুন"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"বদল করুন"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"অ্যাক্সেসিবিলিটি জেসচার পরিবর্তন করে অ্যাক্সেসেবিলিটি বোতাম করা হয়েছে\n\n"<annotation id="link">"সেটিংস দেখুন"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"আপনি অ্যাক্সেসিবিলিটি জেসচারের বদলে \n\n"<annotation id="link">"সেটিংস"</annotation>" বোতামে সুইচ করতে পারেন"</string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"এটি অস্থায়ীভাবে লুকাতে বোতামটি কোণে সরান"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"উপরে বাঁদিকে সরান"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"উপরে ডানদিকে সরান"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"সাম্প্রতিক মেসেজ, মিসড কল এবং স্ট্যাটাস সংক্রান্ত আপডেট দেখুন"</string>
<string name="people_tile_title" msgid="6589377493334871272">"কথোপকথন"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> একটি মেসেজ পাঠিয়েছেন"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> একটি ছবি পাঠিয়েছেন"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"ব্যাটারির মিটারের রিডিং নেওয়ার সময় সমস্যা হয়েছে"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index cfcceb69..02cdd7a 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -1039,6 +1039,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Uvećavanje dijela ekrana"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prekidač"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Dugme za pristupačnost je zamijenilo pokret za pristupačnost\n\n"<annotation id="link">"Prikaži postavke"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Možete prebaciti s pokreta za pristupačnost na dugme\n\n"<annotation id="link">"Postavke"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Premjestite dugme do ivice da ga privremeno sakrijete"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Pomjeranje gore lijevo"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Pomjeranje gore desno"</string>
@@ -1149,6 +1150,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Pregledajte nedavne poruke, propuštene pozive i ažuriranja statusa"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Razgovor"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> je poslao/la poruku"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> je poslao/la sliku"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Došlo je do problema prilikom očitavanja mjerača stanja baterije"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 925a293..a01d4fe 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplia una part de la pantalla"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Canvia"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"El gest d\'accessibilitat s\'ha substituït pel botó d\'accessibilitat\n\n"<annotation id="link">"Mostra la configuració"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Pots canviar del gest d\'accessibilitat a un botó\n\n"<annotation id="link">"Configuració"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mou el botó a l\'extrem per amagar-lo temporalment"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mou a dalt a l\'esquerra"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mou a dalt a la dreta"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Consulta els missatges recents, les trucades perdudes i les actualitzacions d\'estat"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Conversa"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> ha enviat un missatge"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> ha enviat una imatge"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Hi ha hagut un problema en llegir el mesurador de la bateria"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index f3d64ab..f3a5828 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -1044,6 +1044,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zvětšit část obrazovky"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Přepnout"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Tlačítko přístupnosti bylo nahrazeno gestem přístupnosti\n\n"<annotation id="link">"Zobrazit nastavení"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Z gesta přístupnosti můžete přejít na tlačítko\n\n"<annotation id="link">"Nastavení"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Přesunutím tlačítka k okraji ho dočasně skryjete"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Přesunout vlevo nahoru"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Přesunout vpravo nahoru"</string>
@@ -1155,6 +1156,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Zobrazit poslední zprávy, zmeškané hovory a aktualizace stavu"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Konverzace"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> posílá zprávu"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> posílá obrázek"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problém s načtením měřiče baterie"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 0c7a743..53fb06c 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Forstør en del af skærmen"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Skift"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Knappen Hjælpefunktioner har erstattet bevægelsen for hjælpefunktioner\n\n"<annotation id="link">"Se indstillinger"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Hvis du ikke vil bruge bevægelsen til hjælpefunktioner, kan du skifte til knappen\n\n"<annotation id="link">"Indstillinger"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Flyt knappen til kanten for at skjule den midlertidigt"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Flyt op til venstre"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Flyt op til højre"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Se dine seneste beskeder, mistede opkald og statusopdateringer"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Samtale"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> har sendt en sms"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> har sendt et billede"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Der er problemer med at aflæse dit batteriniveau"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 0f18757..82c8c58 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Teil des Bildschirms vergrößern"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Schalter"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Die Schaltfläche „Bedienungshilfen“ ersetzt die Touch-Geste für Bedienungshilfen\n\n"<annotation id="link">"Einstellungen aufrufen"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Du kannst von der barrierefreien Geste zu einer Schaltfläche wechseln\n\n"<annotation id="link">"Einstellungen"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Durch Ziehen an den Rand wird die Schaltfläche zeitweise ausgeblendet"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Nach oben links verschieben"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Nach rechts oben verschieben"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Letzte Nachrichten, verpasste Anrufe und Statusaktualisierungen ansehen"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Unterhaltung"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> hat eine Nachricht gesendet"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> hat ein Bild gesendet"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem beim Lesen des Akkustands"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 9525f99..2ab27a7 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Μεγέθυνση μέρους της οθόνης"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Εναλλαγή"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Το κουμπί προσβασιμότητας αντικατέστησε την κίνηση προσβασιμότητας\n\n"<annotation id="link">"Προβολή ρυθμίσεων"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Μπορείτε να κάνετε εναλλαγή από την κίνηση προσβασιμότητας σε ένα κουμπί\n\n"<annotation id="link">"Ρυθμίσεις"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Μετακινήστε το κουμπί στο άκρο για προσωρινή απόκρυψη"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Μετακίνηση επάνω αριστερά"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Μετακίνηση επάνω δεξιά"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Δείτε πρόσφατα μηνύματα, αναπάντητες κλήσεις και ενημερώσεις κατάστασης"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Συνομιλία"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"Ο χρήστης <xliff:g id="NAME">%1$s</xliff:g> έστειλε ένα μήνυμα"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"Ο χρήστης <xliff:g id="NAME">%1$s</xliff:g> έστειλε μια εικόνα"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Υπάρχει κάποιο πρόβλημα με την ανάγνωση του μετρητή μπαταρίας"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index c346bbe..7fec05a 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Accessibility button replaced the accessibility gesture\n\n"<annotation id="link">"View settings"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"You can switch from the accessibility gesture to a button\n\n"<annotation id="link">"Settings"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string>
@@ -1143,6 +1144,7 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"See recent messages, missed calls and status updates"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Conversation"</string>
+ <string name="paused_by_dnd" msgid="7856941866433556428">"Paused by Do Not Disturb"</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> sent a message"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> sent an image"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem reading your battery meter"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index b588e86..97624b6 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Accessibility button replaced the accessibility gesture\n\n"<annotation id="link">"View settings"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"You can switch from the accessibility gesture to a button\n\n"<annotation id="link">"Settings"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string>
@@ -1143,6 +1144,7 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"See recent messages, missed calls and status updates"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Conversation"</string>
+ <string name="paused_by_dnd" msgid="7856941866433556428">"Paused by Do Not Disturb"</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> sent a message"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> sent an image"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem reading your battery meter"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index c346bbe..7fec05a 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Accessibility button replaced the accessibility gesture\n\n"<annotation id="link">"View settings"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"You can switch from the accessibility gesture to a button\n\n"<annotation id="link">"Settings"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string>
@@ -1143,6 +1144,7 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"See recent messages, missed calls and status updates"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Conversation"</string>
+ <string name="paused_by_dnd" msgid="7856941866433556428">"Paused by Do Not Disturb"</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> sent a message"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> sent an image"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem reading your battery meter"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index c346bbe..7fec05a 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Accessibility button replaced the accessibility gesture\n\n"<annotation id="link">"View settings"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"You can switch from the accessibility gesture to a button\n\n"<annotation id="link">"Settings"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string>
@@ -1143,6 +1144,7 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"See recent messages, missed calls and status updates"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Conversation"</string>
+ <string name="paused_by_dnd" msgid="7856941866433556428">"Paused by Do Not Disturb"</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> sent a message"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> sent an image"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem reading your battery meter"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index a73c387..cf89f22 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Accessibility button replaced the accessibility gesture\n\n"<annotation id="link">"View settings"</annotation>""</string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"You can switch from the accessibility gesture to a button\n\n"<annotation id="link">"Settings"</annotation>""</string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Move button to the edge to hide it temporarily"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Move top left"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Move top right"</string>
@@ -1143,6 +1144,7 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"See recent messages, missed calls, and status updates"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Conversation"</string>
+ <string name="paused_by_dnd" msgid="7856941866433556428">"Paused by Do Not Disturb"</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> sent a message"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> sent an image"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem reading your battery meter"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 051c0dd..6de7754 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -471,7 +471,7 @@
<string name="interruption_level_alarms_twoline" msgid="2045067991335708767">"Solo\nalarmas"</string>
<string name="keyguard_indication_charging_time_wireless" msgid="577856646141738675">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando de manera inalámbrica • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time" msgid="6492711711891071502">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
- <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando rápido • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Carga rápida • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Cargando lento • Se completará en <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Cambiar usuario"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="5759855008166759399">"Cambiar de usuario (usuario actual: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>)"</string>
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte de la pantalla"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Botón"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"El botón de accesibilidad reemplaza el gesto de accesibilidad\n\n"<annotation id="link">"Ver configuración"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Puedes cambiar de un gesto a un botón de accesibilidad\n\n"<annotation id="link">"Configuración"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mueve el botón hacia el borde para ocultarlo temporalmente"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover arriba a la izquierda"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover arriba a la derecha"</string>
@@ -1143,6 +1144,7 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g> o más"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Consulta mensajes recientes, llamadas perdidas y actualizaciones de estado"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Conversación"</string>
+ <string name="paused_by_dnd" msgid="7856941866433556428">"Se detuvo por el modo No interrumpir"</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> envió un mensaje"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> envió una imagen"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problema al leer el medidor de batería"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index d04fd45..b58b0f6 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte de la pantalla"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Cambiar"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"El botón Accesibilidad ha reemplazado el gesto de accesibilidad\n\nVer ajustes"<annotation id="link"></annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Puedes cambiar el gesto de accesibilidad por un botón\n\n"<annotation id="link">"Ajustes"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mueve el botón hacia el borde para ocultarlo temporalmente"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover arriba a la izquierda"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover arriba a la derecha"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Consulta los mensajes recientes, las llamadas perdidas y los cambios de estado"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Conversación"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> ha enviado un mensaje"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> ha enviado una imagen"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"No se ha podido leer el indicador de batería"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 00d98c2..2afd6af 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekraanikuva osa suurendamine"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Vaheta"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Juurdepääsetavuse nupp asendas juurdepääsuliigutuse\n\n"<annotation id="link">"Vaadake seadeid"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Saate juurdepääsuliigutuselt nupule lülituda.\n\n"<annotation id="link">"Seaded"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Teisaldage nupp serva, et see ajutiselt peita"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Teisalda üles vasakule"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Teisalda üles paremale"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Vaadake hiljutisi sõnumeid, vastamata kõnesid ja olekuvärskendusi"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Vestlus"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> saatis sõnumi"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> saatis pildi"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Probleem akumõõdiku lugemisel"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 78eb728..64641d6 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -160,7 +160,7 @@
<string name="biometric_dialog_use_pin" msgid="8385294115283000709">"Erabili PINa"</string>
<string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"Erabili eredua"</string>
<string name="biometric_dialog_use_password" msgid="3445033859393474779">"Erabili pasahitza"</string>
- <string name="biometric_dialog_wrong_pin" msgid="1878539073972762803">"PIN kodea ez da zuzena"</string>
+ <string name="biometric_dialog_wrong_pin" msgid="1878539073972762803">"PINa ez da zuzena"</string>
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Eredua ez da zuzena"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Pasahitza ez da zuzena"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Saiakera oker gehiegi egin dituzu.\nSaiatu berriro <xliff:g id="NUMBER">%d</xliff:g> segundo barru."</string>
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Handitu pantailaren zati bat"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Botoia"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Erabilerraztasuna botoiak erabilerraztasun-keinua ordezkatu du\n\n"<annotation id="link">"Ikusi ezarpenak"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Erabilerraztasun-keinua erabiltzeari utz diezaiokezu eta botoi bat erabiltzen has zaitezke\n\n"<annotation id="link">"Ezarpenak"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Eraman botoia ertzera aldi baterako ezkutatzeko"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Eraman goialdera, ezkerretara"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Eraman goialdera, eskuinetara"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Ikusi azken mezuak, dei galduak eta egoerari buruzko informazio eguneratua"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Elkarrizketa"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> erabiltzaileak mezu bat bidali du"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> erabiltzaileak irudi bat bidali du"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Arazo bat gertatu da bateria-neurgailua irakurtzean"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 10e95f4..c71ee67 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"درشتنمایی بخشی از صفحه"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"کلید"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"دکمه دسترسپذیری جایگزین اشاره دسترسپذیری شد\n\n"<annotation id="link">"مشاهده تنظیمات"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"میتوانید بهجای اشاره دسترسپذیری از دکمه استفاده کنید\n\n"<annotation id="link">"تنظیمات"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"برای پنهان کردن موقتی دکمه، آن را به لبه ببرید"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"انتقال به بالا سمت راست"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"انتقال به بالا سمت چپ"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"بیشاز <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="people_tile_description" msgid="8154966188085545556">"دیدن بهروزرسانیهای وضعیت، تماسهای بیپاسخ، و پیامهای اخیر"</string>
<string name="people_tile_title" msgid="6589377493334871272">"مکالمه"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> پیامی ارسال کرد"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> تصویری ارسال کرد"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"مشکلی در خواندن میزان باتری وجود دارد"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 51b73df..136ab7c 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Suurenna osa näytöstä"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Vaihda"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Esteettömyyspainike on korvannut esteettömyyseleen\n\n"<annotation id="link">"Katso asetukset"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Voit vaihtaa käyttämään esteettömyyseleen sijaan painiketta\n\n"<annotation id="link">"Asetukset"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Piilota painike tilapäisesti siirtämällä se reunaan"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Siirrä vasempaan yläreunaan"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Siirrä oikeaan yläreunaan"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"Yli <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Katso viimeaikaiset viestit, vastaamattomat puhelut ja tilapäivitykset"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Keskustelu"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> lähetti viestin"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> lähetti kuvan"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Ongelma akkumittarin lukemisessa"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index e657308..00457cd 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -349,7 +349,7 @@
<string name="quick_settings_rotation_locked_portrait_label" msgid="1194988975270484482">"Portrait"</string>
<string name="quick_settings_rotation_locked_landscape_label" msgid="2000295772687238645">"Paysage"</string>
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Mode de saisie"</string>
- <string name="quick_settings_location_label" msgid="2621868789013389163">"Position"</string>
+ <string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Localisation désactivée"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Accès à l\'appareil photo"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Accès au micro"</string>
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Agrandir une partie de l\'écran"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Commutateur"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Le bouton d\'accessibilité a remplacé le geste d\'accessibilité\n\n"<annotation id="link">"Voir les paramètres"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Vous pouvez passer du geste d\'accessibilité au bouton\n\n"<annotation id="link">"Paramètres"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Déplacez le bouton vers le bord pour le masquer temporairement"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Déplacer dans coin sup. gauche"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Déplacer dans coin sup. droit"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Affichez les messages récents, les appels manqués et les mises à jour d\'état"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Conversation"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> a envoyé un message"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> a envoyé une image"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Un problème est survenu lors de la lecture du niveau de charge de la pile"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 04f7692..3835cf7 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Agrandir une partie de l\'écran"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Changer"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Le bouton Accessibilité a remplacé le geste d\'accessibilité\n\n"<annotation id="link">"Afficher les paramètres"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Vous pouvez passer du geste d\'accessibilité à un bouton\n\n"<annotation id="link">"Paramètres"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Déplacer le bouton vers le bord pour le masquer temporairement"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Déplacer en haut à gauche"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Déplacer en haut à droite"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"+ de <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Voir les messages récents, les appels manqués et les notifications d\'état"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Conversation"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> a envoyé un message"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> a envoyé une image"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Un problème est survenu au niveau de la lecture de votre outil de mesure de batterie"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 52c6e33..ffe51cc1 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplía parte da pantalla"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Cambiar"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"O botón de accesibilidade substituíu o xesto de accesibilidade\n\n"<annotation id="link">"Ver configuración"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Podes cambiar do xesto de accesibilidade a un botón\n\n"<annotation id="link">"Configuración"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Para ocultar temporalmente o botón, móveo ata o bordo"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover á parte super. esquerda"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover á parte superior dereita"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"+ de <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Consulta as mensaxes recentes, as chamadas perdidas e as actualizacións dos estados"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Conversa"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> enviou unha mensaxe"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> enviou unha imaxe"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Produciuse un problema ao ler o medidor da batería"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index c06826b..96bfdd7 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"સ્ક્રીનનો કોઈ ભાગ મોટો કરો"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"સ્વિચ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ઍક્સેસિબિલિટી સંકેતને ઍક્સેસિબિલિટી બટન વડે બદલવામાં આવ્યા છે\n\n"<annotation id="link">"સેટિંગ જુઓ"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"તમે ઍક્સેસિબિલિટી સંકેત પરથી કોઈ બટન પર સ્વિચ કરી શકો છો\n\n"<annotation id="link">"સેટિંગ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"તેને હંગામી રૂપે ખસેડવા માટે બટનને કિનારી પર ખસેડો"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ઉપર ડાબે ખસેડો"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ઉપર જમણે ખસેડો"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"તાજેતરના સંદેશા, ચૂકી ગયેલા કૉલ અને સ્ટેટસ અપડેટ જુઓ"</string>
<string name="people_tile_title" msgid="6589377493334871272">"વાતચીત"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા કોઈ સંદેશ મોકલવામાં આવ્યો"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા કોઈ છબી મોકલવામાં આવી"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"તમારું બૅટરી મીટર વાંચવામાં સમસ્યા આવી"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index cdead41..bf32e20 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -728,7 +728,7 @@
<string name="inline_silent_button_keep_alerting" msgid="6577845442184724992">"सूचना देना जारी रखें"</string>
<string name="inline_turn_off_notifications" msgid="8543989584403106071">"सूचनाएं बंद करें"</string>
<string name="inline_keep_showing_app" msgid="4393429060390649757">"इस ऐप्लिकेशन से जुड़ी सूचनाएं दिखाना जारी रखें?"</string>
- <string name="notification_silence_title" msgid="8608090968400832335">"आवाज़ के बिना सूचनाएं दिखाएं"</string>
+ <string name="notification_silence_title" msgid="8608090968400832335">"बिना आवाज़ के सूचनाएं दिखाएं"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"डिफ़ॉल्ट"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"अपने-आप"</string>
<string name="notification_channel_summary_low" msgid="4860617986908931158">"किसी तरह की आवाज़ या वाइब्रेशन न हो"</string>
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीन के किसी हिस्से को ज़ूम करें"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"स्विच"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"सुलभता वाले हाथ के जेस्चर (हाव-भाव) को सुलभता बटन से बदल दिया गया है\n\n"<annotation id="link">"सेटिंग देखें"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"आप सुलभता वाले हाथ के जेस्चर (हाव-भाव) के बजाय, बटन का इस्तेमाल कर सकते हैं\n\n"<annotation id="link">"सेटिंग"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"बटन को कुछ समय छिपाने के लिए, उसे किनारे पर ले जाएं"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"सबसे ऊपर बाईं ओर ले जाएं"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"सबसे ऊपर दाईं ओर ले जाएं"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"हाल के मैसेज, मिस्ड कॉल, और स्टेटस अपडेट देखें"</string>
<string name="people_tile_title" msgid="6589377493334871272">"बातचीत"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> ने एक मैसेज भेजा है"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> ने एक इमेज भेजी है"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"आपके डिवाइस के बैटरी मीटर की रीडिंग लेने में समस्या आ रही है"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 14250a7..0c039c1 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -1039,6 +1039,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Povećaj dio zaslona"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prebacivanje"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Gumb za Pristupačnost zamijenio je pokret pristupačnosti\n\n"<annotation id="link">"Prikaz postavki"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Možete prijeći s pokreta za pristupačnost na gumb\n\n"<annotation id="link">"Postavke"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Pomaknite gumb do ruba da biste ga privremeno sakrili"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Premjesti u gornji lijevi kut"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Premjesti u gornji desni kut"</string>
@@ -1149,6 +1150,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Pogledajte nedavne poruke, propuštene pozive i ažuriranja statusa"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Razgovor"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"Korisnik <xliff:g id="NAME">%1$s</xliff:g> šalje poruku"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"Korisnik <xliff:g id="NAME">%1$s</xliff:g> poslao je sliku"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem s očitavanjem mjerača baterije"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 2022281..8908b2a 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Képernyő bizonyos részének nagyítása"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Váltás"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"A kisegítő kézmozdulat helyébe a Kisegítő lehetőségek gomb lépett\n\n"<annotation id="link">"Beállítások megtekintése"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Átválthat kisegítő kézmozdulatról gomb használatára\n\n"<annotation id="link">"Beállítások"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"A gombot a szélre áthelyezve ideiglenesen elrejtheti"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Áthelyezés fel és balra"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Áthelyezés fel és jobbra"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Megtekintheti a legutóbbi üzeneteket, a nem fogadott hívásokat és az állapotfrissítéseket."</string>
<string name="people_tile_title" msgid="6589377493334871272">"Beszélgetés"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> üzenetet küldött"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> képet küldött"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Probléma merült fel az akkumulátor-töltésmérő olvasásakor"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 4fb191f..5818223 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Խոշորացնել էկրանի որոշակի հատվածը"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Փոխել"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Հատուկ գործառույթների ժեստը փոխարինվել է կոճակով\n\n"<annotation id="link">"Բացել կարգավորումները"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Դուք հատուկ գործառույթի ժեստի փոխարեն կարող եք անցնել համապատասխան կոճակի օգտագործմանը\n\n"<annotation id="link">"Կարգավորումներ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Կոճակը ժամանակավորապես թաքցնելու համար այն տեղափոխեք էկրանի եզր"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Տեղափոխել վերև՝ ձախ"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Տեղափոխել վերև՝ աջ"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Տեսեք վերջին հաղորդագրությունները, բաց թողնված զանգերը և կարգավիճակի մասին թարմացումները"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Զրույց"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> օգտատերը հաղորդագրություն է ուղարկել"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> օգտատերը պատկեր է ուղարկել"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Մարտկոցի ցուցիչի ցուցմունքը կարդալու հետ կապված խնդիր կա"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 7e4ba76..072b691 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Perbesar sebagian layar"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Alihkan"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Tombol aksesibilitas menggantikan gestur aksesibilitas\n\n"<annotation id="link">"Tampilkan setelan"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Anda dapat beralih dari gestur aksesibilitas ke tombol\n\n"<annotation id="link">"Setelan"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Pindahkan tombol ke tepi agar tersembunyi untuk sementara"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Pindahkan ke kiri atas"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Pindahkan ke kanan atas"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Lihat pesan terbaru, panggilan tak terjawab, dan pembaruan status"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Percakapan"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> mengirim pesan"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> mengirim gambar"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Terjadi masalah saat membaca indikator baterai"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 229bcbb..9d54e6f 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Stækka hluta skjásins"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Rofi"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Aðgengishnappur kom í stað aðgengisbendingar\n\n"<annotation id="link">"Skoða stillingar"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Þú getur skipt úr aðgengisbendingu yfir í hnapp\n\n"<annotation id="link">"Stillingar"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Færðu hnappinn að brúninni til að fela hann tímabundið"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Færa efst til vinstri"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Færa efst til hægri"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Sjá nýleg skilboð, ósvöruð símtöl og stöðuuppfærslur"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Samtal"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> sendi skilaboð"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> sendi mynd"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Vandamál við að lesa stöðu rafhlöðu"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 6c1ef8a..3063e84 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ingrandisci parte dello schermo"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Opzione"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Il pulsante Accessibilità ha sostituito il gesto di accessibilità\n\n"<annotation id="link">"Visualizza le impostazioni"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Puoi passare dal gesto di accessibilità a un pulsante\n\n"<annotation id="link">"Impostazioni"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Sposta il pulsante fino al bordo per nasconderlo temporaneamente"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Sposta in alto a sinistra"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Sposta in alto a destra"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"+<xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Visualizza messaggi recenti, chiamate senza risposta e aggiornamenti dello stato"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Conversazione"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> ha inviato un messaggio"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> ha inviato un\'immagine"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problema durante la lettura dell\'indicatore di livello della batteria"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index aae58cb..14b7c64 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -721,7 +721,7 @@
<string name="notification_channel_unsilenced" msgid="94878840742161152">"ההודעות אלה יישלחו כהתראות"</string>
<string name="inline_blocking_helper" msgid="2891486013649543452">"ההתראות האלה בדרך כלל נדחות על ידך. \nלהמשיך להציג אותן?"</string>
<string name="inline_done_button" msgid="6043094985588909584">"סיום"</string>
- <string name="inline_ok_button" msgid="603075490581280343">"החלה"</string>
+ <string name="inline_ok_button" msgid="603075490581280343">"אישור"</string>
<string name="inline_keep_showing" msgid="8736001253507073497">"שנמשיך להציג לך את ההתראות האלה?"</string>
<string name="inline_stop_button" msgid="2453460935438696090">"לא, אל תמשיכו"</string>
<string name="inline_deliver_silently_button" msgid="2714314213321223286">"הצגה ללא צליל"</string>
@@ -909,7 +909,7 @@
<string-array name="clock_options">
<item msgid="3986445361435142273">"הצגת שעות, דקות ושניות"</item>
<item msgid="1271006222031257266">"הצגת שעות ודקות (ברירת מחדל)"</item>
- <item msgid="6135970080453877218">"לא להציג את הסמל הזה"</item>
+ <item msgid="6135970080453877218">"בלי הסמל הזה"</item>
</string-array>
<string-array name="battery_options">
<item msgid="7714004721411852551">"תמיד להציג באחוזים"</item>
@@ -1044,6 +1044,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"הגדלת חלק מהמסך"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"מעבר"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"לחצן הנגישות החליף את תנועת הנגישות\n\n"<annotation id="link">"להצגת ההגדרות"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"ניתן להחליף את תנועת הנגישות בלחצן\n\n"<annotation id="link">"הגדרות"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"כדי להסתיר זמנית את הלחצן, יש להזיז אותו לקצה"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"העברה לפינה השמאלית העליונה"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"העברה לפינה הימנית העליונה"</string>
@@ -1155,6 +1156,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"ההודעות האחרונות, שיחות שלא נענו ועדכוני סטטוס"</string>
<string name="people_tile_title" msgid="6589377493334871272">"שיחה"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> שלח/ה הודעה"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> שלח/ה תמונה"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"בעיה בקריאת מדדי הסוללה"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 2ba73b3..d320625 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"画面の一部を拡大します"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"スイッチ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ユーザー補助ジェスチャーに代わって、ユーザー補助機能ボタンが導入されました\n\n"<annotation id="link">"設定を表示"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"ユーザー補助操作からボタンに切り替えることができます\n\n"<annotation id="link">"設定"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ボタンを一時的に非表示にするには、端に移動させてください"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"左上に移動"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"右上に移動"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g> 件以上"</string>
<string name="people_tile_description" msgid="8154966188085545556">"最近のメッセージ、不在着信、最新のステータスが表示されます"</string>
<string name="people_tile_title" msgid="6589377493334871272">"会話"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> さんからメッセージが届きました"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> さんが画像を送信しました"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"電池残量の読み込み中に問題が発生しました"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 1f071a2..c7a52e99 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ეკრანის ნაწილის გადიდება"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"გადართვა"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"მარტივი წვდომის ღილაკმა ჩაანაცვლა მარტივი წვდომის ჟესტი\n\n"<annotation id="link">"პარამეტრების ნახვა"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"შეგიძლიათ, გადართოთ მარტივი წვდომის ჟესტიდან ღილაკის\n\n"<annotation id="link">"პარამეტრებზე"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"გადაიტანეთ ღილაკი კიდეში, რათა დროებით დამალოთ ის"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ზევით და მარცხნივ გადატანა"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ზევით და მარჯვნივ გადატანა"</string>
@@ -1143,6 +1144,7 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"ბოლოდროინდელი შეტყობინებების, გამოტოვებული ზარების და სტატუსის განახლებების ნახვა"</string>
<string name="people_tile_title" msgid="6589377493334871272">"მიმოწერა"</string>
+ <string name="paused_by_dnd" msgid="7856941866433556428">"დაპაუზებულია ფუნქციის „არ შემაწუხოთ“ მიერ"</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g>-მ(ა) შეტყობინება გამოგზავნა"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g>-მ(ა) სურათი გამოგზავნა"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"თქვენი ბატარეის მზომის წაკითხვასთან დაკავშირებით პრობლემა დაფიქსირდა"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 9ab5d80..5abd6c7 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -664,7 +664,7 @@
<string name="enable_demo_mode" msgid="3180345364745966431">"Демо режимін қосу"</string>
<string name="show_demo_mode" msgid="3677956462273059726">"Демо режимін көрсету"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
- <string name="status_bar_alarm" msgid="87160847643623352">"Дабыл"</string>
+ <string name="status_bar_alarm" msgid="87160847643623352">"Оятқыш"</string>
<string name="wallet_title" msgid="5369767670735827105">"Әмиян"</string>
<string name="wallet_empty_state_label" msgid="7776761245237530394">"Телефоныңызбен бұрынғыдан да жылдам әрі қауіпсіз сатып алу үшін параметрлерді орнатыңыз."</string>
<string name="wallet_app_button_label" msgid="7123784239111190992">"Барлығын көрсету"</string>
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Экранның бөлігін ұлғайту"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Ауысу"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Арнайы мүмкіндіктер қимылының орнына \"Арнайы мүмкіндіктер\" түймесі болады.\n\n"<annotation id="link">"Параметрлерді көру"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Арнайы мүмкіндіктер қимылынан арнайы мүмкіндіктер түймесіне ауысуыңызға болады.\n\n"<annotation id="link">"Параметрлер"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Түймені уақытша жасыру үшін оны шетке қарай жылжытыңыз."</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Жоғарғы сол жаққа жылжыту"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Жоғарғы оң жаққа жылжыту"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Соңғы хабарларды, өткізіп алған қоңыраулар мен жаңартылған күйлерді көруге болады."</string>
<string name="people_tile_title" msgid="6589377493334871272">"Әңгіме"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> хабар жіберді."</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> сурет жіберді."</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Батарея зарядының дерегі алынбай жатыр"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 6ce7300..3291ada 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ពង្រីកផ្នែកនៃអេក្រង់"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ប៊ូតុងបិទបើក"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ប៊ូតុងភាពងាយស្រួលបានជំនួសចលនាភាពងាយស្រួល\n\n"<annotation id="link">"មើលការកំណត់"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"អ្នកអាចប្ដូរពីចលនាភាពងាយស្រួលទៅប៊ូតុង\n\n"<annotation id="link">"ការកំណត់"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ផ្លាស់ទីប៊ូតុងទៅគែម ដើម្បីលាក់វាជាបណ្ដោះអាសន្ន"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ផ្លាស់ទីទៅខាងលើផ្នែកខាងឆ្វេង"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ផ្លាស់ទីទៅខាងលើផ្នែកខាងស្ដាំ"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"មើលព័ត៌មានថ្មីៗអំពីស្ថានភាព ការខកខានទទួល និងសារថ្មីៗ"</string>
<string name="people_tile_title" msgid="6589377493334871272">"ការសន្ទនា"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> បានផ្ញើសារ"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> បានផ្ញើរូបភាព"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"មានបញ្ហាក្នុងការអានឧបករណ៍រង្វាស់កម្រិតថ្មរបស់អ្នក"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 78d086f..c923550 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ಸ್ಕ್ರೀನ್ನ ಅರ್ಧಭಾಗವನ್ನು ಝೂಮ್ ಮಾಡಿ"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ಸ್ವಿಚ್"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ಪ್ರವೇಶಿಸುವಿಕೆ ಬಟನ್, ಪ್ರವೇಶಿಸುವಿಕೆ ಗೆಸ್ಚರ್ ಅನ್ನು ಬದಲಾಯಿಸಿದೆ\n\n"<annotation id="link">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"ನೀವು ಪ್ರವೇಶಿಸುವಿಕೆ ಗೆಸ್ಚರ್ನಿಂದ ಬಟನ್ಗೆ ಬದಲಾಯಿಸಬಹುದು\n\n"<annotation id="link">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ಅದನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ಮರೆಮಾಡಲು ಅಂಚಿಗೆ ಬಟನ್ ಸರಿಸಿ"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ಎಡ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ಬಲ ಮೇಲ್ಭಾಗಕ್ಕೆ ಸರಿಸಿ"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"ಇತ್ತೀಚಿನ ಸಂದೇಶಗಳು, ಮಿಸ್ಡ್ ಕಾಲ್ಗಳು ಮತ್ತು ಸ್ಥಿತಿ ಅಪ್ಡೇಟ್ಗಳು"</string>
<string name="people_tile_title" msgid="6589377493334871272">"ಸಂಭಾಷಣೆ"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> ಸಂದೇಶವನ್ನು ಕಳುಹಿಸಿದ್ದಾರೆ"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> ಅವರು ಚಿತ್ರವನ್ನು ಕಳುಹಿಸಿದ್ದಾರೆ"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"ನಿಮ್ಮ ಬ್ಯಾಟರಿ ಮೀಟರ್ ಓದುವಾಗ ಸಮಸ್ಯೆ ಎದುರಾಗಿದೆ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 819250c4..5a1e57a 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"화면 일부 확대"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"전환"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"접근성 동작이 접근성 버튼으로 대체되었습니다\n\n"<annotation id="link">"설정 보기"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"접근성 동작을 버튼으로 전환할 수 있습니다.\n\n"<annotation id="link">"설정"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"버튼을 가장자리로 옮겨서 일시적으로 숨기세요"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"왼쪽 상단으로 이동"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"오른쪽 상단으로 이동"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"최근 메시지, 부재중 전화, 상태 업데이트 보기"</string>
<string name="people_tile_title" msgid="6589377493334871272">"대화"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g>님이 메시지를 보냈습니다."</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g>님이 이미지를 보냈습니다."</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"배터리 수준을 읽는 중에 문제가 발생함"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index b5bbf21..13609cf 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Экрандын бир бөлүгүн чоңойтуу"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Которулуу"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Атайын мүмкүнчүлүктөр жаңсоосунун ордуна атайын мүмкүнчүлүктөр баскычы колдонулмакчы\n\n"<annotation id="link">"Жөндөөлөрдү көрүү"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Атайын мүмкүнчүлүктөр жаңсоосунан баскычка которула аласыз\n\n"<annotation id="link">"Жөндөөлөр"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Баскычты убактылуу жашыра туруу үчүн экрандын четине жылдырыңыз"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Жогорку сол жакка жылдыруу"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Жогорку оң жакка жылдырыңыз"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Акыркы билдирүүлөрдү, жооп берилбеген чалууларды жана статустардын жаңырганын көрөсүз"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Сүйлөшүү"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> билдирүү жөнөттү"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> сүрөт жөнөттү"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Батареяңыздын кубаты аныкталбай жатат"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index ca4122e..f708b98 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ຂະຫຍາຍບາງສ່ວນຂອງໜ້າຈໍ"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ສະຫຼັບ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ປຸ່ມການຊ່ວຍເຂົ້າເຖິງຖືກແທນທີ່ທ່າທາງຊ່ວຍເຂົ້າເຖິງແລ້ວ\n\n"<annotation id="link">"ເບິ່ງການຕັ້ງຄ່າ"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"ທ່ານສາມາດສະຫຼັບທ່າທາງການຊ່ວຍເຂົ້າເຖິງເປັນປຸ່ມໄດ້\n\n"<annotation id="link">"ການຕັ້ງຄ່າ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ຍ້າຍປຸ່ມໄປໃສ່ຂອບເພື່ອເຊື່ອງມັນຊົ່ວຄາວ"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ຍ້າຍຊ້າຍເທິງ"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ຍ້າຍຂວາເທິງ"</string>
@@ -1143,6 +1144,7 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"ເບິ່ງຂໍ້ຄວາມຫຼ້າສຸດ, ສາຍບໍ່ໄດ້ຮັບ ແລະ ອັບເດດສະຖານະ"</string>
<string name="people_tile_title" msgid="6589377493334871272">"ການສົນທະນາ"</string>
+ <string name="paused_by_dnd" msgid="7856941866433556428">"ຢຸດຊົ່ວຄາວແລ້ວໂດຍໂໝດຫ້າມລົບກວນ"</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> ສົ່ງຂໍ້ຄວາມແລ້ວ"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> ສົ່ງຮູບພາບແລ້ວ"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"ເກີດບັນຫາໃນການອ່ານຕົວວັດແທກແບັດເຕີຣີຂອງທ່ານ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 65a41bb..0da285da 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -1044,6 +1044,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Didinti ekrano dalį"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Perjungti"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Pritaikomumo gestas pakeistas pritaikomumo mygtuku\n\n"<annotation id="link">"Žr. nustatymus"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Galite pereiti nuo pritaikomumo gesto prie mygtuko.\n\n"<annotation id="link">"Nustatymai"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Perkelkite mygtuką prie krašto, kad laikinai jį paslėptumėte"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Perkelti į viršų kairėje"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Perkelti į viršų dešinėje"</string>
@@ -1155,6 +1156,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g> +"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Peržiūrėkite naujausius pranešimus, praleistus skambučius ir būsenos atnaujinimus"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Pokalbis"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> išsiuntė pranešimą"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> išsiuntė vaizdą"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Nuskaitant akumuliatoriaus skaitiklį iškilo problema"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 13de036..abd59e8 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -1039,6 +1039,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Palielināt ekrāna daļu"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Pārslēgt"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Pieejamības žests ir aizstāts ar pieejamības pogu\n\n"<annotation id="link">"Skatīt iestatījumus"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Varat pārslēgties no pieejamības žesta uz pogu.\n\n"<annotation id="link">"Iestatījumi"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Lai īslaicīgi paslēptu pogu, pārvietojiet to uz malu"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Pārvietot augšpusē pa kreisi"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Pārvietot augšpusē pa labi"</string>
@@ -1149,6 +1150,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Skatiet jaunākos ziņojumus, neatbildētos zvanus un statusa atjauninājumus."</string>
<string name="people_tile_title" msgid="6589377493334871272">"Saruna"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> nosūtīja ziņojumu"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> nosūtīja attēlu"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Nevar iegūt informāciju par akumulatora uzlādes līmeni."</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index b70e2e5..545c42c 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Зголемувајте дел од екранот"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Префрли"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Копчето за пристапност го замени движењето за пристапност\n\n"<annotation id="link">"Прикажи поставки"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Може да го смените движењето за пристапност во копче\n\n"<annotation id="link">"Поставки"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Преместете го копчето до работ за да го сокриете привремено"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Премести горе лево"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Премести горе десно"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Видете ги неодамнешните пораки, пропуштени повици и промени на статусот"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Разговор"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> испрати порака"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> испрати слика"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Проблем при читањето на мерачот на батеријата"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 746d6f4..d7dc7d8 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"സ്ക്രീനിന്റെ ഭാഗം മാഗ്നിഫൈ ചെയ്യുക"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"മാറുക"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ഉപയോഗസഹായി ജെസ്ച്ചറിനെ മാറ്റി പകരം ഉപയോഗസഹായി ബട്ടൺ വന്നു\n\n"<annotation id="link">"ക്രമീകരണം കാണുക"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"നിങ്ങൾക്ക് ഉപയോഗസഹായി ജെസ്ച്ചറിൽ നിന്ന് ഒരു ബട്ടണിലേക്ക് മാറാനാകും\n\n"<annotation id="link">"ക്രമീകരണം"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"തൽക്കാലം മറയ്ക്കുന്നതിന് ബട്ടൺ അരുകിലേക്ക് നീക്കുക"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"മുകളിൽ ഇടതുഭാഗത്തേക്ക് നീക്കുക"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"മുകളിൽ വലതുഭാഗത്തേക്ക് നീക്കുക"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"അടുത്തിടെയുള്ള സന്ദേശങ്ങൾ, മിസ്ഡ് കോൾ, സ്റ്റാറ്റസ് അപ്ഡേറ്റുകൾ എന്നിവ കാണൂ"</string>
<string name="people_tile_title" msgid="6589377493334871272">"സംഭാഷണം"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g>, ഒരു സന്ദേശം അയച്ചു"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g>, ഒരു ചിത്രം അയച്ചു"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"നിങ്ങളുടെ ബാറ്ററി മീറ്റർ വായിക്കുന്നതിൽ പ്രശ്നമുണ്ട്"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 9732fbd..897e074 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Дэлгэцийн нэг хэсгийг томруулах"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Сэлгэх"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Хандалтын товчлуурыг хандалтын зангаагаар сольсон\n\n"<annotation id="link">"Тохиргоо харах"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Та хандалтын зангаанаас товчлуур луу сэлгэх боломжтой\n\n"<annotation id="link">"Тохиргоо"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Үүнийг түр нуухын тулд товчлуурыг зах руу зөөнө үү"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Зүүн дээш зөөх"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Баруун дээш зөөх"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Саяхны мессеж, аваагүй дуудлага болон төлөвийн шинэчлэлтийг харах"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Харилцан яриа"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> мессеж илгээсэн"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> зураг илгээсэн"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Таны батарей хэмжигчийг уншихад асуудал гарлаа"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index cc7f740..8737b94 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीनचा काही भाग मॅग्निफाय करा"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"स्विच करा"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"अॅक्सेसिबिलिटी जेश्चर हे आता अॅक्सेसिबिलिटी बटण आहे \n\n"<annotation id="link">"सेटिंग्ज पाहा"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"तुम्ही अॅक्सेसिबिलिटी जेश्चरवरून बटणवर स्विच करू शकता \n\n"<annotation id="link">"सेटिंग्ज"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"बटण तात्पुरते लपवण्यासाठी ते कोपर्यामध्ये हलवा"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"वर डावीकडे हलवा"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"वर उजवीकडे हलवा"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"अलीकडील मेसेज, मिस्ड कॉल आणि स्टेटस अपडेट पाहा"</string>
<string name="people_tile_title" msgid="6589377493334871272">"संभाषण"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> यांनी मेसेज पाठवला"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> यांनी इमेज पाठवली"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"तुमचे बॅटरी मीटर वाचताना समस्या आली"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 1f97ef6..8a3b4ce 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Besarkan sebahagian skrin"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Tukar"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Butang kebolehaksesan menggantikan gerak isyarat kebolehaksesan\n\n"<annotation id="link">"Lihat tetapan"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Anda boleh beralih daripada gerak isyarat kebolehaksesan kepada butang\n\n"<annotation id="link">"Tetapan"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Gerakkan butang ke tepi untuk disembunyikan buat sementara waktu"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Alihkan ke atas sebelah kiri"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Alihkan ke atas sebelah kanan"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Lihat mesej terbaharu, panggilan terlepas dan kemaskinian status"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Perbualan"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> menghantar mesej"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> menghantar imej"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Masalah membaca meter bateri anda"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 78bc991..af80198 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -39,7 +39,7 @@
<string name="battery_saver_start_action" msgid="4553256017945469937">"ဘက်ထရီ အားထိန်းကို ဖွင့်ရန်"</string>
<string name="status_bar_settings_settings_button" msgid="534331565185171556">"အပြင်အဆင်များ"</string>
<string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"Wi-Fi"</string>
- <string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"မျက်နှာပြင်အလိုအလျောက်လှည့်ရန်"</string>
+ <string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"ဖန်သားပြင် အလိုအလျောက်လှည့်ရန်"</string>
<string name="status_bar_settings_mute_label" msgid="914392730086057522">"MUTE"</string>
<string name="status_bar_settings_auto_brightness_label" msgid="2151934479226017725">"AUTO"</string>
<string name="status_bar_settings_notifications" msgid="5285316949980621438">"အကြောင်းကြားချက်များ"</string>
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ဖန်သားပြင် တစ်စိတ်တစ်ပိုင်းကို ချဲ့ပါ"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ခလုတ်"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"အများသုံးစွဲနိုင်မှုခလုတ်က အများသုံးစွဲနိုင်မှုလက်ဟန်ကို အစားထိုးသည်\n\n"<annotation id="link">"ဆက်တင်များကို ကြည့်ပါ"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"အများသုံးစွဲနိုင်မှု လက်ဟန်မှ ခလုတ်သို့ ပြောင်းနိုင်သည်\n\n"<annotation id="link">"ဆက်တင်များ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ခလုတ်ကို ယာယီဝှက်ရန် အစွန်းသို့ရွှေ့ပါ"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ဘယ်ဘက်ထိပ်သို့ ရွှေ့ရန်"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ညာဘက်ထိပ်သို့ ရွှေ့ရန်"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"မကြာသေးမီက မက်ဆေ့ဂျ်၊ လွတ်သွားသောခေါ်ဆိုမှုနှင့် အခြေအနေအပ်ဒိတ်များကို ကြည့်နိုင်သည်"</string>
<string name="people_tile_title" msgid="6589377493334871272">"စကားဝိုင်း"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> က မက်ဆေ့ဂျ်ပို့လိုက်သည်"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> က ပုံပို့လိုက်သည်"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"သင်၏ ဘက်ထရီမီတာကို ဖတ်ရာတွင် ပြဿနာရှိနေသည်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index c181089..d6271c0 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Forstørr en del av skjermen"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Bytt"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Tilgjengelighet-knappen har erstattet tilgjengelighetsbevegelsen\n\n"<annotation id="link">"Se innstillingene"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Du kan bytte fra tilgjengelighetsbevegelsen til en knapp\n\n"<annotation id="link">"Innstillinger"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Flytt knappen til kanten for å skjule den midlertidig"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Flytt til øverst til venstre"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Flytt til øverst til høyre"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Se nylige meldinger, tapte anrop og statusoppdateringer"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Samtale"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> har sendt en melding"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> har sendt et bilde"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Kunne ikke lese batterimåleren"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index a69be5d..2159bf9 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -437,7 +437,7 @@
<string name="recents_quick_scrub_onboarding" msgid="765934300283514912">"एपहरू बदल्न द्रुत गतिमा दायाँतिर ड्र्याग गर्नुहोस्"</string>
<string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"परिदृश्य टगल गर्नुहोस्"</string>
<string name="expanded_header_battery_charged" msgid="5307907517976548448">"चार्ज भयो"</string>
- <string name="expanded_header_battery_charging" msgid="1717522253171025549">"चार्ज हुँदै"</string>
+ <string name="expanded_header_battery_charging" msgid="1717522253171025549">"चार्ज हुँदै छ"</string>
<string name="expanded_header_battery_charging_with_time" msgid="757991461445765011">"<xliff:g id="CHARGING_TIME">%s</xliff:g> पूर्ण नभएसम्म"</string>
<string name="expanded_header_battery_not_charging" msgid="809409140358955848">"चार्ज भइरहेको छैन"</string>
<string name="ssl_ca_cert_warning" msgid="8373011375250324005">"नेटवर्क \n अनुगमनमा हुन सक्छ"</string>
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रिनको केही भाग म्याग्निफाइ गर्नुहोस्"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"बदल्नुहोस्"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"एक्सेसिबिलिटी इसाराका स्थानमा एक्सेसिबिलिटी बटन प्रयोग हुन थालेको छ\n\n"<annotation id="link">"सेटिङ हेर्नुहोस्"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"तपाईं एक्सेसिबिलिटी जेस्चरको साटो बटन प्रयोग गर्न सक्नुहुन्छ\n\n"<annotation id="link">"सेटिङ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"यो बटन केही बेर नदेखिने पार्न किनारातिर सार्नुहोस्"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"सिरानको बायाँतिर सार्नुहोस्"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"सिरानको दायाँतिर सार्नुहोस्"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"हालसालैका म्यासेज, मिस कल र स्ट्याटस अपडेट हेर्नुहोस्"</string>
<string name="people_tile_title" msgid="6589377493334871272">"वार्तालाप"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> ले एउटा म्यासेज पठाउनुभयो"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> ले एउटा फोटो पठाउनुभयो"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"डिभाइसको ब्याट्रीको मिटर रिडिङ क्रममा समस्या भयो"</string>
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 8e6293a..b98694e 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -21,8 +21,8 @@
It's fine to override this color since at that point the shade was dark. -->
<color name="notification_legacy_background_color">@color/GM2_grey_900</color>
- <!-- The color of the dividing line between grouped notifications while . -->
- <color name="notification_divider_color">#212121</color>
+ <!-- The color of the dividing line between grouped notifications. -->
+ <color name="notification_divider_color">@*android:color/background_device_default_dark</color>
<!-- The color of the gear shown behind a notification -->
<color name="notification_gear_color">@color/GM2_grey_500</color>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index e91f0a0..56a95f5 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Deel van het scherm vergroten"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Schakelen"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"De knop Toegankelijkheid vervangt het toegankelijkheidsgebaar\n\n"<annotation id="link">"Instellingen bekijken"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Je kunt overschakelen van het toegankelijkheidsgebaar naar een knop\n\n"<annotation id="link">"Instellingen"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Knop naar de rand verplaatsen om deze tijdelijk te verbergen"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Naar linksboven verplaatsen"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Naar rechtsboven verplaatsen"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Bekijk recente berichten, gemiste gesprekken en statusupdates"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Gesprek"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> heeft een bericht gestuurd"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> heeft een afbeelding gestuurd"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Probleem bij het lezen van je batterijmeter"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 739885d..22fe336 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ସ୍କ୍ରିନର ଅଂଶ ମାଗ୍ନିଫାଏ କରନ୍ତୁ"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ସ୍ୱିଚ୍ କରନ୍ତୁ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ଆକ୍ସେସିବିଲିଟୀ ଜେଶ୍ଚରକୁ ଆକ୍ସେସିବିଲିଟୀ ବଟନରେ ପରିବର୍ତ୍ତନ କରାଯାଇଛି\n\n"<annotation id="link">"ସେଟିଂସ୍ ଦେଖନ୍ତୁ"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"ଆପଣ ଆକ୍ସେସିବିଲିଟୀ ଜେଶ୍ଚରରୁ ଏକ ବଟନକୁ ସ୍ୱିଚ୍ କରିପାରିବେ\n\n"<annotation id="link">"ସେଟିଂସ୍"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ବଟନକୁ ଅସ୍ଥାୟୀ ଭାବେ ଲୁଚାଇବା ପାଇଁ ଏହାକୁ ଗୋଟିଏ ଧାରକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ଶୀର୍ଷ ବାମକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ଶୀର୍ଷ ଡାହାଣକୁ ମୁଭ୍ କରନ୍ତୁ"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"ବର୍ତ୍ତମାନର ମେସେଜ୍, ମିସ୍ଡ କଲ୍ ଏବଂ ସ୍ଥିତି ଅପଡେଟଗୁଡ଼ିକୁ ଦେଖନ୍ତୁ"</string>
<string name="people_tile_title" msgid="6589377493334871272">"ବାର୍ତ୍ତାଳାପ"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> ଏକ ମେସେଜ୍ ପଠାଇଛନ୍ତି"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> ଏକ ଛବି ପଠାଇଛନ୍ତି"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"ଆପଣଙ୍କ ବ୍ୟାଟେରୀ ମିଟର୍ ପଢ଼ିବାରେ ସମସ୍ୟା ହେଉଛି"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index deca3ff..08789cd 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -734,7 +734,7 @@
<string name="notification_channel_summary_low" msgid="4860617986908931158">"ਕੋਈ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਹੀਂ"</string>
<string name="notification_conversation_summary_low" msgid="1734433426085468009">"ਕੋਈ ਧੁਨੀ ਜਾਂ ਥਰਥਰਾਹਟ ਨਹੀਂ ਅਤੇ ਸੂਚਨਾਵਾਂ ਗੱਲਬਾਤ ਸੈਕਸ਼ਨ ਵਿੱਚ ਹੇਠਲੇ ਪਾਸੇ ਦਿਸਦੀਆਂ ਹਨ"</string>
<string name="notification_channel_summary_default" msgid="3282930979307248890">"ਫ਼ੋਨ ਸੈਟਿੰਗਾਂ ਦੇ ਆਧਾਰ \'ਤੇ ਘੰਟੀ ਵੱਜ ਸਕਦੀ ਹੈ ਜਾਂ ਥਰਥਰਾਹਟ ਹੋ ਸਕਦੀ ਹੈ"</string>
- <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ਫ਼ੋਨ ਸੈਟਿੰਗਾਂ ਦੇ ਆਧਾਰ \'ਤੇ ਘੰਟੀ ਵੱਜ ਸਕਦੀ ਹੈ ਜਾਂ ਥਰਥਰਾਹਟ ਹੋ ਸਕਦੀ ਹੈ। ਪੂਰਵ-ਨਿਰਧਾਰਤ ਤੌਰ \'ਤੇ <xliff:g id="APP_NAME">%1$s</xliff:g> ਬਬਲ ਤੋਂ ਗੱਲਾਂਬਾਤਾਂ।"</string>
+ <string name="notification_channel_summary_default_with_bubbles" msgid="1782419896613644568">"ਫ਼ੋਨ ਸੈਟਿੰਗਾਂ ਦੇ ਆਧਾਰ \'ਤੇ ਘੰਟੀ ਵੱਜ ਸਕਦੀ ਹੈ ਜਾਂ ਥਰਥਰਾਹਟ ਹੋ ਸਕਦੀ ਹੈ। ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਤੌਰ \'ਤੇ <xliff:g id="APP_NAME">%1$s</xliff:g> ਤੋਂ ਗੱਲਾਂਬਾਤਾਂ ਬਬਲ ਵਜੋਂ ਦਿਸਦੀਆਂ ਹਨ।"</string>
<string name="notification_channel_summary_bubble" msgid="7235935211580860537">"ਇਸ ਸਮੱਗਰੀ ਦੇ ਅਸਥਿਰ ਸ਼ਾਰਟਕੱਟ ਨਾਲ ਆਪਣਾ ਧਿਆਨ ਕੇਂਦਰਿਤ ਰੱਖੋ।"</string>
<string name="notification_channel_summary_automatic" msgid="5813109268050235275">"ਸਿਸਟਮ ਨੂੰ ਨਿਰਧਾਰਤ ਕਰਨ ਦਿਓ ਕਿ ਇਸ ਸੂਚਨਾ ਲਈ ਕੋਈ ਧੁਨੀ ਵਜਾਉਣੀ ਚਾਹੀਦੀ ਹੈ ਜਾਂ ਥਰਥਰਾਹਟ ਕਰਨੀ ਚਾਹੀਦੀ ਹੈ"</string>
<string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>ਸਥਿਤੀ:</b> ਦਰਜਾ ਵਧਾ ਕੇ ਪੂਰਵ-ਨਿਰਧਾਰਤ \'ਤੇ ਸੈੱਟ ਕੀਤਾ ਗਿਆ"</string>
@@ -742,9 +742,9 @@
<string name="notification_channel_summary_automatic_promoted" msgid="1301710305149590426">"<b>ਸਥਿਤੀ:</b> ਦਰਜਾ ਵਧਾਇਆ ਗਿਆ"</string>
<string name="notification_channel_summary_automatic_demoted" msgid="1831303964660807700">"<b>ਸਥਿਤੀ:</b> ਦਰਜਾ ਘਟਾਇਆ ਗਿਆ"</string>
<string name="notification_channel_summary_priority_baseline" msgid="46674690072551234">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ"</string>
- <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ, ਬਬਲ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ"</string>
+ <string name="notification_channel_summary_priority_bubble" msgid="1275413109619074576">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਵਜੋਂ ਦਿਖਾਈਆਂ ਜਾਂਦੀਆਂ ਹਨ, ਜੋ ਕਿ ਬਬਲ ਵਜੋਂ ਦਿਸਦੀਆਂ ਹਨ"</string>
<string name="notification_channel_summary_priority_dnd" msgid="6665395023264154361">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ, \'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਸੁਵਿਧਾ ਵਿੱਚ ਵੀ ਵਿਘਨ ਪੈ ਸਕਦਾ ਹੈ"</string>
- <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ, ਬਬਲ ਵਜੋਂ ਦਿਖਾਉਂਦਾ ਹੈ, \'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਸੁਵਿਧਾ ਵਿੱਚ ਵੀ ਵਿਘਨ ਪੈ ਸਕਦਾ ਹੈ"</string>
+ <string name="notification_channel_summary_priority_all" msgid="7151752959650048285">"ਗੱਲਬਾਤ ਸੂਚਨਾਵਾਂ ਦੇ ਸਿਖਰ \'ਤੇ ਅਤੇ ਲਾਕ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰੋਫਾਈਲ ਤਸਵੀਰ ਵਜੋਂ ਦਿਖਾਈਆਂ ਜਾਂਦੀਆਂ ਹਨ, ਜੋ ਕਿ ਬਬਲ ਵਜੋਂ ਦਿਸਦੀਆਂ ਹਨ ਅਤੇ \'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਸੁਵਿਧਾ ਵਿੱਚ ਵਿਘਨ ਵੀ ਪਾ ਸਕਦੀਆਂ ਹਨ"</string>
<string name="notification_conversation_channel_settings" msgid="2409977688430606835">"ਸੈਟਿੰਗਾਂ"</string>
<string name="notification_priority_title" msgid="2079708866333537093">"ਤਰਜੀਹ"</string>
<string name="no_shortcut" msgid="8257177117568230126">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਐਪ ਗੱਲਬਾਤ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਦਾ ਸਮਰਥਨ ਨਹੀਂ ਕਰਦੀ"</string>
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ਸਕ੍ਰੀਨ ਦੇ ਹਿੱਸੇ ਨੂੰ ਵੱਡਾ ਕਰੋ"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ਸਵਿੱਚ"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ਪਹੁੰਚਯੋਗਤਾ ਬਟਨ ਨੂੰ ਪਹੁੰਚਯੋਗਤਾ ਸੰਕੇਤ ਨਾਲ ਬਦਲ ਦਿੱਤਾ ਗਿਆ\n\n"<annotation id="link">"ਸੈਟਿੰਗਾਂ ਦੇਖੋ"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"ਤੁਸੀਂ ਪਹੁੰਚਯੋਗਤਾ ਇਸ਼ਾਰੇ ਤੋਂ ਬਟਨ \'ਤੇ ਸਵਿੱਚ ਕਰ ਸਕਦੇ ਹੋ\n\n"<annotation id="link">"ਸੈਟਿੰਗਾਂ"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ਬਟਨ ਨੂੰ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਲੁਕਾਉਣ ਲਈ ਕਿਨਾਰੇ \'ਤੇ ਲਿਜਾਓ"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ਉੱਪਰ ਵੱਲ ਖੱਬੇ ਲਿਜਾਓ"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ਉੱਪਰ ਵੱਲ ਸੱਜੇ ਲਿਜਾਓ"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"ਹਾਲੀਆ ਸੁਨੇਹੇ, ਮਿਸ ਕਾਲਾਂ ਅਤੇ ਸਥਿਤੀ ਸੰਬੰਧੀ ਅੱਪਡੇਟ ਦੇਖੋ"</string>
<string name="people_tile_title" msgid="6589377493334871272">"ਗੱਲਬਾਤ"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> ਨੇ ਇੱਕ ਸੁਨੇਹਾ ਭੇਜਿਆ"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> ਨੇ ਇੱਕ ਚਿੱਤਰ ਭੇਜਿਆ ਹੈ"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"ਤੁਹਾਡੇ ਬੈਟਰੀ ਮੀਟਰ ਨੂੰ ਪੜ੍ਹਨ ਵਿੱਚ ਸਮੱਸਿਆ ਹੋ ਰਹੀ ਹੈ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 217b2f2..f1a8a84 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -1044,6 +1044,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Powiększ część ekranu"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Przełącz"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Przycisk ułatwień dostępu zastąpił gest ułatwień dostępu\n\n"<annotation id="link">"Wyświetl ustawienia"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Możesz przełączyć się z gestu na przycisk ułatwień dostępu.\n\n"<annotation id="link">"Ustawienia"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Przesuń przycisk do krawędzi, aby ukryć go tymczasowo"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Przenieś w lewy górny róg"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Przenieś w prawy górny róg"</string>
@@ -1155,6 +1156,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"+ <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Zobacz ostatnie wiadomości, nieodebrane połączenia i stany"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Rozmowa"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> wysyła wiadomość"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> wysyła zdjęcie"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem z odczytaniem pomiaru wykorzystania baterii"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 6077c54..aa3d628 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte da tela"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Trocar"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"O botão de acessibilidade substituiu o gesto de acessibilidade\n\n"<annotation id="link">"Veja as configurações"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Você pode mudar do gesto de acessibilidade para um botão\n\n"<annotation id="link">"Configurações"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mova o botão para a borda para ocultá-lo temporariamente"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover para o canto superior esquerdo"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover para o canto superior direito"</string>
@@ -1143,6 +1144,7 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Veja mensagens recentes, chamadas perdidas e atualizações de status"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Conversa"</string>
+ <string name="paused_by_dnd" msgid="7856941866433556428">"Pausado pelo Não perturbe"</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> enviou uma mensagem"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> enviou uma imagem"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problema para ler seu medidor de bateria"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 2e7c629..22b2eb0 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte do ecrã"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Mudar"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"O botão Acessibilidade substituiu o gesto de acessibilidade\n\n"<annotation id="link">"Ver definições"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Pode mudar de um gesto de acessibilidade para um botão\n\n"<annotation id="link">"Definições"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mova o botão para a extremidade para o ocultar temporariamente"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover p/ parte sup. esquerda"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover parte superior direita"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Veja mensagens recentes, chamadas não atendidas e atualizações de estado"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Conversa"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> enviou uma mensagem"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> enviou uma imagem"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Ocorreu um problema ao ler o medidor da bateria"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 6077c54..aa3d628 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte da tela"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Trocar"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"O botão de acessibilidade substituiu o gesto de acessibilidade\n\n"<annotation id="link">"Veja as configurações"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Você pode mudar do gesto de acessibilidade para um botão\n\n"<annotation id="link">"Configurações"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mova o botão para a borda para ocultá-lo temporariamente"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mover para o canto superior esquerdo"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mover para o canto superior direito"</string>
@@ -1143,6 +1144,7 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Veja mensagens recentes, chamadas perdidas e atualizações de status"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Conversa"</string>
+ <string name="paused_by_dnd" msgid="7856941866433556428">"Pausado pelo Não perturbe"</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> enviou uma mensagem"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> enviou uma imagem"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problema para ler seu medidor de bateria"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index d6c3756..35a976a 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -1039,6 +1039,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Măriți o parte a ecranului"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Comutator"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Butonul de accesibilitate a înlocuit gestul de accesibilitate\n\n"<annotation id="link">"Vedeți setările"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Puteți trece de la gestul de accesibilitate la un buton\n\n"<annotation id="link">"Setări"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mutați butonul spre margine pentru a-l ascunde temporar"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mutați în stânga sus"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mutați în dreapta sus"</string>
@@ -1149,6 +1150,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Vedeți mesaje recente, apeluri pierdute și actualizări de stare"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Conversație"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> a trimis un mesaj"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> a trimis o imagine"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problemă la citirea măsurării bateriei"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 33f47de..ec07b3c 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -1044,6 +1044,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увеличить часть экрана"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Переключить"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Жест заменен на кнопку специальных возможностей\n\n"<annotation id="link">"Открыть настройки"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Вы можете использовать кнопку вместо жеста.\n\n"<annotation id="link">"Настройки"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Чтобы временно скрыть кнопку, переместите ее к краю экрана"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Перенести в левый верхний угол"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Перенести в правый верхний угол"</string>
@@ -1155,6 +1156,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Будьте в курсе последних сообщений, пропущенных вызовов и обновлений статуса."</string>
<string name="people_tile_title" msgid="6589377493334871272">"Чат"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"Пользователь <xliff:g id="NAME">%1$s</xliff:g> отправил сообщение"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"Пользователь <xliff:g id="NAME">%1$s</xliff:g> отправил изображение"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Не удается получить данные об уровне заряда батареи"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index cc27dfb..e81c661 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"තිරයේ කොටසක් විශාලනය කරන්න"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ස්විචය"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ප්රවේශ්යතා බොත්තම ප්රවේශ්යතා ඉංගිතය ප්රතිස්ථාපනය කළේය\n\n"<annotation id="link">"සැකසීම් බලන්න"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"ඔබට ප්රවේශ්යතා ඉංගිතයෙන් බොත්තම් \n\n"<annotation id="link">"සැකසීම්වලට"</annotation>" මාරු විය හැකිය"</string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"එය තාවකාලිකව සැඟවීමට බොත්තම දාරයට ගෙන යන්න"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ඉහළ වමට ගෙන යන්න"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ඉහළ දකුණට ගෙන යන්න"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"මෑත පණිවිඩ, මඟ හැරුණු ඇමතුම් සහ තත්ත්ව යාවත්කාලීන කිරීම් බලන්න"</string>
<string name="people_tile_title" msgid="6589377493334871272">"සංවාදය"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> පණිවිඩයක් එවා ඇත"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> රූපයක් යවන ලදී"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"ඔබගේ බැටරි මනුව කියවීමේ දෝෂයකි"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 0b74313..3b2d174 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -1044,6 +1044,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zväčšiť časť obrazovky"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prepnúť"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Tlačidlo dostupnosti nahradilo gesto dostupnosti\n\n"<annotation id="link">"Zobraziť nastavenia"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Môžete prepnúť z gesta dostupnosti na tlačidlo\n\n"<annotation id="link">"Nastavenia"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Ak chcete tlačidlo dočasne skryť, presuňte ho k okraju"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Presunúť doľava nahor"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Presunúť doprava nahor"</string>
@@ -1155,6 +1156,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Pozrite si nedávne správy, zmeškané hovory a aktualizácie stavu"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Konverzácia"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> poslal(a) správu"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> poslal(a) obrázok"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Pri čítaní meradla batérie sa vyskytol problém"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index b4729f4..b21b342 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -1044,6 +1044,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Povečava dela zaslona"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Stikalo"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Gumb za funkcije za ljudi s posebnimi potrebami je zamenjal pripadajočo potezo.\n\n"<annotation id="link">"Ogled nastavitev"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Preklopite lahko s poteze na gumb za funkcije z ljudmi za posebne potrebe.\n\n"<annotation id="link">"Nastavitve"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Če želite gumb začasno skriti, ga premaknite ob rob."</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Premakni zgoraj levo"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Premakni zgoraj desno"</string>
@@ -1155,6 +1156,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"Več kot <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Ogled nedavnih sporočil, neodgovorjenih klicev in posodobitev stanj"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Pogovor"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"Oseba <xliff:g id="NAME">%1$s</xliff:g> je poslala sporočilo."</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"Oseba <xliff:g id="NAME">%1$s</xliff:g> je poslala sliko."</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Težava z branjem indikatorja stanja napolnjenosti baterije"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 3ec14ea..dda9b6c0 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zmadho një pjesë të ekranit"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Ndërro"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Butoni i qasshmërisë zëvendësoi gjestin e qasshmërisë\n\n"<annotation id="link">"Shiko cilësimet"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Mund të kalosh nga gjesti i qasshmërisë te një buton\n\n"<annotation id="link">"Cilësimet"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Zhvendose butonin në skaj për ta fshehur përkohësisht"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Zhvendos lart majtas"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Zhvendos lart djathtas"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"Mbi <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Shiko mesazhet e fundit, telefonatat e humbura dhe përditësimet e statusit"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Biseda"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> dërgoi një mesazh"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> dërgoi një imazh"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem me leximin e matësit të baterisë"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 70fcb26..4a95efb 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -1039,6 +1039,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увећајте део екрана"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Пређи"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Дугме Приступачност је заменило покрет за приступачност\n\n"<annotation id="link">"Прикажи подешавања"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Можете да пређете са покрета за приступачност на дугме\n\n"<annotation id="link">"Подешавања"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Померите дугме до ивице да бисте га привремено сакрили"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Премести горе лево"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Премести горе десно"</string>
@@ -1149,6 +1150,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Погледајте недавне поруке, пропуштене позиве и ажурирања статуса"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Конверзација"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> шаље поруку"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> шаље слику"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Проблем са очитавањем мерача батерије"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 6233370..322efd8 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Förstora en del av skärmen"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Reglage"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Tillgänglighetsknappen har ersatt tillgänglighetsrörelsen\n\n"<annotation id="link">"Visa inställningarna"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Du kan byta från tillgänglighetsrörelsen till en knapp\n\n"<annotation id="link">"Inställningar"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Flytta knappen till kanten för att dölja den tillfälligt"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Flytta högst upp till vänster"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Flytta högst upp till höger"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"över <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Se de senaste meddelandena, missade samtal och statusuppdateringar"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Konversation"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> skickade ett meddelande"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> skickade en bild"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Batteriindikatorn visas inte"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index f7e50ef..36d7189 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -349,7 +349,7 @@
<string name="quick_settings_rotation_locked_portrait_label" msgid="1194988975270484482">"Wima"</string>
<string name="quick_settings_rotation_locked_landscape_label" msgid="2000295772687238645">"Mlalo"</string>
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Mbinu ya uingizaji"</string>
- <string name="quick_settings_location_label" msgid="2621868789013389163">"Kutambua Mahali"</string>
+ <string name="quick_settings_location_label" msgid="2621868789013389163">"Mahali"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Kitambua eneo kimezimwa"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Ufikiaji wa kamera"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Ufikiaji wa maikrofoni"</string>
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Kuza sehemu ya skrini"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Swichi"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Kitufe cha zana za ufikivu kimechukua nafasi ya ishara ya ufikivu\n\n"<annotation id="link">"Angalia mipangilio"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Unaweza kubadilisha uache kutumia ishara ya ufikivu ili utumie kitufe\n\n"<annotation id="link">"Mipangilio"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Sogeza kitufe kwenye ukingo ili ukifiche kwa muda"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Sogeza juu kushoto"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Sogeza juu kulia"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Angalia ujumbe wa hivi majuzi, simu ambazo hukujibu na taarifa za hali"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Mazungumzo"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> ametuma ujumbe"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> ametuma picha"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Tatizo la kusoma mita ya betri yako"</string>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 9f8e636..da80b85 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -36,10 +36,6 @@
<!-- On tablets this is just the close_handle_height -->
<dimen name="peek_height">@dimen/close_handle_height</dimen>
- <!-- The margin between the clock and the notifications on Keyguard. See
- keyguard_clock_height_fraction_* for the difference between min and max.-->
- <dimen name="keyguard_clock_notifications_margin">44dp</dimen>
-
<!-- Height of the status bar header bar when on Keyguard -->
<dimen name="status_bar_header_height_keyguard">60dp</dimen>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index df52b0e..44d9441 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"திரையின் ஒரு பகுதியைப் பெரிதாக்கும்"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ஸ்விட்ச்"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"அணுகல்தன்மை பட்டன் இப்போது அணுகல்தன்மை சைகையாக மாற்றப்பட்டுள்ளது\n\n"<annotation id="link">"அமைப்புகளில் காண்க"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"அணுகல்தன்மை சைகையிலிருந்து பட்டனுக்கு மாறிக்கொள்ளலாம்\n\n"<annotation id="link">"அமைப்புகள்"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"பட்டனைத் தற்காலிகமாக மறைக்க ஓரத்திற்கு நகர்த்தும்"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"மேலே இடதுபுறத்திற்கு நகர்த்து"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"மேலே வலதுபுறத்திற்கு நகர்த்து"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"சமீபத்திய மெசேஜ்களையும் தவறிய அழைப்புகளையும் ஸ்டேட்டஸ் அப்டேட்களையும் பார்க்கலாம்"</string>
<string name="people_tile_title" msgid="6589377493334871272">"உரையாடல்"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> ஒரு மெசேஜ் அனுப்பியுள்ளார்"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> ஒரு படம் அனுப்பியுள்ளார்"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"பேட்டரி அளவை அறிவதில் சிக்கல்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 84b33a9..8df60b2 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"స్క్రీన్లో భాగాన్ని మాగ్నిఫై చేయండి"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"స్విచ్ చేయి"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"యాక్సెసిబిలిటీ బటన్, యాక్సెసిబిలిటీ సంజ్ఞను భర్తీ చేసింది\n\n"<annotation id="link">"సెట్టింగ్లను చూడండి"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"మీరు యాక్సెసిబిలిటీ సంజ్ఞ నుండి బటన్ మధ్య మారవచ్చు\n\n"<annotation id="link">"సెట్టింగ్లు"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"తాత్కాలికంగా దానిని దాచడానికి బటన్ను చివరకు తరలించండి"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ఎగువ ఎడమ వైపునకు తరలించు"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ఎగువ కుడి వైపునకు తరలించు"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"ఇటీవలి మెసేజ్లు, మిస్డ్ కాల్లు, అలాగే స్టేటస్ అప్డేట్లను చూడండి"</string>
<string name="people_tile_title" msgid="6589377493334871272">"సంభాషణ"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> మెసేజ్ను పంపారు"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> ఇమేజ్ను పంపారు"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"మీ బ్యాటరీ మీటర్ను చదవడంలో సమస్య"</string>
diff --git a/packages/SystemUI/res/values-television/colors.xml b/packages/SystemUI/res/values-television/colors.xml
index e5f3b47..566f6b2 100644
--- a/packages/SystemUI/res/values-television/colors.xml
+++ b/packages/SystemUI/res/values-television/colors.xml
@@ -19,4 +19,22 @@
<resources>
<color name="volume_dialog_background_color">#E61F232B</color>
<color name="volume_dialog_background_color_above_blur">#C71F232B</color>
+
+ <color name="bottom_sheet_icon_color">#D2E3FC</color>
+
+ <color name="bottom_sheet_title_color">#E8F0FE</color>
+ <color name="bottom_sheet_body_color">#D2E3FC</color>
+
+ <color name="bottom_sheet_background_color">#1F232C</color>
+ <color name="bottom_sheet_background_color_with_blur">#AA1A2734</color>
+
+ <color name="bottom_sheet_button_background_color_focused">#E8F0FE</color>
+ <color name="bottom_sheet_button_background_color_unfocused">#0FE8EAED</color>
+
+ <color name="bottom_sheet_button_text_color_focused">#DB202124</color>
+ <color name="bottom_sheet_button_text_color_unfocused">#B5E8EAED</color>
+
+ <color name="privacy_circle">#5BB974</color> <!-- g400 -->
+ <color name="privacy_icon_tint">#30302A</color>
+ <color name="privacy_chip_dot_bg_tint">#66000000</color>
</resources>
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index 70bd850..2f0957c 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -33,6 +33,7 @@
<item>com.android.systemui.statusbar.tv.notifications.TvNotificationPanel</item>
<item>com.android.systemui.statusbar.tv.notifications.TvNotificationHandler</item>
<item>com.android.systemui.statusbar.tv.VpnStatusObserver</item>
+ <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
<item>com.android.systemui.usb.StorageNotification</item>
<item>com.android.systemui.power.PowerUI</item>
<item>com.android.systemui.media.RingtonePlayer</item>
diff --git a/packages/SystemUI/res/values-television/dimens.xml b/packages/SystemUI/res/values-television/dimens.xml
index 7626db9..c258fcc 100644
--- a/packages/SystemUI/res/values-television/dimens.xml
+++ b/packages/SystemUI/res/values-television/dimens.xml
@@ -18,7 +18,42 @@
<!-- Opacity at which the background for the shutdown UI will be drawn. -->
<item name="shutdown_scrim_behind_alpha" format="float" type="dimen">1.0</item>
- <dimen name="privacy_chip_icon_margin">3dp</dimen>
- <dimen name="privacy_chip_icon_padding">8dp</dimen>
- <dimen name="privacy_chip_icon_size">13dp</dimen>
+ <dimen name="bottom_sheet_padding_horizontal">32dp</dimen>
+ <dimen name="bottom_sheet_padding_vertical">24dp</dimen>
+
+ <dimen name="bottom_sheet_icon_size">42dp</dimen>
+ <dimen name="bottom_sheet_icon_margin">8dp</dimen>
+ <dimen name="bottom_sheet_title_margin_bottom">18dp</dimen>
+ <dimen name="bottom_sheet_details_margin_bottom">8dp</dimen>
+
+ <dimen name="bottom_sheet_actions_width">296dp</dimen>
+ <dimen name="bottom_sheet_actions_spacing">12dp</dimen>
+ <item name="bottom_sheet_button_selection_scaled" format="float" type="dimen">1.1</item>
+ <dimen name="bottom_sheet_button_width">232dp</dimen>
+ <dimen name="bottom_sheet_button_padding_horizontal">20dp</dimen>
+ <dimen name="bottom_sheet_button_padding_vertical">16dp</dimen>
+
+ <dimen name="bottom_sheet_corner_radius">24dp</dimen>
+ <dimen name="bottom_sheet_button_corner_radius">10dp</dimen>
+
+ <dimen name="bottom_sheet_min_height">208dp</dimen>
+ <dimen name="bottom_sheet_margin">24dp</dimen>
+ <dimen name="bottom_sheet_background_blur_radius">120dp</dimen>
+
+ <dimen name="privacy_chip_margin">12dp</dimen>
+ <dimen name="privacy_chip_icon_margin_in_between">9dp</dimen>
+ <dimen name="privacy_chip_padding_horizontal">9dp</dimen>
+ <dimen name="privacy_chip_icon_size">12dp</dimen>
+ <dimen name="privacy_chip_height">24dp</dimen>
+ <dimen name="privacy_chip_min_width">30dp</dimen>
+ <dimen name="privacy_chip_radius">12dp</dimen>
+
+ <dimen name="privacy_chip_dot_size">8dp</dimen>
+ <dimen name="privacy_chip_dot_radius">4dp</dimen>
+ <dimen name="privacy_chip_dot_margin_horizontal">8dp</dimen>
+
+ <dimen name="privacy_chip_dot_bg_width">24dp</dimen>
+ <dimen name="privacy_chip_dot_bg_height">18dp</dimen>
+ <dimen name="privacy_chip_dot_bg_radius">9dp</dimen>
+
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-television/integers.xml b/packages/SystemUI/res/values-television/integers.xml
index 587497e..b265d78 100644
--- a/packages/SystemUI/res/values-television/integers.xml
+++ b/packages/SystemUI/res/values-television/integers.xml
@@ -20,4 +20,6 @@
Value 81 corresponds to BOTTOM|CENTER_HORIZONTAL.
Value 21 corresponds to RIGHT|CENTER_VERTICAL. -->
<integer name="volume_dialog_gravity">21</integer>
+
+ <integer name="privacy_chip_animation_millis">300</integer>
</resources>
diff --git a/packages/SystemUI/res/values-television/styles.xml b/packages/SystemUI/res/values-television/styles.xml
index 00217fb..0fb7898 100644
--- a/packages/SystemUI/res/values-television/styles.xml
+++ b/packages/SystemUI/res/values-television/styles.xml
@@ -28,4 +28,34 @@
<item name="android:textColorPrimaryInverse">@color/tv_volume_dialog_accent</item>
<item name="android:dialogCornerRadius">@dimen/volume_dialog_panel_width_half</item>
</style>
+
+ <style name="BottomSheet" parent="Theme.Leanback">
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:windowActivityTransitions">true</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:backgroundDimAmount">0.2</item>
+ </style>
+
+ <style name="BottomSheet.TitleText">
+ <item name="android:textSize">28sp</item>
+ <item name="android:textColor">@color/bottom_sheet_title_color</item>
+ </style>
+
+ <style name="BottomSheet.BodyText">
+ <item name="android:textSize">16sp</item>
+ <item name="android:textColor">@color/bottom_sheet_body_color</item>
+ </style>
+
+ <style name="BottomSheet.ActionItem">
+ <item name="android:layout_width">@dimen/bottom_sheet_button_width</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:gravity">left|center_vertical</item>
+ <item name="android:textSize">16sp</item>
+ <item name="android:textColor">@color/bottom_sheet_button_text_color</item>
+ <item name="android:background">@drawable/bottom_sheet_button_background</item>
+ <item name="android:paddingHorizontal">@dimen/bottom_sheet_button_padding_horizontal</item>
+ <item name="android:paddingVertical">@dimen/bottom_sheet_button_padding_vertical</item>
+ <item name="android:stateListAnimator">@anim/tv_bottom_sheet_button_state_list_animator</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 2b7bc58..38d2e94 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ขยายบางส่วนของหน้าจอ"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"เปลี่ยน"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ปุ่มการช่วยเหลือพิเศษแทนที่ท่าทางสัมผัสการช่วยเหลือพิเศษแล้ว\n\n"<annotation id="link">"ดูการตั้งค่า"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"คุณสลับจากท่าทางสัมผัสเพื่อการเข้าถึงพิเศษไปที่ปุ่ม\n\n"<annotation id="link">"การตั้งค่า"</annotation>"ได้"</string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"ย้ายปุ่มไปที่ขอบเพื่อซ่อนชั่วคราว"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"ย้ายไปด้านซ้ายบน"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"ย้ายไปด้านขวาบน"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"ดูข้อความล่าสุด สายที่ไม่ได้รับ และการอัปเดตสถานะ"</string>
<string name="people_tile_title" msgid="6589377493334871272">"การสนทนา"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> ส่งข้อความ"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> ส่งรูปภาพ"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"พบปัญหาในการอ่านเครื่องวัดแบตเตอรี่"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 8db4e9e..4e54a52 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"I-magnify ang isang bahagi ng screen"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Switch"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Pinalitan ng button ng accessibility ang galaw ng accessibility\n\n"<annotation id="link">"Tingnan ang mga setting"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Puwede kang lumipat sa button mula sa galaw para sa accessibility\n\n"<annotation id="link">"Mga Setting"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Ilipat ang button sa gilid para pansamantala itong itago"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Ilipat sa kaliwa sa itaas"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Ilipat sa kanan sa itaas"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Tingnan ang mga kamakailang mensahe, hindi nasagot na tawag, at update sa status"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Pag-uusap"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"Nagpadala si <xliff:g id="NAME">%1$s</xliff:g> ng mensahe"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"Nagpadala si <xliff:g id="NAME">%1$s</xliff:g> ng larawan"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Nagkaproblema sa pagbabasa ng iyong battery meter"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 568be8a..2e9784f 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekranın bir parçasını büyütün"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Geç"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Erişilebilirlik hareketi, Erişilebilirlik düğmesi ile değiştirildi\n\n"<annotation id="link">"Ayarları görüntüle"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Erişilebilirlik hareketi yerine bir düğme kullanmaya geçebilirsiniz\n\n"<annotation id="link">"Ayarlar"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Düğmeyi geçici olarak gizlemek için kenara taşıyın"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Sol üste taşı"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Sağ üste taşı"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Yeni mesajları, cevapsız aramaları ve durum güncellemelerini görün"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Görüşme"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> bir mesaj gönderdi"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> bir resim gönderdi"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Pil ölçeriniz okunurken sorun oluştu"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 7aef252..9805da3 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -351,7 +351,7 @@
<string name="quick_settings_rotation_locked_portrait_label" msgid="1194988975270484482">"Книжкова орієнтація"</string>
<string name="quick_settings_rotation_locked_landscape_label" msgid="2000295772687238645">"Альбомна орієнтація"</string>
<string name="quick_settings_ime_label" msgid="3351174938144332051">"Метод введення"</string>
- <string name="quick_settings_location_label" msgid="2621868789013389163">"Місцезнаходження"</string>
+ <string name="quick_settings_location_label" msgid="2621868789013389163">"Геодані"</string>
<string name="quick_settings_location_off_label" msgid="7923929131443915919">"Місцезнаходження вимкнено"</string>
<string name="quick_settings_camera_label" msgid="5612076679385269339">"Доступ до камери"</string>
<string name="quick_settings_mic_label" msgid="8392773746295266375">"Доступ до мікрофона"</string>
@@ -1044,6 +1044,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Збільшити частину екрана"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Перемкнути"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Замість жесту спеціальних можливостей тепер використовується кнопка\n\n"<annotation id="link">"Переглянути налаштування"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Ви можете використовувати кнопку замість жесту спеціальних можливостей\n\n"<annotation id="link">"Налаштування"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Щоб тимчасово сховати кнопку, перемістіть її на край екрана"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Перемістити ліворуч угору"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Перемістити праворуч угору"</string>
@@ -1155,6 +1156,7 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Переглядайте останні повідомлення, пропущені виклики й оновлення статусу"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Розмова"</string>
+ <string name="paused_by_dnd" msgid="7856941866433556428">"Призупинено функцією \"Не турбувати\""</string>
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> надсилає повідомлення"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> надсилає зображення"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Не вдалось отримати дані лічильника акумулятора"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index b8911fd..a28cedc5 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"اسکرین کا حصہ بڑا کریں"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"سوئچ کریں"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"ایکسیسبیلٹی بٹن کو ایکسیسبیلٹی اشارے سے بدل دیا گیا\n\n"<annotation id="link">"ترتیبات دیکھیں"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"آپ ایکسیسبیلٹی اشارے سے بٹن پر سوئچ کر سکتے ہیں\n\n"<annotation id="link">"ترتیبات"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"عارضی طور پر بٹن کو چھپانے کے لئے اسے کنارے پر لے جائیں"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"اوپر بائیں جانب لے جائیں"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"اوپر دائیں جانب لے جائيں"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"+<xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="people_tile_description" msgid="8154966188085545556">"حالیہ پیغامات، چھوٹی ہوئی کالز اور اسٹیٹس اپ ڈیٹس دیکھیں"</string>
<string name="people_tile_title" msgid="6589377493334871272">"گفتگو"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> نے ایک پیغام بھیجا"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> نے ایک تصویر بھیجی"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"آپ کے بیٹری میٹر کو پڑھنے میں دشواری"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index f411938..3263ae9 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran qismini kattalashtirish"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Almashtirish"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Maxsus imkoniyatlar tugmasi maxsus imkoniyatlar ishorasini almashtirdi\n\n"<annotation id="link">"Sozlamalarni ochish"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Siz maxsus imkoniyatlar ishorasi oʻrniga \n\n"<annotation id="link">"Sozlamalar"</annotation>" tugmasidan foydalanishingiz mumkin"</string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Vaqtinchalik berkitish uchun tugmani qirra tomon suring"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Yuqori chapga surish"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Yuqori oʻngga surish"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Oxirgi xabarlar, javobsiz chaqiruvlar va holat yangilanishlari"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Suhbat"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> xabar yubordi"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> rasm yubordi"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Batareya quvvati aniqlanmadi"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 1e10496..28bb507 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Phóng to một phần màn hình"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Chuyển"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Nút hỗ trợ tiếp cận đã thay thế cử chỉ hỗ trợ tiếp cận\n\n"<annotation id="link">"Xem chế độ cài đặt"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Bạn có thể chuyển từ cử chỉ hỗ trợ tiếp cận sang nút hỗ trợ tiếp cận\n\n"<annotation id="link">"Cài đặt"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Di chuyển nút sang cạnh để ẩn nút tạm thời"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Chuyển lên trên cùng bên trái"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Chuyển lên trên cùng bên phải"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"Hơn <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Xem các tin nhắn, cuộc gọi nhỡ và thông tin cập nhật trạng thái gần đây"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Cuộc trò chuyện"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g> đã gửi một tin nhắn"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g> đã gửi một hình ảnh"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Đã xảy ra vấn đề khi đọc dung lượng pin của bạn"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 9272b8a..c773332 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -26,8 +26,8 @@
<string name="status_bar_latest_events_title" msgid="202755896454005436">"通知"</string>
<string name="battery_low_title" msgid="6891106956328275225">"电池电量可能很快就要耗尽"</string>
<string name="battery_low_percent_format" msgid="4276661262843170964">"剩余<xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
- <string name="battery_low_percent_format_hybrid" msgid="3985614339605686167">"剩余电量:<xliff:g id="PERCENTAGE">%1$s</xliff:g>;根据您的使用情况,大约还可使用 <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="battery_low_percent_format_hybrid_short" msgid="5917433188456218857">"剩余电量:<xliff:g id="PERCENTAGE">%1$s</xliff:g>;大约还可使用 <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="battery_low_percent_format_hybrid" msgid="3985614339605686167">"剩余电量:<xliff:g id="PERCENTAGE">%1$s</xliff:g>;根据您的使用情况,大约还可使用<xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="battery_low_percent_format_hybrid_short" msgid="5917433188456218857">"剩余电量:<xliff:g id="PERCENTAGE">%1$s</xliff:g>;大约还可使用<xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"剩余 <xliff:g id="PERCENTAGE">%s</xliff:g>。省电模式已开启。"</string>
<string name="invalid_charger" msgid="4370074072117767416">"无法通过 USB 充电。请使用设备随附的充电器。"</string>
<string name="invalid_charger_title" msgid="938685362320735167">"无法通过 USB 充电"</string>
@@ -228,7 +228,7 @@
<string name="accessibility_no_sims" msgid="5711270400476534667">"没有 SIM 卡。"</string>
<string name="accessibility_battery_details" msgid="6184390274150865789">"打开电量详情"</string>
<string name="accessibility_battery_level" msgid="5143715405241138822">"电池电量为百分之 <xliff:g id="NUMBER">%d</xliff:g>。"</string>
- <string name="accessibility_battery_level_with_estimate" msgid="4843119982547599452">"电池电量为 <xliff:g id="PERCENTAGE">%1$s</xliff:g>,根据您的使用情况,大约还可使用 <xliff:g id="TIME">%2$s</xliff:g>"</string>
+ <string name="accessibility_battery_level_with_estimate" msgid="4843119982547599452">"电池电量为 <xliff:g id="PERCENTAGE">%1$s</xliff:g>,根据您的使用情况,大约还可使用<xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"正在充电,已完成 <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g>%%。"</string>
<string name="accessibility_settings_button" msgid="2197034218538913880">"系统设置。"</string>
<string name="accessibility_notifications_button" msgid="3960913924189228831">"通知。"</string>
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大部分屏幕"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"切换"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"“无障碍”按钮已取代无障碍手势\n\n"<annotation id="link">"查看设置"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"您可以从使用无障碍手势改为使用按钮\n\n"<annotation id="link">"设置"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"将按钮移到边缘,即可暂时将其隐藏"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"移至左上角"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"移至右上角"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"查看近期的消息、未接电话和状态更新"</string>
<string name="people_tile_title" msgid="6589377493334871272">"对话"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g>发送了一条消息"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g>发送了一张图片"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"读取电池计量器时出现问题"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index fff99e4..0eeb4bb 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大部分螢幕畫面"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"切換"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"無障礙功能按鈕已取代無障礙手勢\n\n"<annotation id="link">"查看設定"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"您可以從無障礙手勢改為使用按鈕\n\n"<annotation id="link">"設定"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"將按鈕移到邊緣即可暫時隱藏"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"移去左上方"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"移去右上方"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"查看最近的訊息、未接來電和狀態更新"</string>
<string name="people_tile_title" msgid="6589377493334871272">"對話"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g>傳送了訊息"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g>傳送了圖片"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"讀取電池計量器時發生問題"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 9a278fd..100a87c 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大局部螢幕畫面"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"切換"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"無障礙工具按鈕已取代無障礙手勢\n\n"<annotation id="link">"查看設定"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"你可以從無障礙手勢改為使用按鈕\n\n"<annotation id="link">"設定"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"將按鈕移到邊緣處即可暫時隱藏"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"移到左上方"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"移到右上方"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"查看最近的訊息、未接來電和狀態更新"</string>
<string name="people_tile_title" msgid="6589377493334871272">"對話"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"<xliff:g id="NAME">%1$s</xliff:g>傳送了一則訊息"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"<xliff:g id="NAME">%1$s</xliff:g>傳送了一張圖片"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"讀取電池計量器時發生問題"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index a11c5a8..30eecaf 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -1034,6 +1034,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Khulisa ingxenye eyesikrini"</string>
<string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Iswishi"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="4431046858918714564">"Inkinobho yokufinyeleleka ishintshaniswe ngokuthinta kokufinyeleleka\n\n"<annotation id="link">"Buka amasethingi"</annotation></string>
+ <string name="accessibility_floating_button_switch_migration_tooltip" msgid="6248529129221218770">"Ungashintsha kusuka kunzwa yokufinyeleleka ukuya kunkinobho\n\n"<annotation id="link">"Amasethingi"</annotation></string>
<string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Hambisa inkinobho onqenqemeni ukuze uyifihle okwesikhashana"</string>
<string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Hamba phezulu kwesokunxele"</string>
<string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Hamba phezulu ngakwesokudla"</string>
@@ -1143,6 +1144,8 @@
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
<string name="people_tile_description" msgid="8154966188085545556">"Bona imiyalezo yakamuva, amakholi akuphuthile, nezibuyekezo zesimo"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Ingxoxo"</string>
+ <!-- no translation found for paused_by_dnd (7856941866433556428) -->
+ <skip />
<string name="new_notification_text_content_description" msgid="5574393603145263727">"U-<xliff:g id="NAME">%1$s</xliff:g> uthumele umlayezo"</string>
<string name="new_notification_image_content_description" msgid="6017506886810813123">"U-<xliff:g id="NAME">%1$s</xliff:g> uthumele isithombe"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Kube khona inkinga ngokufunda imitha yakho yebhethri"</string>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 067d56f..d2ed601 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -143,6 +143,7 @@
<attr name="handleThickness" format="dimension" />
<attr name="handleColor" format="color" />
<attr name="scrimColor" format="color" />
+ <attr name="containerBackgroundColor" format="color" />
<attr name="isVertical" format="boolean" />
@@ -178,6 +179,7 @@
<attr name="handleThickness" />
<attr name="handleColor" />
<attr name="scrimColor" />
+ <attr name="containerBackgroundColor" />
</declare-styleable>
<declare-styleable name="MagnifierView">
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 2cf3058..e7edb0e 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -93,7 +93,7 @@
<color name="notification_material_background_dark_color">#ff333333</color>
<!-- The color of the dividing line between grouped notifications. -->
- <color name="notification_divider_color">#FF616161</color>
+ <color name="notification_divider_color">@*android:color/background_device_default_light</color>
<!-- The color of the ripples on the untinted notifications -->
<color name="notification_ripple_untinted_color">#28000000</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index b4deaa0..2a1bee5 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -396,10 +396,6 @@
<!-- Whether or not the notifications should always fade as they are dismissed. -->
<bool name="config_fadeNotificationsOnDismiss">false</bool>
- <!-- Whether or not the parent of the notification row itself is being translated when swiped or
- its children views. If true, then the contents are translated and vice versa. -->
- <bool name="config_translateNotificationContentsOnSwipe">true</bool>
-
<!-- Whether or not the fade on the notification is based on the amount that it has been swiped
off-screen. -->
<bool name="config_fadeDependingOnAmountSwiped">false</bool>
@@ -430,7 +426,7 @@
<!-- Whether or not the dividing lines should be shown when the container is expanding and
collapsing. If this value is true, then the lines will only show when the container has
been completely expanded. -->
- <bool name="config_hideDividersDuringExpand">false</bool>
+ <bool name="config_hideDividersDuringExpand">true</bool>
<!-- Whether or not child notifications that are part of a group will have shadows. -->
<bool name="config_enableShadowOnChildNotifications">true</bool>
@@ -449,6 +445,9 @@
<!-- Adjust the theme on fully custom and decorated custom view notifications -->
<bool name="config_adjustThemeOnNotificationCustomViews">false</bool>
+ <!-- Notifications are sized to match the width of two (of 4) qs tiles in landscape. -->
+ <bool name="config_skinnyNotifsInLandscape">true</bool>
+
<!-- If true, enable the advance anti-falsing classifier on the lockscreen. On some devices it
does not work well, particularly with noisy touchscreens. Note that disabling it may
increase the rate of unintentional unlocks. -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0ccde60..ea54bb4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -347,7 +347,7 @@
<!-- Padding to make tappable chip height 48dp (18+11+11+4+4) -->
<dimen name="screenshot_action_chip_margin_vertical">4dp</dimen>
<dimen name="screenshot_action_chip_padding_vertical">11dp</dimen>
- <dimen name="screenshot_action_chip_icon_size">18dp</dimen>
+ <dimen name="screenshot_action_chip_icon_size">18sp</dimen>
<!-- Padding on each side of the icon for icon-only chips -->
<dimen name="screenshot_action_chip_icon_only_padding_horizontal">14dp</dimen>
<!-- Padding at the edges of the chip for icon-and-text chips -->
@@ -685,7 +685,7 @@
<dimen name="notification_shadow_radius">0dp</dimen>
<!-- The alpha of the dividing line between child notifications of a notification group. -->
- <item name="notification_divider_alpha" format="float" type="dimen">0.5</item>
+ <item name="notification_divider_alpha" format="float" type="dimen">1</item>
<!-- The height of the divider between the individual notifications in a notification
group. -->
@@ -735,8 +735,8 @@
<!-- Minimum distance the user has to drag down to go to the full shade. -->
<dimen name="keyguard_drag_down_min_distance">100dp</dimen>
- <!-- The margin between the clock and the notifications on Keyguard.-->
- <dimen name="keyguard_clock_notifications_margin">30dp</dimen>
+ <!-- The margin between the status view and the notifications on Keyguard.-->
+ <dimen name="keyguard_status_view_bottom_margin">20dp</dimen>
<!-- Minimum margin between clock and status bar -->
<dimen name="keyguard_clock_top_margin">36dp</dimen>
<!-- The margin between top of clock and bottom of lock icon. -->
@@ -1149,7 +1149,7 @@
<dimen name="edge_margin">8dp</dimen>
<!-- The absolute side margins of quick settings -->
- <dimen name="quick_settings_bottom_margin_media">16dp</dimen>
+ <dimen name="quick_settings_bottom_margin_media">8dp</dimen>
<dimen name="rounded_corner_content_padding">0dp</dimen>
<dimen name="nav_content_padding">0dp</dimen>
<dimen name="nav_quick_scrub_track_edge_padding">24dp</dimen>
@@ -1278,12 +1278,12 @@
<dimen name="qs_media_album_radius">14dp</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_info_spacing">8dp</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_media_action_spacing">4dp</dimen>
- <dimen name="qs_media_action_top">8dp</dimen>
+ <dimen name="qs_media_action_margin">12dp</dimen>
<dimen name="qs_seamless_height">24dp</dimen>
<dimen name="qs_seamless_icon_size">12dp</dimen>
<dimen name="qs_seamless_fallback_icon_size">@dimen/qs_seamless_icon_size</dimen>
@@ -1291,8 +1291,8 @@
<dimen name="qs_footer_horizontal_margin">22dp</dimen>
<dimen name="qs_media_disabled_seekbar_height">1dp</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>
+ <dimen name="qs_media_enabled_seekbar_vertical_padding">31dp</dimen>
+ <dimen name="qs_media_disabled_seekbar_vertical_padding">32dp</dimen>
<!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
<dimen name="qs_aa_media_rec_album_size_collapsed">72dp</dimen>
@@ -1451,7 +1451,7 @@
<dimen name="people_space_messages_count_radius">12dp</dimen>
<dimen name="people_space_widget_background_padding">6dp</dimen>
<dimen name="required_width_for_medium">136dp</dimen>
- <dimen name="required_width_for_large">138dp</dimen>
+ <dimen name="required_width_for_large">120dp</dimen>
<dimen name="required_height_for_large">168dp</dimen>
<dimen name="default_width">146dp</dimen>
<dimen name="default_height">92dp</dimen>
@@ -1470,10 +1470,12 @@
<dimen name="below_name_text_padding">16dp</dimen>
<dimen name="above_notification_text_padding">22dp</dimen>
<dimen name="regular_predefined_icon">18dp</dimen>
- <dimen name="large_predefined_icon">24dp</dimen>
+ <dimen name="larger_predefined_icon">24dp</dimen>
+ <dimen name="largest_predefined_icon">32dp</dimen>
<dimen name="availability_dot_status_padding">8dp</dimen>
<dimen name="availability_dot_notification_padding">12dp</dimen>
<dimen name="medium_content_padding_above_name">4dp</dimen>
+ <dimen name="padding_between_suppressed_layout_items">8dp</dimen>
<!-- Accessibility floating menu -->
<dimen name="accessibility_floating_menu_elevation">3dp</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 027f162..b999e51 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -36,6 +36,13 @@
<!-- The new animations to/from lockscreen and AOD! -->
<bool name="flag_lockscreen_animations">false</bool>
+ <!-- The new swipe to unlock animation, which shows the app/launcher behind the keyguard during
+ the swipe. -->
+ <bool name="flag_new_unlock_swipe_animation">true</bool>
+
+ <!-- The shared-element transition between lockscreen smartspace and launcher smartspace. -->
+ <bool name="flag_smartspace_shared_element_transition">false</bool>
+
<bool name="flag_pm_lite">true</bool>
<bool name="flag_charging_ripple">false</bool>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 91301df..bc1c67c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2889,7 +2889,7 @@
<!--Text explaining to tap a conversation to select it show in their Conversation widget [CHAR LIMIT=180] -->
<string name="select_conversation_text">Tap a conversation to add it to your Home screen</string>
<!--Text explaining there are no existing conversations to show in their Conversation widget [CHAR LIMIT=100] -->
- <string name="no_conversations_text">Check back here once you get some messages</string>
+ <string name="no_conversations_text">Your recent conversations will show up here</string>
<!--Text header for priority conversation tiles available to be added to the home screen [CHAR LIMIT=100] -->
<string name="priority_conversations">Priority conversations</string>
<!--Text header for recent conversation tiles available to be added to the home screen [CHAR LIMIT=100] -->
@@ -2932,7 +2932,7 @@
<string name="audio_status">Listening</string>
<!-- Status text on the Conversation widget for playing a game [CHAR LIMIT=20] -->
<string name="game_status">Playing</string>
- <!-- Empty user name before user has selected a friend for their Conversation widget [CHAR LIMIT=20] -->
+ <!-- Empty user name before user has selected a friend for their Conversation widget [CHAR LIMIT=10] -->
<string name="empty_user_name">Friends</string>
<!-- Empty status shown before user has selected a friend for their Conversation widget [CHAR LIMIT=20] -->
<string name="empty_status">Let’s chat tonight!</string>
@@ -2946,6 +2946,8 @@
<string name="people_tile_description">See recent messages, missed calls, and status updates</string>
<!-- Title text displayed for the Conversation widget [CHAR LIMIT=50] -->
<string name="people_tile_title">Conversation</string>
+ <!-- Text when the Conversation widget when Do Not Disturb is suppressing the notification. [CHAR LIMIT=50] -->
+ <string name="paused_by_dnd">Paused by Do Not Disturb</string>
<!-- Content description text on the Conversation widget when a person has sent a new text message [CHAR LIMIT=150] -->
<string name="new_notification_text_content_description"><xliff:g id="name" example="Anna">%1$s</xliff:g> sent a message</string>
<!-- Content description text on the Conversation widget when a person has sent a new image message [CHAR LIMIT=150] -->
@@ -2979,6 +2981,8 @@
<!-- Content description for a chip in the status bar showing that the user is currently on a phone call. [CHAR LIMIT=NONE] -->
<string name="ongoing_phone_call_content_description">Ongoing phone call</string>
+ <!-- Placeholder for string describing changes in global actions -->
+ <string name="global_actions_change_description" translatable="false"><xliff:g>%1$s</xliff:g></string>
<!-- URL for more information about changes in global actions -->
<string name="global_actions_change_url" translatable="false"></string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 97273a8..6d25a5b 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -404,6 +404,7 @@
<item name="offStateColor">@android:color/system_neutral1_800</item>
<item name="underSurfaceColor">@android:color/system_neutral1_1000</item>
<item name="android:colorBackground">@android:color/system_neutral1_900</item>
+ <item name="android:itemTextAppearance">@style/Control.MenuItem</item>
</style>
<style name="Theme.SystemUI.QuickSettings.BrightnessDialog" parent="@android:style/Theme.DeviceDefault.Dialog">
@@ -641,7 +642,10 @@
<item name="android:background">@drawable/qs_media_light_source</item>
<item name="android:tint">?android:attr/textColorPrimary</item>
<item name="android:stateListAnimator">@anim/media_button_state_list_animator</item>
- <item name="android:padding">12dp</item>
+ <item name="android:paddingTop">8dp</item>
+ <item name="android:paddingStart">12dp</item>
+ <item name="android:paddingEnd">12dp</item>
+ <item name="android:paddingBottom">16dp</item>
<item name="android:scaleType">centerInside</item>
</style>
@@ -883,5 +887,6 @@
<style name="Wallet.Theme" parent="@android:style/Theme.DeviceDefault">
<item name="android:colorBackground">@android:color/system_neutral1_900</item>
+ <item name="android:itemBackground">@android:color/system_neutral1_800</item>
</style>
</resources>
diff --git a/packages/SystemUI/res/xml/media_collapsed.xml b/packages/SystemUI/res/xml/media_collapsed.xml
index a03a1d3..d6c6a60 100644
--- a/packages/SystemUI/res/xml/media_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_collapsed.xml
@@ -92,7 +92,7 @@
android:layout_height="wrap_content"
app:layout_constrainedWidth="true"
android:layout_marginTop="@dimen/qs_media_info_spacing"
- app:layout_constraintTop_toTopOf="@id/center_horizontal_guideline"
+ app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
app:layout_constraintStart_toStartOf="@id/header_title"
app:layout_constraintEnd_toStartOf="@id/media_action_barrier"
app:layout_constraintHorizontal_bias="0"/>
@@ -123,7 +123,7 @@
<Constraint
android:id="@+id/action0"
android:layout_width="48dp"
- android:layout_height="56dp"
+ android:layout_height="48dp"
android:layout_marginStart="@dimen/qs_media_padding"
android:layout_marginEnd="@dimen/qs_media_action_spacing"
android:visibility="gone"
@@ -139,7 +139,7 @@
<Constraint
android:id="@+id/action1"
android:layout_width="48dp"
- android:layout_height="56dp"
+ android:layout_height="48dp"
android:layout_marginStart="@dimen/qs_media_action_spacing"
android:layout_marginEnd="@dimen/qs_media_action_spacing"
app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
@@ -152,7 +152,7 @@
<Constraint
android:id="@+id/action2"
android:layout_width="48dp"
- android:layout_height="56dp"
+ android:layout_height="48dp"
android:layout_marginStart="@dimen/qs_media_action_spacing"
android:layout_marginEnd="@dimen/qs_media_action_spacing"
app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
@@ -165,7 +165,7 @@
<Constraint
android:id="@+id/action3"
android:layout_width="48dp"
- android:layout_height="56dp"
+ android:layout_height="48dp"
android:layout_marginStart="@dimen/qs_media_action_spacing"
android:layout_marginEnd="@dimen/qs_media_action_spacing"
app:layout_constraintTop_toBottomOf="@id/center_horizontal_guideline"
@@ -178,7 +178,7 @@
<Constraint
android:id="@+id/action4"
android:layout_width="48dp"
- android:layout_height="56dp"
+ android:layout_height="48dp"
android:layout_marginStart="@dimen/qs_media_action_spacing"
android:layout_marginEnd="@dimen/qs_media_padding"
android:visibility="gone"
diff --git a/packages/SystemUI/res/xml/media_expanded.xml b/packages/SystemUI/res/xml/media_expanded.xml
index fd04fa0..9d706c5 100644
--- a/packages/SystemUI/res/xml/media_expanded.xml
+++ b/packages/SystemUI/res/xml/media_expanded.xml
@@ -91,7 +91,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/qs_media_padding"
- android:layout_marginBottom="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_media_info_margin"
app:layout_constrainedWidth="true"
android:layout_marginTop="@dimen/qs_media_info_spacing"
app:layout_constraintTop_toBottomOf="@id/header_title"
@@ -124,10 +124,10 @@
android:id="@+id/action0"
android:layout_width="48dp"
android:layout_height="48dp"
- android:layout_marginTop="@dimen/qs_media_action_top"
+ android:layout_marginTop="@dimen/qs_media_action_margin"
android:layout_marginStart="@dimen/qs_media_padding"
android:layout_marginEnd="@dimen/qs_media_action_spacing"
- android:layout_marginBottom="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_media_action_margin"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/action1"
@@ -139,10 +139,10 @@
android:id="@+id/action1"
android:layout_width="48dp"
android:layout_height="48dp"
- android:layout_marginTop="@dimen/qs_media_action_top"
+ android:layout_marginTop="@dimen/qs_media_action_margin"
android:layout_marginStart="@dimen/qs_media_action_spacing"
android:layout_marginEnd="@dimen/qs_media_action_spacing"
- android:layout_marginBottom="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_media_action_margin"
app:layout_constraintLeft_toRightOf="@id/action0"
app:layout_constraintRight_toLeftOf="@id/action2"
app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
@@ -153,10 +153,10 @@
android:id="@+id/action2"
android:layout_width="48dp"
android:layout_height="48dp"
- android:layout_marginTop="@dimen/qs_media_action_top"
+ android:layout_marginTop="@dimen/qs_media_action_margin"
android:layout_marginStart="@dimen/qs_media_action_spacing"
android:layout_marginEnd="@dimen/qs_media_action_spacing"
- android:layout_marginBottom="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_media_action_margin"
app:layout_constraintLeft_toRightOf="@id/action1"
app:layout_constraintRight_toLeftOf="@id/action3"
app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
@@ -167,10 +167,10 @@
android:id="@+id/action3"
android:layout_width="48dp"
android:layout_height="48dp"
- android:layout_marginTop="@dimen/qs_media_action_top"
+ android:layout_marginTop="@dimen/qs_media_action_margin"
android:layout_marginStart="@dimen/qs_media_action_spacing"
android:layout_marginEnd="@dimen/qs_media_action_spacing"
- android:layout_marginBottom="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_media_action_margin"
app:layout_constraintLeft_toRightOf="@id/action2"
app:layout_constraintRight_toLeftOf="@id/action4"
app:layout_constraintTop_toBottomOf="@id/media_progress_bar"
@@ -181,10 +181,10 @@
android:id="@+id/action4"
android:layout_width="48dp"
android:layout_height="48dp"
- android:layout_marginTop="@dimen/qs_media_action_top"
+ android:layout_marginTop="@dimen/qs_media_action_margin"
android:layout_marginStart="@dimen/qs_media_action_spacing"
android:layout_marginEnd="@dimen/qs_media_padding"
- android:layout_marginBottom="@dimen/qs_media_padding"
+ android:layout_marginBottom="@dimen/qs_media_action_margin"
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/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index c90833c..2b35bcd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -345,7 +345,7 @@
// Create our own ClassLoader so we can use our own code as the parent.
ClassLoader classLoader = mManager.getClassLoader(info);
Context pluginContext = new PluginContextWrapper(
- mContext.createPackageContext(pkg, 0), classLoader);
+ mContext.createApplicationContext(info, 0), classLoader);
Class<?> pluginClass = Class.forName(cls, true, classLoader);
// TODO: Only create the plugin before version check if we need it for
// legacy version check.
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/BlurUtils.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/BlurUtils.java
index 59e1cb5..61b0e4d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/BlurUtils.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/BlurUtils.java
@@ -19,6 +19,7 @@
import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED;
import android.app.ActivityManager;
+import android.os.SystemProperties;
public abstract class BlurUtils {
@@ -28,6 +29,7 @@
* @return {@code true} when supported.
*/
public static boolean supportsBlursOnWindows() {
- return CROSS_WINDOW_BLUR_SUPPORTED && ActivityManager.isHighEndGfx();
+ return CROSS_WINDOW_BLUR_SUPPORTED && ActivityManager.isHighEndGfx()
+ && !SystemProperties.getBoolean("persist.sysui.disableBlur", false);
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
index 42d2333..44271687 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
@@ -17,6 +17,7 @@
package com.android.systemui.shared.system;
import android.annotation.IntDef;
+import android.os.Build;
import android.view.View;
import com.android.internal.jank.InteractionJankMonitor;
@@ -58,23 +59,48 @@
public @interface CujType {
}
- public static boolean begin(View v, @CujType int cujType) {
- return InteractionJankMonitor.getInstance().begin(v, cujType);
+ /**
+ * Begin a trace session.
+ *
+ * @param v an attached view.
+ * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ */
+ public static void begin(View v, @CujType int cujType) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return;
+ InteractionJankMonitor.getInstance().begin(v, cujType);
}
- public static boolean begin(View v, @CujType int cujType, long timeout) {
+ /**
+ * Begin a trace session.
+ *
+ * @param v an attached view.
+ * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @param timeout duration to cancel the instrumentation in ms
+ */
+ public static void begin(View v, @CujType int cujType, long timeout) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return;
Configuration.Builder builder =
new Configuration.Builder(cujType)
.setView(v)
.setTimeout(timeout);
- return InteractionJankMonitor.getInstance().begin(builder);
+ InteractionJankMonitor.getInstance().begin(builder);
}
- public static boolean end(@CujType int cujType) {
- return InteractionJankMonitor.getInstance().end(cujType);
+ /**
+ * End a trace session.
+ *
+ * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ */
+ public static void end(@CujType int cujType) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return;
+ InteractionJankMonitor.getInstance().end(cujType);
}
- public static boolean cancel(@CujType int cujType) {
- return InteractionJankMonitor.getInstance().cancel(cujType);
+ /**
+ * Cancel the trace session.
+ */
+ public static void cancel(@CujType int cujType) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return;
+ InteractionJankMonitor.getInstance().cancel(cujType);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index ef6212d..a28d174 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -205,6 +205,10 @@
}
}
+ int getNotificationIconAreaHeight() {
+ return mNotificationIconAreaController.getHeight();
+ }
+
@Override
protected void onViewDetached() {
if (CUSTOM_CLOCKS_ENABLED) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 123c0e6..2096c31 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -187,10 +187,11 @@
}
/**
- * Get the height of the keyguard status view.
+ * Get the height of the keyguard status view without the notification icon area, as that's
+ * only visible on AOD.
*/
- public int getHeight() {
- return mView.getHeight();
+ public int getLockscreenHeight() {
+ return mView.getHeight() - mKeyguardClockSwitchController.getNotificationIconAreaHeight();
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f3a6d63..e2e221b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -71,6 +71,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.Vibrator;
import android.provider.Settings;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
@@ -85,6 +86,7 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import androidx.annotation.Nullable;
import androidx.lifecycle.Observer;
import com.android.internal.annotations.VisibleForTesting;
@@ -95,6 +97,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.biometrics.UdfpsController;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -249,6 +252,16 @@
public void onStateChanged(int newState) {
mStatusBarState = newState;
}
+
+ @Override
+ public void onExpandedChanged(boolean isExpanded) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onShadeExpandedChanged(isExpanded);
+ }
+ }
+ }
};
HashMap<Integer, SimData> mSimDatas = new HashMap<>();
@@ -272,6 +285,9 @@
@VisibleForTesting
protected boolean mTelephonyCapable;
+ private final boolean mAcquiredHapticEnabled;
+ @Nullable private final Vibrator mVibrator;
+
// Device provisioning state
private boolean mDeviceProvisioned;
@@ -768,13 +784,16 @@
}
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
- mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
- getCurrentUser());
+ mFingerprintLockedOutPermanent = true;
+ requireStrongAuthIfAllLockedOut();
}
if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
|| msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) {
mFingerprintLockedOut = true;
+ if (isUdfpsEnrolled()) {
+ updateFingerprintListeningState();
+ }
}
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -787,6 +806,7 @@
private void handleFingerprintLockoutReset() {
mFingerprintLockedOut = false;
+ mFingerprintLockedOutPermanent = false;
updateFingerprintListeningState();
}
@@ -947,8 +967,8 @@
}
if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
- mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
- getCurrentUser());
+ mFaceLockedOutPermanent = true;
+ requireStrongAuthIfAllLockedOut();
}
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -961,6 +981,7 @@
}
private void handleFaceLockoutReset() {
+ mFaceLockedOutPermanent = false;
updateFaceListeningState();
}
@@ -1036,6 +1057,18 @@
|| isSimPinSecure());
}
+ private void requireStrongAuthIfAllLockedOut() {
+ final boolean faceLock =
+ mFaceLockedOutPermanent || !shouldListenForFace();
+ final boolean fpLock =
+ mFingerprintLockedOutPermanent || !shouldListenForFingerprint(isUdfpsEnrolled());
+
+ if (faceLock && fpLock) {
+ Log.d(TAG, "All biometrics locked out - requiring strong auth");
+ mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
+ getCurrentUser());
+ }
+ }
public boolean getUserCanSkipBouncer(int userId) {
return getUserHasTrust(userId) || getUserUnlockedWithBiometric(userId);
@@ -1319,46 +1352,69 @@
handleFingerprintAuthenticated(userId, isStrongBiometric);
};
- private final FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback
+ @VisibleForTesting
+ final FingerprintManager.AuthenticationCallback mFingerprintAuthenticationCallback
= new AuthenticationCallback() {
+ private boolean mPlayedAcquiredHaptic;
- @Override
- public void onAuthenticationFailed() {
- handleFingerprintAuthFailed();
- }
+ @Override
+ public void onAuthenticationFailed() {
+ handleFingerprintAuthFailed();
+ }
- @Override
- public void onAuthenticationSucceeded(AuthenticationResult result) {
- Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded");
- handleFingerprintAuthenticated(result.getUserId(), result.isStrongBiometric());
- Trace.endSection();
- }
+ @Override
+ public void onAuthenticationSucceeded(AuthenticationResult result) {
+ Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded");
+ handleFingerprintAuthenticated(result.getUserId(), result.isStrongBiometric());
+ Trace.endSection();
- @Override
- public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
- handleFingerprintHelp(helpMsgId, helpString.toString());
- }
+ // on auth success, we sometimes never received an acquired haptic
+ if (!mPlayedAcquiredHaptic) {
+ playAcquiredHaptic();
+ }
+ }
- @Override
- public void onAuthenticationError(int errMsgId, CharSequence errString) {
- handleFingerprintError(errMsgId, errString.toString());
- }
+ @Override
+ public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
+ handleFingerprintHelp(helpMsgId, helpString.toString());
+ }
- @Override
- public void onAuthenticationAcquired(int acquireInfo) {
- handleFingerprintAcquired(acquireInfo);
- }
+ @Override
+ public void onAuthenticationError(int errMsgId, CharSequence errString) {
+ handleFingerprintError(errMsgId, errString.toString());
+ }
- @Override
- public void onUdfpsPointerDown(int sensorId) {
- Log.d(TAG, "onUdfpsPointerDown, sensorId: " + sensorId);
- }
+ @Override
+ public void onAuthenticationAcquired(int acquireInfo) {
+ handleFingerprintAcquired(acquireInfo);
+ if (acquireInfo == FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
+ playAcquiredHaptic();
+ }
+ }
- @Override
- public void onUdfpsPointerUp(int sensorId) {
- Log.d(TAG, "onUdfpsPointerUp, sensorId: " + sensorId);
- }
- };
+ @Override
+ public void onUdfpsPointerDown(int sensorId) {
+ Log.d(TAG, "onUdfpsPointerDown, sensorId: " + sensorId);
+ mPlayedAcquiredHaptic = false;
+ }
+
+ @Override
+ public void onUdfpsPointerUp(int sensorId) {
+ Log.d(TAG, "onUdfpsPointerUp, sensorId: " + sensorId);
+ }
+
+ private void playAcquiredHaptic() {
+ if (mAcquiredHapticEnabled && mVibrator != null && isUdfpsEnrolled()) {
+ mPlayedAcquiredHaptic = true;
+ String effect = Settings.Global.getString(
+ mContext.getContentResolver(),
+ "udfps_acquired_type");
+ mVibrator.vibrate(UdfpsController.getVibration(effect,
+ UdfpsController.EFFECT_TICK),
+ UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES);
+ }
+ }
+ };
private final FaceManager.FaceDetectionCallback mFaceDetectionCallback
= (sensorId, userId, isStrongBiometric) -> {
@@ -1367,7 +1423,7 @@
};
@VisibleForTesting
- FaceManager.AuthenticationCallback mFaceAuthenticationCallback
+ final FaceManager.AuthenticationCallback mFaceAuthenticationCallback
= new FaceManager.AuthenticationCallback() {
@Override
@@ -1404,6 +1460,8 @@
private FaceManager mFaceManager;
private List<FaceSensorPropertiesInternal> mFaceSensorProperties;
private boolean mFingerprintLockedOut;
+ private boolean mFingerprintLockedOutPermanent;
+ private boolean mFaceLockedOutPermanent;
private TelephonyManager mTelephonyManager;
/**
@@ -1650,7 +1708,8 @@
LockPatternUtils lockPatternUtils,
AuthController authController,
TelephonyListenerManager telephonyListenerManager,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ @Nullable Vibrator vibrator) {
mContext = context;
mSubscriptionManager = SubscriptionManager.from(context);
mTelephonyListenerManager = telephonyListenerManager;
@@ -1665,6 +1724,9 @@
mLockPatternUtils = lockPatternUtils;
mAuthController = authController;
dumpManager.registerDumpable(getClass().getName(), this);
+ mAcquiredHapticEnabled = Settings.Global.getInt(mContext.getContentResolver(),
+ "udfps_acquired", 0) == 1;
+ mVibrator = vibrator;
mHandler = new Handler(mainLooper) {
@Override
@@ -2115,7 +2177,8 @@
|| (!getUserCanSkipBouncer(getCurrentUser())
&& !isEncryptedOrLockdown(getCurrentUser())
&& !userNeedsStrongAuth()
- && userDoesNotHaveTrust);
+ && userDoesNotHaveTrust
+ && !mFingerprintLockedOut);
return shouldListenKeyguardState && shouldListenUserState && shouldListenBouncerState
&& shouldListenUdfpsState;
}
@@ -3244,6 +3307,7 @@
pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
pw.println(" trustManaged=" + getUserTrustIsManaged(userId));
pw.println(" udfpsEnrolled=" + isUdfpsEnrolled());
+ pw.println(" mFingerprintLockedOut=" + mFingerprintLockedOut);
pw.println(" enabledByUser=" + mBiometricEnabledForUser.get(userId));
if (isUdfpsEnrolled()) {
pw.println(" shouldListenForUdfps=" + shouldListenForFingerprint(true));
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index e561a5a..9849a7e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -333,4 +333,8 @@
*/
public void onRequireUnlockForNfc() { }
+ /**
+ * Called when the notification shade is expanded or collapsed.
+ */
+ public void onShadeExpandedChanged(boolean expanded) { }
}
diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
index 45a0ea1..0ae89bc 100644
--- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java
@@ -27,15 +27,14 @@
import android.content.pm.UserInfo;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
-import android.view.WindowManagerGlobal;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
/**
* Manages notification when a guest session is resumed.
@@ -47,9 +46,12 @@
private static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in";
private Dialog mNewSessionDialog;
+ private final UserSwitcherController mUserSwitcherController;
private final UiEventLogger mUiEventLogger;
- public GuestResumeSessionReceiver(UiEventLogger uiEventLogger) {
+ public GuestResumeSessionReceiver(UserSwitcherController userSwitcherController,
+ UiEventLogger uiEventLogger) {
+ mUserSwitcherController = userSwitcherController;
mUiEventLogger = uiEventLogger;
}
@@ -90,7 +92,8 @@
int notFirstLogin = Settings.System.getIntForUser(
cr, SETTING_GUEST_HAS_LOGGED_IN, 0, userId);
if (notFirstLogin != 0) {
- mNewSessionDialog = new ResetSessionDialog(context, mUiEventLogger, userId);
+ mNewSessionDialog = new ResetSessionDialog(context, mUserSwitcherController,
+ mUiEventLogger, userId);
mNewSessionDialog.show();
} else {
Settings.System.putIntForUser(
@@ -99,54 +102,6 @@
}
}
- /**
- * Wipes the guest session.
- *
- * The guest must be the current user and its id must be {@param userId}.
- */
- private static void wipeGuestSession(Context context, int userId) {
- UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
- UserInfo currentUser;
- try {
- currentUser = ActivityManager.getService().getCurrentUser();
- } catch (RemoteException e) {
- Log.e(TAG, "Couldn't wipe session because ActivityManager is dead");
- return;
- }
- if (currentUser.id != userId) {
- Log.w(TAG, "User requesting to start a new session (" + userId + ")"
- + " is not current user (" + currentUser.id + ")");
- return;
- }
- if (!currentUser.isGuest()) {
- Log.w(TAG, "User requesting to start a new session (" + userId + ")"
- + " is not a guest");
- return;
- }
-
- boolean marked = userManager.markGuestForDeletion(currentUser.id);
- if (!marked) {
- Log.w(TAG, "Couldn't mark the guest for deletion for user " + userId);
- return;
- }
- UserInfo newGuest = userManager.createGuest(context, currentUser.name);
-
- try {
- if (newGuest == null) {
- Log.e(TAG, "Could not create new guest, switching back to system user");
- ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
- userManager.removeUser(currentUser.id);
- WindowManagerGlobal.getWindowManagerService().lockNow(null /* options */);
- return;
- }
- ActivityManager.getService().switchUser(newGuest.id);
- userManager.removeUser(currentUser.id);
- } catch (RemoteException e) {
- Log.e(TAG, "Couldn't wipe session because ActivityManager or WindowManager is dead");
- return;
- }
- }
-
private void cancelDialog() {
if (mNewSessionDialog != null && mNewSessionDialog.isShowing()) {
mNewSessionDialog.cancel();
@@ -160,10 +115,12 @@
private static final int BUTTON_WIPE = BUTTON_NEGATIVE;
private static final int BUTTON_DONTWIPE = BUTTON_POSITIVE;
+ private final UserSwitcherController mUserSwitcherController;
private final UiEventLogger mUiEventLogger;
private final int mUserId;
- ResetSessionDialog(Context context, UiEventLogger uiEventLogger, int userId) {
+ ResetSessionDialog(Context context, UserSwitcherController userSwitcherController,
+ UiEventLogger uiEventLogger, int userId) {
super(context);
setTitle(context.getString(R.string.guest_wipe_session_title));
@@ -175,6 +132,7 @@
setButton(BUTTON_DONTWIPE,
context.getString(R.string.guest_wipe_session_dontwipe), this);
+ mUserSwitcherController = userSwitcherController;
mUiEventLogger = uiEventLogger;
mUserId = userId;
}
@@ -183,7 +141,7 @@
public void onClick(DialogInterface dialog, int which) {
if (which == BUTTON_WIPE) {
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_WIPE);
- wipeGuestSession(getContext(), mUserId);
+ mUserSwitcherController.removeGuestUser(mUserId, UserHandle.USER_NULL);
dismiss();
} else if (which == BUTTON_DONTWIPE) {
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_CONTINUE);
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 64a683e..a68f796 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -130,10 +130,7 @@
.getBounds();
mHeight = window.height();
mWidth = window.width();
- mMiniBitmap = null;
- if (mWorker != null && mWorker.getThreadHandler() != null) {
- mWorker.getThreadHandler().post(this::updateMiniBitmap);
- }
+ mRenderer.setOnBitmapChanged(this::updateMiniBitmap);
}
EglHelper getEglHelperInstance() {
@@ -177,20 +174,19 @@
mPageOffset = (1 - imgWidth) / (float) (mPages - 1);
}
- private void updateMiniBitmap() {
- mRenderer.useBitmap(b -> {
- int size = Math.min(b.getWidth(), b.getHeight());
- float scale = 1.0f;
- if (size > MIN_SURFACE_WIDTH) {
- scale = (float) MIN_SURFACE_WIDTH / (float) size;
- }
- mImgHeight = b.getHeight();
- mImgWidth = b.getWidth();
- mMiniBitmap = Bitmap.createScaledBitmap(b, (int) Math.max(scale * b.getWidth(), 1),
- (int) Math.max(scale * b.getHeight(), 1), false);
- computeAndNotifyLocalColors(mLocalColorsToAdd, mMiniBitmap);
- mLocalColorsToAdd.clear();
- });
+ private void updateMiniBitmap(Bitmap b) {
+ if (b == null) return;
+ int size = Math.min(b.getWidth(), b.getHeight());
+ float scale = 1.0f;
+ if (size > MIN_SURFACE_WIDTH) {
+ scale = (float) MIN_SURFACE_WIDTH / (float) size;
+ }
+ mImgHeight = b.getHeight();
+ mImgWidth = b.getWidth();
+ mMiniBitmap = Bitmap.createScaledBitmap(b, (int) Math.max(scale * b.getWidth(), 1),
+ (int) Math.max(scale * b.getHeight(), 1), false);
+ computeAndNotifyLocalColors(mLocalColorsToAdd, mMiniBitmap);
+ mLocalColorsToAdd.clear();
}
private void updateSurfaceSize() {
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index b0f4da2..affad7a 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -391,9 +391,9 @@
boolean animateLeft = (Math.abs(velocity) > getEscapeVelocity() && velocity < 0) ||
(getTranslation(animView) < 0 && !isDismissAll);
if (animateLeft || animateLeftForRtl || animateUpForMenu) {
- newPos = -getSize(animView);
+ newPos = -getTotalTranslationLength(animView);
} else {
- newPos = getSize(animView);
+ newPos = getTotalTranslationLength(animView);
}
long duration;
if (fixedDuration == 0) {
@@ -470,6 +470,15 @@
}
/**
+ * Get the total translation length where we want to swipe to when dismissing the view. By
+ * default this is the size of the view, but can also be larger.
+ * @param animView the view to ask about
+ */
+ protected float getTotalTranslationLength(View animView) {
+ return getSize(animView);
+ }
+
+ /**
* Called to update the dismiss animation.
*/
protected void prepareDismissAnimation(View view, Animator anim) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
index 7cd43ef..cff6cf1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
@@ -19,6 +19,7 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import android.content.Context;
+import android.os.UserHandle;
import android.text.TextUtils;
import androidx.annotation.MainThread;
@@ -40,11 +41,11 @@
AccessibilityButtonModeObserver.ModeChangedListener,
AccessibilityButtonTargetsObserver.TargetsChangedListener {
- private final Context mContext;
private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
private final AccessibilityButtonTargetsObserver mAccessibilityButtonTargetsObserver;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private Context mContext;
@VisibleForTesting
IAccessibilityFloatingMenu mFloatingMenu;
private int mBtnMode;
@@ -79,6 +80,7 @@
@Override
public void onUserSwitchComplete(int userId) {
+ mContext = mContext.createContextAsUser(UserHandle.of(userId), /* flags= */ 0);
mBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
mBtnTargets =
mAccessibilityButtonTargetsObserver.getCurrentAccessibilityButtonTargets();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
index 5bb5522..e891e5b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
@@ -16,6 +16,7 @@
package com.android.systemui.accessibility.floatingmenu;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.util.MathUtils.constrain;
import static android.util.MathUtils.sq;
import static android.view.WindowInsets.Type.ime;
@@ -200,6 +201,7 @@
mListView = listView;
mWindowManager = context.getSystemService(WindowManager.class);
+ mLastConfiguration = new Configuration(getResources().getConfiguration());
mAdapter = new AccessibilityTargetAdapter(mTargets);
mUiHandler = createUiHandler();
mPosition = position;
@@ -243,7 +245,6 @@
}
});
- mLastConfiguration = new Configuration(getResources().getConfiguration());
initListView();
updateStrokeWith(getResources().getConfiguration().uiMode, mAlignment);
@@ -567,8 +568,10 @@
final int currentX = (int) event.getX();
final int currentY = (int) event.getY();
+ final int marginStartEnd = getMarginStartEndWith(mLastConfiguration);
final Rect touchDelegateBounds =
- new Rect(mMargin, mMargin, mMargin + getLayoutWidth(), mMargin + getLayoutHeight());
+ new Rect(marginStartEnd, mMargin, marginStartEnd + getLayoutWidth(),
+ mMargin + getLayoutHeight());
if (action == MotionEvent.ACTION_DOWN
&& touchDelegateBounds.contains(currentX, currentY)) {
mIsDownInEnlargedTouchArea = true;
@@ -682,15 +685,13 @@
mListView.setLayoutManager(layoutManager);
mListView.addOnItemTouchListener(this);
mListView.animate().setInterpolator(new OvershootInterpolator());
- updateListView();
+ updateListViewWith(mLastConfiguration);
addView(mListView);
}
- private void updateListView() {
- final LayoutParams layoutParams = (FrameLayout.LayoutParams) mListView.getLayoutParams();
- layoutParams.setMargins(mMargin, mMargin, mMargin, mMargin);
- mListView.setLayoutParams(layoutParams);
+ private void updateListViewWith(Configuration configuration) {
+ updateMarginWith(configuration);
final int elevation =
getResources().getDimensionPixelSize(R.dimen.accessibility_floating_menu_elevation);
@@ -719,13 +720,15 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ mLastConfiguration.setTo(newConfig);
+
final int diff = newConfig.diff(mLastConfiguration);
if ((diff & ActivityInfo.CONFIG_LOCALE) != 0) {
updateAccessibilityTitle(mCurrentLayoutParams);
}
updateDimensions();
- updateListView();
+ updateListViewWith(newConfig);
updateItemViewWith(mSizeType);
updateColor();
updateStrokeWith(newConfig.uiMode, mAlignment);
@@ -733,8 +736,6 @@
updateRadiusWith(mSizeType, mRadiusType, mTargets.size());
updateScrollModeWith(hasExceededMaxLayoutHeight());
setSystemGestureExclusion();
-
- mLastConfiguration.setTo(newConfig);
}
@VisibleForTesting
@@ -756,11 +757,11 @@
}
private int getMinWindowX() {
- return -mMargin;
+ return -getMarginStartEndWith(mLastConfiguration);
}
private int getMaxWindowX() {
- return mScreenWidth - mMargin - getLayoutWidth();
+ return mScreenWidth - getMarginStartEndWith(mLastConfiguration) - getLayoutWidth();
}
private int getMaxWindowY() {
@@ -805,6 +806,15 @@
return layoutBottomY > imeY ? (layoutBottomY - imeY) : 0;
}
+ private void updateMarginWith(Configuration configuration) {
+ // Avoid overlapping with system bars under landscape mode, update the margins of the menu
+ // to align the edge of system bars.
+ final int marginStartEnd = getMarginStartEndWith(configuration);
+ final LayoutParams layoutParams = (FrameLayout.LayoutParams) mListView.getLayoutParams();
+ layoutParams.setMargins(marginStartEnd, mMargin, marginStartEnd, mMargin);
+ mListView.setLayoutParams(layoutParams);
+ }
+
private void updateOffsetWith(@ShapeType int shapeType, @Alignment int side) {
final float halfWidth = getLayoutWidth() / 2.0f;
final float offset = (shapeType == ShapeType.OVAL) ? 0 : halfWidth;
@@ -896,6 +906,12 @@
return (mPadding + mIconHeight) * mTargets.size() + mPadding;
}
+ private int getMarginStartEndWith(Configuration configuration) {
+ return configuration != null
+ && configuration.orientation == ORIENTATION_PORTRAIT
+ ? mMargin : 0;
+ }
+
private @DimenRes int getRadiusResId(@SizeType int sizeType, int itemCount) {
return sizeType == SizeType.SMALL
? getSmallSizeResIdWith(itemCount)
@@ -932,7 +948,7 @@
}
private int getWindowWidth() {
- return mMargin * 2 + getLayoutWidth();
+ return getMarginStartEndWith(mLastConfiguration) * 2 + getLayoutWidth();
}
private int getWindowHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index cf577a3..77cca2e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -24,9 +24,13 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.Utils
+import com.android.systemui.statusbar.CircleReveal
+import com.android.systemui.statusbar.LiftReveal
+import com.android.systemui.statusbar.LightRevealEffect
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
@@ -49,10 +53,12 @@
private val commandRegistry: CommandRegistry,
private val notificationShadeWindowController: NotificationShadeWindowController,
private val bypassController: KeyguardBypassController,
+ private val biometricUnlockController: BiometricUnlockController,
rippleView: AuthRippleView?
) : ViewController<AuthRippleView>(rippleView) {
var fingerprintSensorLocation: PointF? = null
private var faceSensorLocation: PointF? = null
+ private var circleReveal: LightRevealEffect? = null
@VisibleForTesting
public override fun onViewAttached() {
@@ -96,15 +102,47 @@
private fun showRipple() {
notificationShadeWindowController.setForcePluginOpen(true, this)
- mView.startRipple(Runnable {
- notificationShadeWindowController.setForcePluginOpen(false, this)
- })
+ val biometricUnlockMode = biometricUnlockController.mode
+ val useCircleReveal = circleReveal != null &&
+ (biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK ||
+ biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING ||
+ biometricUnlockMode == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
+ val lightRevealScrim = statusBar.lightRevealScrim
+ if (useCircleReveal) {
+ lightRevealScrim?.revealEffect = circleReveal!!
+ }
+
+ mView.startRipple(
+ /* end runnable */
+ Runnable {
+ notificationShadeWindowController.setForcePluginOpen(false, this)
+ if (useCircleReveal) {
+ lightRevealScrim?.revealEffect = LiftReveal
+ }
+ },
+ /* circleReveal */
+ if (useCircleReveal) {
+ lightRevealScrim
+ } else {
+ null
+ }
+ )
}
fun updateSensorLocation() {
fingerprintSensorLocation = authController.fingerprintSensorLocation
faceSensorLocation = authController.faceAuthSensorLocation
- statusBar.updateCircleReveal()
+ fingerprintSensorLocation?.let {
+ circleReveal = CircleReveal(
+ it.x,
+ it.y,
+ 0f,
+ Math.max(
+ Math.max(it.x, statusBar.displayWidth - it.x),
+ Math.max(it.y, statusBar.displayHeight - it.y)
+ )
+ )
+ }
}
private fun updateRippleColor() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index 75373ab..dd73c4f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -31,6 +31,7 @@
import android.view.View
import android.view.animation.PathInterpolator
import com.android.internal.graphics.ColorUtils
+import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.charging.RippleShader
private const val RIPPLE_ANIMATION_DURATION: Long = 1533
@@ -70,51 +71,79 @@
.toFloat()
}
- fun startRipple(onAnimationEnd: Runnable?) {
+ fun startRipple(onAnimationEnd: Runnable?, lightReveal: LightRevealScrim?) {
if (rippleInProgress) {
return // Ignore if ripple effect is already playing
}
- val animator = ValueAnimator.ofFloat(0f, 1f)
- animator.interpolator = PathInterpolator(0.4f, 0f, 0f, 1f)
- animator.duration = RIPPLE_ANIMATION_DURATION
- animator.addUpdateListener { animator ->
- val now = animator.currentPlayTime
- rippleShader.progress = animator.animatedValue as Float
- rippleShader.time = now.toFloat()
- rippleShader.distortionStrength = 1 - rippleShader.progress
- invalidate()
- }
- val alphaInAnimator = ValueAnimator.ofInt(0, 127)
- alphaInAnimator.duration = 167
- alphaInAnimator.addUpdateListener { alphaInAnimator ->
- rippleShader.color = ColorUtils.setAlphaComponent(rippleShader.color,
- alphaInAnimator.animatedValue as Int)
- invalidate()
- }
- val alphaOutAnimator = ValueAnimator.ofInt(127, 0)
- alphaOutAnimator.startDelay = 417
- alphaOutAnimator.duration = 1116
- alphaOutAnimator.addUpdateListener { alphaOutAnimator ->
- rippleShader.color = ColorUtils.setAlphaComponent(rippleShader.color,
- alphaOutAnimator.animatedValue as Int)
- invalidate()
+ val rippleAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
+ interpolator = PathInterpolator(0.4f, 0f, 0f, 1f)
+ duration = RIPPLE_ANIMATION_DURATION
+ addUpdateListener { animator ->
+ val now = animator.currentPlayTime
+ rippleShader.progress = animator.animatedValue as Float
+ rippleShader.time = now.toFloat()
+
+ lightReveal?.revealAmount = animator.animatedValue as Float
+ invalidate()
+ }
}
- val animatorSet = AnimatorSet()
- animatorSet.playTogether(animator, alphaInAnimator, alphaOutAnimator)
- animatorSet.addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator?) {
- onAnimationEnd?.run()
- rippleInProgress = false
- visibility = GONE
+ val revealAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
+ interpolator = rippleAnimator.interpolator
+ startDelay = 10
+ duration = rippleAnimator.duration
+ addUpdateListener { animator ->
+ lightReveal?.revealAmount = animator.animatedValue as Float
}
- })
+ }
+
+ val alphaInAnimator = ValueAnimator.ofInt(0, 127).apply {
+ duration = 167
+ addUpdateListener { animator ->
+ rippleShader.color = ColorUtils.setAlphaComponent(
+ rippleShader.color,
+ animator.animatedValue as Int
+ )
+ invalidate()
+ }
+ }
+
+ val alphaOutAnimator = ValueAnimator.ofInt(127, 0).apply {
+ startDelay = 417
+ duration = 1116
+ addUpdateListener { animator ->
+ rippleShader.color = ColorUtils.setAlphaComponent(
+ rippleShader.color,
+ animator.animatedValue as Int
+ )
+ invalidate()
+ }
+ }
+
+ val animatorSet = AnimatorSet().apply {
+ playTogether(
+ rippleAnimator,
+ revealAnimator,
+ alphaInAnimator,
+ alphaOutAnimator
+ )
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animation: Animator?) {
+ rippleInProgress = true
+ visibility = VISIBLE
+ }
+
+ override fun onAnimationEnd(animation: Animator?) {
+ onAnimationEnd?.run()
+ rippleInProgress = false
+ visibility = GONE
+ }
+ })
+ }
// TODO (b/185124905): custom haptic TBD
// vibrate()
animatorSet.start()
- visibility = VISIBLE
- rippleInProgress = true
}
fun setColor(color: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index ec930b0..3d2c4e1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -16,6 +16,8 @@
package com.android.systemui.biometrics;
+import static android.os.VibrationEffect.Composition.PRIMITIVE_LOW_TICK;
+
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
@@ -67,9 +69,11 @@
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.concurrency.Execution;
import java.util.Optional;
@@ -96,6 +100,7 @@
private static final long MIN_TOUCH_LOG_INTERVAL = 50;
private final Context mContext;
+ private final Execution mExecution;
private final FingerprintManager mFingerprintManager;
@NonNull private final LayoutInflater mInflater;
private final WindowManager mWindowManager;
@@ -111,6 +116,7 @@
@NonNull private final FalsingManager mFalsingManager;
@NonNull private final PowerManager mPowerManager;
@NonNull private final AccessibilityManager mAccessibilityManager;
+ @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
@Nullable private final UdfpsHbmProvider mHbmProvider;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@@ -143,32 +149,22 @@
private Runnable mAodInterruptRunnable;
@VisibleForTesting
- static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
+ public static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
.build();
- private final VibrationEffect mEffectTick = VibrationEffect.get(VibrationEffect.EFFECT_TICK);
- private final VibrationEffect mEffectTextureTick =
+ public static final VibrationEffect EFFECT_TICK =
+ VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+ private static final VibrationEffect EFFECT_TEXTURE_TICK =
VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
@VisibleForTesting
- final VibrationEffect mEffectClick = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
- private final VibrationEffect mEffectHeavy =
+ static final VibrationEffect EFFECT_CLICK = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ private static final VibrationEffect EFFECT_HEAVY =
VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
- private final VibrationEffect mDoubleClick =
+ private static final VibrationEffect EFFECT_DOUBLE_CLICK =
VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
- private final Runnable mAcquiredVibration = new Runnable() {
- @Override
- public void run() {
- if (mVibrator == null) {
- return;
- }
- String effect = Settings.Global.getString(mContext.getContentResolver(),
- "udfps_acquired_type");
- mVibrator.vibrate(getVibration(effect, mEffectTick), VIBRATION_SONIFICATION_ATTRIBUTES);
- }
- };
private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
@Override
@@ -445,16 +441,7 @@
String startEffectSetting = Settings.Global.getString(
contentResolver, "udfps_start_type");
mVibrator.vibrate(getVibration(startEffectSetting,
- mEffectClick), VIBRATION_SONIFICATION_ATTRIBUTES);
- }
-
- int acquiredEnabled = Settings.Global.getInt(contentResolver,
- "udfps_acquired", 0);
- if (acquiredEnabled > 0) {
- int delay = Settings.Global.getInt(contentResolver,
- "udfps_acquired_delay", 500);
- mMainHandler.removeCallbacks(mAcquiredVibration);
- mMainHandler.postDelayed(mAcquiredVibration, delay);
+ EFFECT_CLICK), VIBRATION_SONIFICATION_ATTRIBUTES);
}
}
@@ -494,6 +481,7 @@
@Inject
public UdfpsController(@NonNull Context context,
+ @NonNull Execution execution,
@NonNull LayoutInflater inflater,
@Nullable FingerprintManager fingerprintManager,
@NonNull WindowManager windowManager,
@@ -507,10 +495,12 @@
@NonNull FalsingManager falsingManager,
@NonNull PowerManager powerManager,
@NonNull AccessibilityManager accessibilityManager,
+ @NonNull LockscreenShadeTransitionController lockscreenShadeTransitionController,
@NonNull ScreenLifecycle screenLifecycle,
@Nullable Vibrator vibrator,
@NonNull Optional<UdfpsHbmProvider> hbmProvider) {
mContext = context;
+ mExecution = execution;
// TODO (b/185124905): inject main handler and vibrator once done prototyping
mMainHandler = new Handler(Looper.getMainLooper());
mVibrator = vibrator;
@@ -529,6 +519,7 @@
mFalsingManager = falsingManager;
mPowerManager = powerManager;
mAccessibilityManager = accessibilityManager;
+ mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mHbmProvider = hbmProvider.orElse(null);
screenLifecycle.addObserver(mScreenObserver);
mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
@@ -716,6 +707,7 @@
mFgExecutor,
mDumpManager,
mKeyguardViewMediator,
+ mLockscreenShadeTransitionController,
this
);
case IUdfpsOverlayController.REASON_AUTH_BP:
@@ -815,8 +807,8 @@
mIsAodInterruptActive = false;
}
- // This method can be called from the UI thread.
private void onFingerDown(int x, int y, float minor, float major) {
+ mExecution.assertIsMainThread();
if (mView == null) {
Log.w(TAG, "Null view in onFingerDown");
return;
@@ -830,11 +822,10 @@
});
}
- // This method can be called from the UI thread.
private void onFingerUp() {
+ mExecution.assertIsMainThread();
mActivePointerId = -1;
mGoodCaptureReceived = false;
- mMainHandler.removeCallbacks(mAcquiredVibration);
if (mView == null) {
Log.w(TAG, "Null view in onFingerUp");
return;
@@ -846,23 +837,34 @@
}
- private VibrationEffect getVibration(String effect, VibrationEffect defaultEffect) {
+ /**
+ * get vibration to play given string
+ * used for testing purposes (b/185124905)
+ */
+ public static VibrationEffect getVibration(String effect, VibrationEffect defaultEffect) {
if (TextUtils.isEmpty(effect)) {
return defaultEffect;
}
switch (effect.toLowerCase()) {
case "click":
- return mEffectClick;
+ return EFFECT_CLICK;
case "heavy":
- return mEffectHeavy;
+ return EFFECT_HEAVY;
case "texture_tick":
- return mEffectTextureTick;
+ return EFFECT_TEXTURE_TICK;
case "tick":
- return mEffectTick;
+ return EFFECT_TICK;
case "double_tap":
- return mDoubleClick;
+ return EFFECT_DOUBLE_CLICK;
default:
+ try {
+ int primitive = Integer.parseInt(effect);
+ if (primitive <= PRIMITIVE_LOW_TICK && primitive > -1) {
+ return VibrationEffect.startComposition().addPrimitive(primitive).compose();
+ }
+ } catch (NumberFormatException e) {
+ }
return defaultEffect;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 00888df..35ca470 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -31,6 +31,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -54,6 +55,7 @@
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@NonNull private final DelayableExecutor mExecutor;
@NonNull private final KeyguardViewMediator mKeyguardViewMediator;
+ @NonNull private final LockscreenShadeTransitionController mLockScreenShadeTransitionController;
@NonNull private final UdfpsController mUdfpsController;
@Nullable private Runnable mCancelDelayedHintRunnable;
@@ -63,6 +65,7 @@
private boolean mFaceDetectRunning;
private boolean mHintShown;
private int mStatusBarState;
+ private float mTransitionToFullShadeProgress;
/**
* hidden amount of pin/pattern/password bouncer
@@ -81,12 +84,14 @@
@NonNull DelayableExecutor mainDelayableExecutor,
@NonNull DumpManager dumpManager,
@NonNull KeyguardViewMediator keyguardViewMediator,
+ @NonNull LockscreenShadeTransitionController transitionController,
@NonNull UdfpsController udfpsController) {
super(view, statusBarStateController, statusBar, dumpManager);
mKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mExecutor = mainDelayableExecutor;
mKeyguardViewMediator = keyguardViewMediator;
+ mLockScreenShadeTransitionController = transitionController;
mUdfpsController = udfpsController;
}
@@ -116,6 +121,7 @@
updatePauseAuth();
mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
+ mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(this);
}
@Override
@@ -127,6 +133,9 @@
mStatusBarStateController.removeCallback(mStateListener);
mKeyguardViewManager.removeAlternateAuthInterceptor(mAlternateAuthInterceptor);
mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
+ if (mLockScreenShadeTransitionController.getUdfpsKeyguardViewController() == this) {
+ mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
+ }
if (mCancelDelayedHintRunnable != null) {
mCancelDelayedHintRunnable.run();
@@ -256,11 +265,21 @@
}
}
+ /**
+ * Set the progress we're currently transitioning to the full shade. 0.0f means we're not
+ * transitioning yet, while 1.0f means we've fully dragged down.
+ */
+ public void setTransitionToFullShadeProgress(float progress) {
+ mTransitionToFullShadeProgress = progress;
+ updateAlpha();
+ }
+
private void updateAlpha() {
// fade icon on transition to showing bouncer
int alpha = mShowingUdfpsBouncer ? 255
: Math.abs((int) MathUtils.constrainedMap(0f, 255f, .4f, .7f,
mInputBouncerHiddenAmount));
+ alpha *= (1.0f - mTransitionToFullShadeProgress);
mView.setUnpausedAlpha(alpha);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
index 6a6f57a..77fad35 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
@@ -23,42 +23,36 @@
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.RectF;
-import android.os.Build;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
-import com.android.systemui.biometrics.UdfpsHbmTypes.HbmType;
-
/**
- * Under-display fingerprint sensor Surface View. The surface should be used for HBM-specific things
- * only. All other animations should be done on the other view.
+ * Surface View for providing the Global High-Brightness Mode (GHBM) illumination for UDFPS.
*/
-public class UdfpsSurfaceView extends SurfaceView implements UdfpsIlluminator {
+public class UdfpsSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "UdfpsSurfaceView";
- private static final String SETTING_HBM_TYPE =
- "com.android.systemui.biometrics.UdfpsSurfaceView.hbmType";
- private static final @HbmType int DEFAULT_HBM_TYPE = UdfpsHbmTypes.GLOBAL_HBM;
/**
- * This is used instead of {@link android.graphics.drawable.Drawable}, because the latter has
- * several abstract methods that are not used here but require implementation.
+ * Notifies {@link UdfpsView} when to enable GHBM illumination.
*/
- private interface SimpleDrawable {
- void draw(Canvas canvas);
+ interface GhbmIlluminationListener {
+ /**
+ * @param surface the surface for which GHBM should be enabled.
+ * @param onIlluminatedRunnable a runnable that should be run after GHBM is enabled.
+ */
+ void enableGhbm(@NonNull Surface surface, @Nullable Runnable onIlluminatedRunnable);
}
@NonNull private final SurfaceHolder mHolder;
@NonNull private final Paint mSensorPaint;
- @NonNull private final SimpleDrawable mIlluminationDotDrawable;
- private final @HbmType int mHbmType;
- @NonNull private RectF mSensorRect;
- @Nullable private UdfpsHbmProvider mHbmProvider;
+ @Nullable private GhbmIlluminationListener mGhbmIlluminationListener;
+ @Nullable private Runnable mOnIlluminatedRunnable;
+ boolean mAwaitingSurfaceToStartIllumination;
+ boolean mHasValidSurface;
public UdfpsSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -70,80 +64,77 @@
setZOrderOnTop(true);
mHolder = getHolder();
+ mHolder.addCallback(this);
mHolder.setFormat(PixelFormat.RGBA_8888);
- mSensorRect = new RectF();
mSensorPaint = new Paint(0 /* flags */);
mSensorPaint.setAntiAlias(true);
mSensorPaint.setARGB(255, 255, 255, 255);
mSensorPaint.setStyle(Paint.Style.FILL);
+ }
- mIlluminationDotDrawable = canvas -> {
- canvas.drawOval(mSensorRect, mSensorPaint);
- };
-
- if (Build.IS_ENG || Build.IS_USERDEBUG) {
- mHbmType = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- SETTING_HBM_TYPE, DEFAULT_HBM_TYPE, UserHandle.USER_CURRENT);
- } else {
- mHbmType = DEFAULT_HBM_TYPE;
+ @Override public void surfaceCreated(SurfaceHolder holder) {
+ mHasValidSurface = true;
+ if (mAwaitingSurfaceToStartIllumination) {
+ doIlluminate(mOnIlluminatedRunnable);
+ mOnIlluminatedRunnable = null;
+ mAwaitingSurfaceToStartIllumination = false;
}
}
@Override
- public void setHbmProvider(@Nullable UdfpsHbmProvider hbmProvider) {
- mHbmProvider = hbmProvider;
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ // Unused.
}
- @Override
- public void startIllumination(@Nullable Runnable onIlluminatedRunnable) {
- if (mHbmProvider != null) {
- final Surface surface =
- (mHbmType == UdfpsHbmTypes.GLOBAL_HBM) ? mHolder.getSurface() : null;
-
- final Runnable onHbmEnabled = () -> {
- if (mHbmType == UdfpsHbmTypes.GLOBAL_HBM) {
- drawImmediately(mIlluminationDotDrawable);
- }
- if (onIlluminatedRunnable != null) {
- // No framework API can reliably tell when a frame reaches the panel. A timeout
- // is the safest solution. The frame should be displayed within 3 refresh
- // cycles, which on a 60 Hz panel equates to 50 milliseconds.
- postDelayed(onIlluminatedRunnable, 50 /* delayMillis */);
- } else {
- Log.w(TAG, "startIllumination | onIlluminatedRunnable is null");
- }
- };
-
- mHbmProvider.enableHbm(mHbmType, surface, onHbmEnabled);
- } else {
- Log.e(TAG, "startIllumination | mHbmProvider is null");
- }
+ @Override public void surfaceDestroyed(SurfaceHolder holder) {
+ mHasValidSurface = false;
}
- @Override
- public void stopIllumination() {
- if (mHbmProvider != null) {
- final Runnable onHbmDisabled =
- (mHbmType == UdfpsHbmTypes.GLOBAL_HBM) ? this::invalidate : null;
- mHbmProvider.disableHbm(onHbmDisabled);
- } else {
- Log.e(TAG, "stopIllumination | mHbmProvider is null");
- }
- }
-
- void onSensorRectUpdated(@NonNull RectF sensorRect) {
- mSensorRect = sensorRect;
+ void setGhbmIlluminationListener(@Nullable GhbmIlluminationListener listener) {
+ mGhbmIlluminationListener = listener;
}
/**
- * Immediately draws the provided drawable on this SurfaceView's surface.
+ * Note: there is no corresponding method to stop GHBM illumination. It is expected that
+ * {@link UdfpsView} will hide this view, which would destroy the surface and remove the
+ * illumination dot.
*/
- private void drawImmediately(@NonNull SimpleDrawable drawable) {
+ void startGhbmIllumination(@Nullable Runnable onIlluminatedRunnable) {
+ if (mGhbmIlluminationListener == null) {
+ Log.e(TAG, "startIllumination | mGhbmIlluminationListener is null");
+ return;
+ }
+
+ if (mHasValidSurface) {
+ doIlluminate(onIlluminatedRunnable);
+ } else {
+ mAwaitingSurfaceToStartIllumination = true;
+ mOnIlluminatedRunnable = onIlluminatedRunnable;
+ }
+ }
+
+ private void doIlluminate(@Nullable Runnable onIlluminatedRunnable) {
+ if (mGhbmIlluminationListener == null) {
+ Log.e(TAG, "doIlluminate | mGhbmIlluminationListener is null");
+ return;
+ }
+
+ mGhbmIlluminationListener.enableGhbm(mHolder.getSurface(), onIlluminatedRunnable);
+ }
+
+ /**
+ * Immediately draws the illumination dot on this SurfaceView's surface.
+ */
+ void drawIlluminationDot(@NonNull RectF sensorRect) {
+ if (!mHasValidSurface) {
+ Log.e(TAG, "drawIlluminationDot | the surface is destroyed or was never created.");
+ return;
+ }
Canvas canvas = null;
try {
canvas = mHolder.lockCanvas();
- drawable.draw(canvas);
+ canvas.drawOval(sensorRect, mSensorPaint);
} finally {
// Make sure the surface is never left in a bad state.
if (canvas != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index 5e5584c..15f77ff 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -26,14 +26,19 @@
import android.graphics.PointF;
import android.graphics.RectF;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.os.Build;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
+import android.view.Surface;
import android.view.View;
import android.widget.FrameLayout;
import com.android.systemui.R;
+import com.android.systemui.biometrics.UdfpsHbmTypes.HbmType;
import com.android.systemui.doze.DozeReceiver;
/**
@@ -43,18 +48,25 @@
public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIlluminator {
private static final String TAG = "UdfpsView";
+ private static final String SETTING_HBM_TYPE =
+ "com.android.systemui.biometrics.UdfpsSurfaceView.hbmType";
+ private static final @HbmType int DEFAULT_HBM_TYPE = UdfpsHbmTypes.LOCAL_HBM;
+
private static final int DEBUG_TEXT_SIZE_PX = 32;
@NonNull private final RectF mSensorRect;
@NonNull private final Paint mDebugTextPaint;
+ private final float mSensorTouchAreaCoefficient;
+ private final int mOnIlluminatedDelayMs;
+ private final @HbmType int mHbmType;
- @NonNull private UdfpsSurfaceView mHbmSurfaceView;
+ // Only used for UdfpsHbmTypes.GLOBAL_HBM.
+ @Nullable private UdfpsSurfaceView mGhbmView;
+ // Can be different for enrollment, BiometricPrompt, Keyguard, etc.
@Nullable private UdfpsAnimationViewController mAnimationViewController;
-
// Used to obtain the sensor location.
@NonNull private FingerprintSensorPropertiesInternal mSensorProps;
-
- private final float mSensorTouchAreaCoefficient;
+ @Nullable private UdfpsHbmProvider mHbmProvider;
@Nullable private String mDebugMessage;
private boolean mIlluminationRequested;
@@ -81,7 +93,15 @@
mDebugTextPaint.setColor(Color.BLUE);
mDebugTextPaint.setTextSize(DEBUG_TEXT_SIZE_PX);
- mIlluminationRequested = false;
+ mOnIlluminatedDelayMs = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_udfps_illumination_transition_ms);
+
+ if (Build.IS_ENG || Build.IS_USERDEBUG) {
+ mHbmType = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ SETTING_HBM_TYPE, DEFAULT_HBM_TYPE, UserHandle.USER_CURRENT);
+ } else {
+ mHbmType = DEFAULT_HBM_TYPE;
+ }
}
// Don't propagate any touch events to the child views.
@@ -93,7 +113,9 @@
@Override
protected void onFinishInflate() {
- mHbmSurfaceView = findViewById(R.id.hbm_view);
+ if (mHbmType == UdfpsHbmTypes.GLOBAL_HBM) {
+ mGhbmView = findViewById(R.id.hbm_view);
+ }
}
void setSensorProperties(@NonNull FingerprintSensorPropertiesInternal properties) {
@@ -102,7 +124,7 @@
@Override
public void setHbmProvider(@Nullable UdfpsHbmProvider hbmProvider) {
- mHbmSurfaceView.setHbmProvider(hbmProvider);
+ mHbmProvider = hbmProvider;
}
@Override
@@ -125,7 +147,6 @@
2 * mSensorProps.sensorRadius + paddingX,
2 * mSensorProps.sensorRadius + paddingY);
- mHbmSurfaceView.onSensorRectUpdated(new RectF(mSensorRect));
if (mAnimationViewController != null) {
mAnimationViewController.onSensorRectUpdated(new RectF(mSensorRect));
}
@@ -204,8 +225,32 @@
if (mAnimationViewController != null) {
mAnimationViewController.onIlluminationStarting();
}
- mHbmSurfaceView.setVisibility(View.VISIBLE);
- mHbmSurfaceView.startIllumination(onIlluminatedRunnable);
+
+ if (mGhbmView != null) {
+ mGhbmView.setGhbmIlluminationListener(this::doIlluminate);
+ mGhbmView.setVisibility(View.VISIBLE);
+ mGhbmView.startGhbmIllumination(onIlluminatedRunnable);
+ } else {
+ doIlluminate(null /* surface */, onIlluminatedRunnable);
+ }
+ }
+
+ private void doIlluminate(@Nullable Surface surface, @Nullable Runnable onIlluminatedRunnable) {
+ if (mGhbmView != null && surface == null) {
+ Log.e(TAG, "doIlluminate | surface must be non-null for GHBM");
+ }
+ mHbmProvider.enableHbm(mHbmType, surface, () -> {
+ if (mGhbmView != null) {
+ mGhbmView.drawIlluminationDot(mSensorRect);
+ }
+ if (onIlluminatedRunnable != null) {
+ // No framework API can reliably tell when a frame reaches the panel. A timeout
+ // is the safest solution.
+ postDelayed(onIlluminatedRunnable, mOnIlluminatedDelayMs);
+ } else {
+ Log.w(TAG, "doIlluminate | onIlluminatedRunnable is null");
+ }
+ });
}
@Override
@@ -214,7 +259,10 @@
if (mAnimationViewController != null) {
mAnimationViewController.onIlluminationStopped();
}
- mHbmSurfaceView.setVisibility(View.INVISIBLE);
- mHbmSurfaceView.stopIllumination();
+ if (mGhbmView != null) {
+ mGhbmView.setGhbmIlluminationListener(null);
+ mGhbmView.setVisibility(View.INVISIBLE);
+ }
+ mHbmProvider.disableHbm(null /* onHbmDisabled */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 2dbf30f..de8ed70 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -25,6 +25,7 @@
import com.android.systemui.screenrecord.ScreenRecordDialog;
import com.android.systemui.screenshot.LongScreenshotActivity;
import com.android.systemui.sensorprivacy.SensorUseStartedActivity;
+import com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity;
import com.android.systemui.settings.brightness.BrightnessDialog;
import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity;
import com.android.systemui.tuner.TunerActivity;
@@ -120,4 +121,10 @@
@IntoMap
@ClassKey(SensorUseStartedActivity.class)
public abstract Activity bindSensorUseStartedActivity(SensorUseStartedActivity activity);
+
+ /** Inject into TvUnblockSensorActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(TvUnblockSensorActivity.class)
+ public abstract Activity bindTvUnblockSensorActivity(TvUnblockSensorActivity activity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 746621d..d85c9a7 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -231,7 +231,8 @@
@Main Handler mainHandler,
UiEventLogger uiEventLogger,
NavigationBarOverlayController navBarOverlayController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ UserTracker userTracker) {
return new NavigationBarController(context,
windowManager,
assistManagerLazy,
@@ -256,7 +257,8 @@
mainHandler,
uiEventLogger,
navBarOverlayController,
- configurationController);
+ configurationController,
+ userTracker);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 053d75d..954ba79 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -63,6 +63,7 @@
import android.telecom.TelecomManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.view.CrossWindowBlurListeners;
import android.view.IWindowManager;
import android.view.ViewConfiguration;
import android.view.WindowManager;
@@ -139,6 +140,12 @@
}
@Provides
+ @Singleton
+ static CrossWindowBlurListeners provideCrossWindowBlurListeners() {
+ return CrossWindowBlurListeners.getInstance();
+ }
+
+ @Provides
@DisplayId
static int provideDisplayId(Context context) {
return context.getDisplayId();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index ff95604..455f3c0 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -38,6 +38,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.doze.DozeMachine.State;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.Assert;
@@ -70,7 +71,6 @@
* Assuming that the screen should start on.
*/
private static boolean sWakeDisplaySensorState = true;
- private Runnable mQuickPickupDozeCancellable;
private static final int PROXIMITY_TIMEOUT_DELAY_MS = 500;
@@ -96,6 +96,7 @@
private long mNotificationPulseTime;
private boolean mPulsePending;
+ private Runnable mAodInterruptRunnable;
/** see {@link #onProximityFar} prox for callback */
private boolean mWantProxSensor;
@@ -277,14 +278,14 @@
boolean isWakeDisplayEvent = isQuickPickup || ((isWakeOnPresence || isWakeOnReach)
&& rawValues != null && rawValues.length > 0 && rawValues[0] != 0);
- if (isWakeOnPresence || isQuickPickup) {
- onWakeScreen(isQuickPickup || isWakeDisplayEvent,
+ if (isWakeOnPresence) {
+ onWakeScreen(isWakeDisplayEvent,
mMachine.isExecutingTransition() ? null : mMachine.getState(),
pulseReason);
} else if (isLongPress) {
requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
null /* onPulseSuppressedListener */);
- } else if (isWakeOnReach) {
+ } else if (isWakeOnReach || isQuickPickup) {
if (isWakeDisplayEvent) {
requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
null /* onPulseSuppressedListener */);
@@ -303,11 +304,16 @@
} else if (isPickup) {
gentleWakeUp(pulseReason);
} else if (isUdfpsLongPress) {
+ final State state = mMachine.getState();
+ if (state == State.DOZE_AOD || state == State.DOZE) {
+ // Since the gesture won't be received by the UDFPS view, we need to
+ // manually inject an event once the display is ON
+ mAodInterruptRunnable = () ->
+ mAuthController.onAodInterrupt((int) screenX, (int) screenY,
+ rawValues[3] /* major */, rawValues[4] /* minor */);
+ }
+
requestPulse(DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS, true, null);
- // Since the gesture won't be received by the UDFPS view, manually inject an
- // event.
- mAuthController.onAodInterrupt((int) screenX, (int) screenY,
- rawValues[3] /* major */, rawValues[4] /* minor */);
} else {
mDozeHost.extendPulse(pulseReason);
}
@@ -381,11 +387,7 @@
*/
private void onWakeScreen(boolean wake, @Nullable DozeMachine.State state, int reason) {
mDozeLog.traceWakeDisplay(wake, reason);
- final boolean isWakeOnPresence = reason == DozeLog.REASON_SENSOR_WAKE_UP;
- final boolean isQuickPickup = reason == DozeLog.REASON_SENSOR_QUICK_PICKUP;
- if (isWakeOnPresence) {
- sWakeDisplaySensorState = wake;
- }
+ sWakeDisplaySensorState = wake;
if (wake) {
proximityCheckThenCall((result) -> {
@@ -398,27 +400,13 @@
// Log sensor triggered
Optional.ofNullable(DozingUpdateUiEvent.fromReason(reason))
.ifPresent(mUiEventLogger::log);
-
- if (isQuickPickup) {
- // schedule runnable to go back to DOZE
- onQuickPickup();
- }
- } else if (state == DozeMachine.State.DOZE_AOD && isQuickPickup) {
- // elongate time in DOZE_AOD, schedule new runnable to go back to DOZE
- onQuickPickup();
}
- }, isQuickPickup /* alreadyPerformedProxCheck */, reason);
+ }, false /* alreadyPerformedProxCheck */, reason);
} else {
boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
- boolean pulse = (state == DozeMachine.State.DOZE_REQUEST_PULSE)
- || (state == DozeMachine.State.DOZE_PULSING)
- || (state == DozeMachine.State.DOZE_PULSING_BRIGHT);
- boolean docked = (state == DozeMachine.State.DOZE_AOD_DOCKED);
+
if (!pausing && !paused) {
- if (isQuickPickup && (pulse || docked)) {
- return;
- }
mMachine.requestState(DozeMachine.State.DOZE);
// log wake timeout
mUiEventLogger.log(DozingUpdateUiEvent.DOZING_UPDATE_WAKE_TIMEOUT);
@@ -426,19 +414,11 @@
}
}
- private void onQuickPickup() {
- cancelQuickPickupDelayableDoze();
- mQuickPickupDozeCancellable = mMainExecutor.executeDelayed(() -> {
- onWakeScreen(false,
- mMachine.isExecutingTransition() ? null : mMachine.getState(),
- DozeLog.REASON_SENSOR_QUICK_PICKUP);
- }, mDozeParameters.getQuickPickupAodDuration());
- }
-
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
case INITIALIZED:
+ mAodInterruptRunnable = null;
sWakeDisplaySensorState = true;
mBroadcastReceiver.register(mBroadcastDispatcher);
mDozeHost.addCallback(mHostCallback);
@@ -448,6 +428,7 @@
break;
case DOZE:
case DOZE_AOD:
+ mAodInterruptRunnable = null;
mWantProxSensor = newState != DozeMachine.State.DOZE;
mWantSensors = true;
mWantTouchScreenSensors = true;
@@ -472,7 +453,6 @@
mDozeSensors.requestTemporaryDisable();
break;
case FINISH:
- cancelQuickPickupDelayableDoze();
mBroadcastReceiver.unregister(mBroadcastDispatcher);
mDozeHost.removeCallback(mHostCallback);
mDockManager.removeListener(mDockEventListener);
@@ -494,19 +474,14 @@
|| state == Display.STATE_DOZE_SUSPEND || state == Display.STATE_OFF;
mDozeSensors.setProxListening(mWantProxSensor && lowPowerStateOrOff);
mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors, lowPowerStateOrOff);
- }
- /**
- * Cancels last scheduled Runnable that transitions to STATE_DOZE (blank screen) after
- * going into STATE_AOD (AOD screen) from the quick pickup gesture.
- */
- private void cancelQuickPickupDelayableDoze() {
- if (mQuickPickupDozeCancellable != null) {
- mQuickPickupDozeCancellable.run();
- mQuickPickupDozeCancellable = null;
+ if (mAodInterruptRunnable != null && state == Display.STATE_ON) {
+ mAodInterruptRunnable.run();
+ mAodInterruptRunnable = null;
}
}
+
private void checkTriggersAtInit() {
if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR
|| mDozeHost.isBlockingDoze()
@@ -576,6 +551,8 @@
@Override
public void dump(PrintWriter pw) {
+ pw.println(" mAodInterruptRunnable=" + mAodInterruptRunnable);
+
pw.print(" notificationPulseTime=");
pw.println(Formatter.formatShortElapsedTime(mContext, mNotificationPulseTime));
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index ff3cb21..fbe06b0 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -21,6 +21,7 @@
import android.app.AlarmManager;
import android.content.Context;
+import android.content.res.Configuration;
import android.os.Handler;
import android.os.SystemClock;
import android.provider.Settings;
@@ -34,6 +35,7 @@
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.AlarmTimeout;
import com.android.systemui.util.wakelock.WakeLock;
@@ -48,7 +50,8 @@
* The policy controlling doze.
*/
@DozeScope
-public class DozeUi implements DozeMachine.Part, TunerService.Tunable {
+public class DozeUi implements DozeMachine.Part, TunerService.Tunable,
+ ConfigurationController.ConfigurationListener {
// if enabled, calls dozeTimeTick() whenever the time changes:
private static final boolean BURN_IN_TESTING_ENABLED = false;
private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min
@@ -63,6 +66,7 @@
private final DozeLog mDozeLog;
private final Lazy<StatusBarStateController> mStatusBarStateController;
private final TunerService mTunerService;
+ private final ConfigurationController mConfigurationController;
private boolean mKeyguardShowing;
private final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback =
@@ -84,6 +88,11 @@
mHandler.post(mWakeLock.wrap(() -> {}));
}
}
+
+ @Override
+ public void onShadeExpandedChanged(boolean expanded) {
+ updateAnimateScreenOff();
+ }
};
private long mLastTimeTickElapsed = 0;
@@ -93,7 +102,8 @@
WakeLock wakeLock, DozeHost host, @Main Handler handler,
DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor,
DozeLog dozeLog, TunerService tunerService,
- Lazy<StatusBarStateController> statusBarStateController) {
+ Lazy<StatusBarStateController> statusBarStateController,
+ ConfigurationController configurationController) {
mContext = context;
mWakeLock = wakeLock;
mHost = host;
@@ -107,11 +117,15 @@
mStatusBarStateController = statusBarStateController;
mTunerService.addTunable(this, Settings.Secure.DOZE_ALWAYS_ON);
+
+ mConfigurationController = configurationController;
+ mConfigurationController.addCallback(this);
}
@Override
public void destroy() {
mTunerService.removeTunable(this);
+ mConfigurationController.removeCallback(this);
}
@Override
@@ -274,4 +288,9 @@
updateAnimateScreenOff();
}
}
+
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ updateAnimateScreenOff();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index dfd85fe..bc4ced4 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -30,6 +30,7 @@
import android.app.trust.TrustManager;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
@@ -70,8 +71,8 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -101,7 +102,6 @@
private final LockPatternUtils mLockPatternUtils;
private final KeyguardStateController mKeyguardStateController;
- private final NotificationShadeDepthController mDepthController;
private final SysUiState mSysUiState;
private final ActivityStarter mActivityStarter;
private final SysuiColorExtractor mSysuiColorExtractor;
@@ -112,84 +112,123 @@
@VisibleForTesting
boolean mShowLockScreenCards = false;
+ private final KeyguardStateController.Callback mKeyguardStateControllerListener =
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onUnlockedChanged() {
+ if (mDialog != null) {
+ ActionsDialog dialog = (ActionsDialog) mDialog;
+ boolean unlocked = mKeyguardStateController.isUnlocked();
+ if (dialog.mWalletViewController != null) {
+ dialog.mWalletViewController.onDeviceLockStateChanged(!unlocked);
+ }
+
+ if (unlocked) {
+ dialog.hideLockMessage();
+ }
+ }
+ }
+ };
+
+ private final ContentObserver mSettingsObserver = new ContentObserver(mMainHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ onPowerMenuLockScreenSettingsChanged();
+ }
+ };
+
/**
* @param context everything needs a context :(
*/
@Inject
- public GlobalActionsDialog(Context context, GlobalActionsManager windowManagerFuncs,
- AudioManager audioManager, IDreamManager iDreamManager,
- DevicePolicyManager devicePolicyManager, LockPatternUtils lockPatternUtils,
+ public GlobalActionsDialog(
+ Context context,
+ GlobalActionsManager windowManagerFuncs,
+ AudioManager audioManager,
+ IDreamManager iDreamManager,
+ DevicePolicyManager devicePolicyManager,
+ LockPatternUtils lockPatternUtils,
BroadcastDispatcher broadcastDispatcher,
TelephonyListenerManager telephonyListenerManager,
- GlobalSettings globalSettings, SecureSettings secureSettings,
- @Nullable Vibrator vibrator, @Main Resources resources,
- ConfigurationController configurationController, ActivityStarter activityStarter,
- KeyguardStateController keyguardStateController, UserManager userManager,
- TrustManager trustManager, IActivityManager iActivityManager,
- @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger,
- NotificationShadeDepthController depthController, SysuiColorExtractor colorExtractor,
+ GlobalSettings globalSettings,
+ SecureSettings secureSettings,
+ @Nullable Vibrator vibrator,
+ @Main Resources resources,
+ ConfigurationController configurationController,
+ ActivityStarter activityStarter,
+ KeyguardStateController keyguardStateController,
+ UserManager userManager,
+ TrustManager trustManager,
+ IActivityManager iActivityManager,
+ @Nullable TelecomManager telecomManager,
+ MetricsLogger metricsLogger,
+ SysuiColorExtractor colorExtractor,
IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
IWindowManager iWindowManager,
@Background Executor backgroundExecutor,
UiEventLogger uiEventLogger,
- RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler) {
+ RingerModeTracker ringerModeTracker,
+ SysUiState sysUiState,
+ @Main Handler handler,
+ PackageManager packageManager,
+ StatusBar statusBar) {
- super(context, windowManagerFuncs,
- audioManager, iDreamManager,
- devicePolicyManager, lockPatternUtils,
- broadcastDispatcher, telephonyListenerManager,
- globalSettings, secureSettings,
- vibrator, resources,
+ super(context,
+ windowManagerFuncs,
+ audioManager,
+ iDreamManager,
+ devicePolicyManager,
+ lockPatternUtils,
+ broadcastDispatcher,
+ telephonyListenerManager,
+ globalSettings,
+ secureSettings,
+ vibrator,
+ resources,
configurationController,
- keyguardStateController, userManager,
- trustManager, iActivityManager,
- telecomManager, metricsLogger,
- depthController, colorExtractor,
+ keyguardStateController,
+ userManager,
+ trustManager,
+ iActivityManager,
+ telecomManager,
+ metricsLogger,
+ colorExtractor,
statusBarService,
notificationShadeWindowController,
iWindowManager,
backgroundExecutor,
uiEventLogger,
null,
- ringerModeTracker, sysUiState, handler);
+ ringerModeTracker,
+ sysUiState,
+ handler,
+ packageManager,
+ statusBar);
mLockPatternUtils = lockPatternUtils;
mKeyguardStateController = keyguardStateController;
- mDepthController = depthController;
mSysuiColorExtractor = colorExtractor;
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
mSysUiState = sysUiState;
mActivityStarter = activityStarter;
- keyguardStateController.addCallback(new KeyguardStateController.Callback() {
- @Override
- public void onUnlockedChanged() {
- if (mDialog != null) {
- ActionsDialog dialog = (ActionsDialog) mDialog;
- boolean unlocked = mKeyguardStateController.isUnlocked();
- if (dialog.mWalletViewController != null) {
- dialog.mWalletViewController.onDeviceLockStateChanged(!unlocked);
- }
- if (unlocked) {
- dialog.hideLockMessage();
- }
- }
- }
- });
+ mKeyguardStateController.addCallback(mKeyguardStateControllerListener);
// Listen for changes to show pay on the power menu while locked
onPowerMenuLockScreenSettingsChanged();
mGlobalSettings.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT),
false /* notifyForDescendants */,
- new ContentObserver(handler) {
- @Override
- public void onChange(boolean selfChange) {
- onPowerMenuLockScreenSettingsChanged();
- }
- });
+ mSettingsObserver);
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ mKeyguardStateController.removeCallback(mKeyguardStateControllerListener);
+ mGlobalSettings.unregisterContentObserver(mSettingsObserver);
}
/**
@@ -223,11 +262,11 @@
protected ActionsDialogLite createDialog() {
initDialogItems();
- mDepthController.setShowingHomeControls(true);
ActionsDialog dialog = new ActionsDialog(getContext(), mAdapter, mOverflowAdapter,
- this::getWalletViewController, mDepthController, mSysuiColorExtractor,
+ this::getWalletViewController, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
- mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter, getEventLogger());
+ mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter, getEventLogger(),
+ getStatusBar());
if (shouldShowLockMessage(dialog)) {
dialog.showLockMessage();
@@ -291,15 +330,16 @@
ActionsDialog(Context context, MyAdapter adapter, MyOverflowAdapter overflowAdapter,
Provider<GlobalActionsPanelPlugin.PanelViewController> walletFactory,
- NotificationShadeDepthController depthController,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
- MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger) {
+ MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
+ StatusBar statusBar) {
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions,
- adapter, overflowAdapter, depthController, sysuiColorExtractor,
- statusBarService, notificationShadeWindowController, sysuiState,
- onRotateCallback, keyguardShowing, powerAdapter, uiEventLogger, null);
+ adapter, overflowAdapter, sysuiColorExtractor, statusBarService,
+ notificationShadeWindowController, sysuiState, onRotateCallback,
+ keyguardShowing, powerAdapter, uiEventLogger, null,
+ statusBar);
mWalletFactory = walletFactory;
// Update window attributes
@@ -447,8 +487,6 @@
float animatedValue = animation.getAnimatedFraction();
int alpha = (int) (animatedValue * mScrimAlpha * 255);
mBackgroundDrawable.setAlpha(alpha);
- mDepthController.updateGlobalDialogVisibility(animatedValue,
- mGlobalActionsLayout);
});
ObjectAnimator xAnimator =
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 8e15283..5acb303 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -74,8 +74,10 @@
import android.util.ArraySet;
import android.util.Log;
import android.view.ContextThemeWrapper;
+import android.view.GestureDetector;
import android.view.IWindowManager;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
@@ -117,8 +119,8 @@
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.scrim.ScrimDrawable;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -174,6 +176,7 @@
private final IDreamManager mDreamManager;
private final DevicePolicyManager mDevicePolicyManager;
private final LockPatternUtils mLockPatternUtils;
+ private final TelephonyListenerManager mTelephonyListenerManager;
private final KeyguardStateController mKeyguardStateController;
private final BroadcastDispatcher mBroadcastDispatcher;
protected final GlobalSettings mGlobalSettings;
@@ -186,7 +189,6 @@
private final TelecomManager mTelecomManager;
private final MetricsLogger mMetricsLogger;
private final UiEventLogger mUiEventLogger;
- private final NotificationShadeDepthController mDepthController;
private final SysUiState mSysUiState;
private final GlobalActionsInfoProvider mInfoProvider;
@@ -228,6 +230,7 @@
private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms
protected Handler mMainHandler;
private int mSmallestScreenWidthDp;
+ private final StatusBar mStatusBar;
@VisibleForTesting
public enum GlobalActionsEvent implements UiEventLogger.UiEventEnum {
@@ -304,31 +307,45 @@
* @param context everything needs a context :(
*/
@Inject
- public GlobalActionsDialogLite(Context context, GlobalActionsManager windowManagerFuncs,
- AudioManager audioManager, IDreamManager iDreamManager,
- DevicePolicyManager devicePolicyManager, LockPatternUtils lockPatternUtils,
+ public GlobalActionsDialogLite(
+ Context context,
+ GlobalActionsManager windowManagerFuncs,
+ AudioManager audioManager,
+ IDreamManager iDreamManager,
+ DevicePolicyManager devicePolicyManager,
+ LockPatternUtils lockPatternUtils,
BroadcastDispatcher broadcastDispatcher,
TelephonyListenerManager telephonyListenerManager,
- GlobalSettings globalSettings, SecureSettings secureSettings,
- @Nullable Vibrator vibrator, @Main Resources resources,
+ GlobalSettings globalSettings,
+ SecureSettings secureSettings,
+ @Nullable Vibrator vibrator,
+ @Main Resources resources,
ConfigurationController configurationController,
- KeyguardStateController keyguardStateController, UserManager userManager,
- TrustManager trustManager, IActivityManager iActivityManager,
- @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger,
- NotificationShadeDepthController depthController, SysuiColorExtractor colorExtractor,
+ KeyguardStateController keyguardStateController,
+ UserManager userManager,
+ TrustManager trustManager,
+ IActivityManager iActivityManager,
+ @Nullable TelecomManager telecomManager,
+ MetricsLogger metricsLogger,
+ SysuiColorExtractor colorExtractor,
IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
IWindowManager iWindowManager,
@Background Executor backgroundExecutor,
UiEventLogger uiEventLogger,
GlobalActionsInfoProvider infoProvider,
- RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler) {
+ RingerModeTracker ringerModeTracker,
+ SysUiState sysUiState,
+ @Main Handler handler,
+ PackageManager packageManager,
+ StatusBar statusBar) {
mContext = context;
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
mDreamManager = iDreamManager;
mDevicePolicyManager = devicePolicyManager;
mLockPatternUtils = lockPatternUtils;
+ mTelephonyListenerManager = telephonyListenerManager;
mKeyguardStateController = keyguardStateController;
mBroadcastDispatcher = broadcastDispatcher;
mGlobalSettings = globalSettings;
@@ -342,7 +359,6 @@
mMetricsLogger = metricsLogger;
mUiEventLogger = uiEventLogger;
mInfoProvider = infoProvider;
- mDepthController = depthController;
mSysuiColorExtractor = colorExtractor;
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
@@ -351,7 +367,8 @@
mRingerModeTracker = ringerModeTracker;
mSysUiState = sysUiState;
mMainHandler = handler;
- mSmallestScreenWidthDp = mContext.getResources().getConfiguration().smallestScreenWidthDp;
+ mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
+ mStatusBar = statusBar;
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -360,11 +377,10 @@
filter.addAction(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter);
- mHasTelephony =
- context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
+ mHasTelephony = packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
// get notified of phone state changes
- telephonyListenerManager.addServiceStateListener(mPhoneStateListener);
+ mTelephonyListenerManager.addServiceStateListener(mPhoneStateListener);
mGlobalSettings.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
mAirplaneModeObserver);
@@ -384,6 +400,16 @@
mConfigurationController.addCallback(this);
}
+ /**
+ * Clean up callbacks
+ */
+ public void destroy() {
+ mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
+ mTelephonyListenerManager.removeServiceStateListener(mPhoneStateListener);
+ mGlobalSettings.unregisterContentObserver(mAirplaneModeObserver);
+ mConfigurationController.removeCallback(this);
+ }
+
protected Context getContext() {
return mContext;
}
@@ -392,6 +418,10 @@
return mUiEventLogger;
}
+ protected StatusBar getStatusBar() {
+ return mStatusBar;
+ }
+
/**
* Show the global actions dialog (creating if necessary)
*
@@ -618,14 +648,12 @@
protected ActionsDialogLite createDialog() {
initDialogItems();
- mDepthController.setShowingHomeControls(false);
ActionsDialogLite dialog = new ActionsDialogLite(mContext,
com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite,
- mAdapter, mOverflowAdapter,
- mDepthController, mSysuiColorExtractor,
+ mAdapter, mOverflowAdapter, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger,
- mInfoProvider);
+ mInfoProvider, mStatusBar);
dialog.setOnDismissListener(this);
dialog.setOnShowListener(this);
@@ -679,14 +707,6 @@
mDialog.refreshDialog();
}
}
-
- /**
- * Clean up callbacks
- */
- public void destroy() {
- mConfigurationController.removeCallback(this);
- }
-
/**
* Implements {@link GlobalActionsPanelPlugin.Callbacks#dismissGlobalActionsMenu()}, which is
* called when the quick access wallet requests dismissal.
@@ -834,6 +854,8 @@
mMetricsLogger.action(MetricsEvent.ACTION_EMERGENCY_DIALER_FROM_POWER_MENU);
mUiEventLogger.log(GlobalActionsEvent.GA_EMERGENCY_DIALER_PRESS);
if (mTelecomManager != null) {
+ // Close shade so user sees the activity
+ mStatusBar.collapseShade();
Intent intent = mTelecomManager.createLaunchEmergencyDialerIntent(
null /* number */);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -964,6 +986,8 @@
Log.w(TAG, "Bugreport handler could not be launched");
mIActivityManager.requestInteractiveBugReport();
}
+ // Close shade so user sees the activity
+ mStatusBar.collapseShade();
} catch (RemoteException e) {
}
}
@@ -982,6 +1006,8 @@
mMetricsLogger.action(MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL);
mUiEventLogger.log(GlobalActionsEvent.GA_BUGREPORT_LONG_PRESS);
mIActivityManager.requestFullBugReport();
+ // Close shade so user sees the activity
+ mStatusBar.collapseShade();
} catch (RemoteException e) {
}
return false;
@@ -2008,7 +2034,7 @@
}
};
- private ContentObserver mAirplaneModeObserver = new ContentObserver(mMainHandler) {
+ private final ContentObserver mAirplaneModeObserver = new ContentObserver(mMainHandler) {
@Override
public void onChange(boolean selfChange) {
onAirplaneModeChanged();
@@ -2093,30 +2119,71 @@
protected boolean mShowing;
protected float mScrimAlpha;
protected final NotificationShadeWindowController mNotificationShadeWindowController;
- protected final NotificationShadeDepthController mDepthController;
protected final SysUiState mSysUiState;
private ListPopupWindow mOverflowPopup;
private Dialog mPowerOptionsDialog;
protected final Runnable mOnRotateCallback;
private UiEventLogger mUiEventLogger;
private GlobalActionsInfoProvider mInfoProvider;
+ private GestureDetector mGestureDetector;
+ private StatusBar mStatusBar;
protected ViewGroup mContainer;
+ @VisibleForTesting
+ protected GestureDetector.SimpleOnGestureListener mGestureListener =
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onDown(MotionEvent e) {
+ // All gestures begin with this message, so continue listening
+ return true;
+ }
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ // Close without opening shade
+ mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ cancel();
+ return false;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+ float distanceY) {
+ if (distanceY < 0 && distanceY > distanceX
+ && e1.getY() <= mStatusBar.getStatusBarHeight()) {
+ // Downwards scroll from top
+ openShadeAndDismiss();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+ float velocityY) {
+ if (velocityY > 0 && Math.abs(velocityY) > Math.abs(velocityX)
+ && e1.getY() <= mStatusBar.getStatusBarHeight()) {
+ // Downwards fling from top
+ openShadeAndDismiss();
+ return true;
+ }
+ return false;
+ }
+ };
+
ActionsDialogLite(Context context, int themeRes, MyAdapter adapter,
MyOverflowAdapter overflowAdapter,
- NotificationShadeDepthController depthController,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
- @Nullable GlobalActionsInfoProvider infoProvider) {
+ @Nullable GlobalActionsInfoProvider infoProvider, StatusBar statusBar) {
super(context, themeRes);
mContext = context;
mAdapter = adapter;
mOverflowAdapter = overflowAdapter;
mPowerOptionsAdapter = powerAdapter;
- mDepthController = depthController;
mColorExtractor = sysuiColorExtractor;
mStatusBarService = statusBarService;
mNotificationShadeWindowController = notificationShadeWindowController;
@@ -2125,6 +2192,9 @@
mKeyguardShowing = keyguardShowing;
mUiEventLogger = uiEventLogger;
mInfoProvider = infoProvider;
+ mStatusBar = statusBar;
+
+ mGestureDetector = new GestureDetector(mContext, mGestureListener);
// Window initialization
Window window = getWindow();
@@ -2146,6 +2216,23 @@
initializeLayout();
}
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return mGestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
+ }
+
+ private void openShadeAndDismiss() {
+ mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ if (mStatusBar.isKeyguardShowing()) {
+ // match existing lockscreen behavior to open QS when swiping from status bar
+ mStatusBar.animateExpandSettingsPanel(null);
+ } else {
+ // otherwise, swiping down should expand notification shade
+ mStatusBar.animateExpandNotificationsPanel();
+ }
+ dismiss();
+ }
+
private ListPopupWindow createPowerOverflowPopup() {
GlobalActionsPopupMenu popup = new GlobalActionsPopupMenu(
new ContextThemeWrapper(
@@ -2194,9 +2281,9 @@
mGlobalActionsLayout.setRotationListener(this::onRotate);
mGlobalActionsLayout.setAdapter(mAdapter);
mContainer = findViewById(com.android.systemui.R.id.global_actions_container);
- mContainer.setOnClickListener(v -> {
- mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
- cancel();
+ mContainer.setOnTouchListener((v, event) -> {
+ mGestureDetector.onTouchEvent(event);
+ return v.onTouchEvent(event);
});
View overflowButton = findViewById(
@@ -2313,7 +2400,6 @@
float animatedValue = animation.getAnimatedFraction();
int alpha = (int) (animatedValue * mScrimAlpha * 255);
mBackgroundDrawable.setAlpha(alpha);
- mDepthController.updateGlobalDialogVisibility(animatedValue, mGlobalActionsLayout);
});
ObjectAnimator xAnimator =
@@ -2343,7 +2429,6 @@
float animatedValue = 1f - animation.getAnimatedFraction();
int alpha = (int) (animatedValue * mScrimAlpha * 255);
mBackgroundDrawable.setAlpha(alpha);
- mDepthController.updateGlobalDialogVisibility(animatedValue, mGlobalActionsLayout);
});
float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
@@ -2380,7 +2465,6 @@
dismissOverflow(true);
dismissPowerOptions(true);
mNotificationShadeWindowController.setRequestTopUi(false, TAG);
- mDepthController.updateGlobalDialogVisibility(0, null /* view */);
mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, false)
.commitUpdate(mContext.getDisplayId());
super.dismiss();
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 178a74c..e37d3d5 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -32,7 +32,6 @@
import com.android.internal.R;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.Utils;
-import com.android.systemui.Dependency;
import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.scrim.ScrimDrawable;
import com.android.systemui.statusbar.BlurUtils;
@@ -52,19 +51,24 @@
private final KeyguardStateController mKeyguardStateController;
private final DeviceProvisionedController mDeviceProvisionedController;
private final BlurUtils mBlurUtils;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final CommandQueue mCommandQueue;
private GlobalActionsDialogLite mGlobalActionsDialog;
private boolean mDisabled;
@Inject
public GlobalActionsImpl(Context context, CommandQueue commandQueue,
- Lazy<GlobalActionsDialogLite> globalActionsDialogLazy, BlurUtils blurUtils) {
+ Lazy<GlobalActionsDialogLite> globalActionsDialogLazy, BlurUtils blurUtils,
+ KeyguardStateController keyguardStateController,
+ DeviceProvisionedController deviceProvisionedController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor) {
mContext = context;
mGlobalActionsDialogLazy = globalActionsDialogLazy;
- mKeyguardStateController = Dependency.get(KeyguardStateController.class);
- mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
+ mKeyguardStateController = keyguardStateController;
+ mDeviceProvisionedController = deviceProvisionedController;
mCommandQueue = commandQueue;
mBlurUtils = blurUtils;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mCommandQueue.addCallback(this);
}
@@ -83,7 +87,7 @@
mGlobalActionsDialog = mGlobalActionsDialogLazy.get();
mGlobalActionsDialog.showOrHideDialog(mKeyguardStateController.isShowing(),
mDeviceProvisionedController.isDeviceProvisioned());
- Dependency.get(KeyguardUpdateMonitor.class).requestFaceAuth();
+ mKeyguardUpdateMonitor.requestFaceAuth();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt
index 39008ee..17b532a 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsInfoProvider.kt
@@ -25,6 +25,7 @@
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ImageView
+import android.widget.TextView
import com.android.systemui.R
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.plugins.ActivityStarter
@@ -70,6 +71,11 @@
val view = LayoutInflater.from(context).inflate(R.layout.global_actions_change_panel,
parent, false)
+
+ val walletTitle = walletClient.serviceLabel ?: context.getString(R.string.wallet_title)
+ val message = view.findViewById<TextView>(R.id.global_actions_change_message)
+ message?.setText(context.getString(R.string.global_actions_change_description, walletTitle))
+
val button = view.findViewById<ImageView>(R.id.global_actions_change_button)
button.setOnClickListener { _ ->
dismissParent.run()
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index 01a353c..d30783c 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -46,6 +46,7 @@
private final ImageGLWallpaper mWallpaper;
private final Rect mSurfaceSize = new Rect();
private final WallpaperTexture mTexture;
+ private Consumer<Bitmap> mOnBitmapUpdated;
public ImageWallpaperRenderer(Context context) {
final WallpaperManager wpm = context.getSystemService(WallpaperManager.class);
@@ -60,10 +61,9 @@
/**
* @hide
- * @return
*/
- public void useBitmap(Consumer<Bitmap> c) {
- mTexture.use(c);
+ public void setOnBitmapChanged(Consumer<Bitmap> c) {
+ mOnBitmapUpdated = c;
}
@Override
@@ -80,6 +80,8 @@
mTexture.use(bitmap -> {
if (bitmap == null) {
Log.w(TAG, "reload texture failed!");
+ } else if (mOnBitmapUpdated != null) {
+ mOnBitmapUpdated.accept(bitmap);
}
mWallpaper.setup(bitmap);
});
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
index bec4ce6..fc5f3b8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
@@ -20,7 +20,6 @@
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.text.TextUtils;
-import android.view.View;
import androidx.annotation.IntDef;
@@ -202,10 +201,7 @@
mCurrIndicationType = type;
mIndicationQueue.removeIf(x -> x == type);
- if (mCurrIndicationType == INDICATION_TYPE_NONE) {
- mView.setVisibility(View.GONE);
- } else {
- mView.setVisibility(View.VISIBLE);
+ if (mCurrIndicationType != INDICATION_TYPE_NONE) {
mIndicationQueue.add(type); // re-add to show later
}
@@ -299,7 +295,7 @@
}
}
- private static final int INDICATION_TYPE_NONE = -1;
+ static final int INDICATION_TYPE_NONE = -1;
public static final int INDICATION_TYPE_OWNER_INFO = 0;
public static final int INDICATION_TYPE_DISCLOSURE = 1;
public static final int INDICATION_TYPE_LOGOUT = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 38d153e..941f2c6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -31,6 +31,7 @@
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
+import com.android.systemui.statusbar.FeatureFlags
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
import javax.inject.Inject
@@ -89,7 +90,8 @@
private val keyguardStateController: KeyguardStateController,
private val keyguardViewMediator: Lazy<KeyguardViewMediator>,
private val keyguardViewController: KeyguardViewController,
- private val smartspaceTransitionController: SmartspaceTransitionController
+ private val smartspaceTransitionController: SmartspaceTransitionController,
+ private val featureFlags: FeatureFlags
) : KeyguardStateController.Callback {
/**
@@ -346,6 +348,10 @@
* keyguard visible.
*/
private fun updateKeyguardViewMediatorIfThresholdsReached() {
+ if (!featureFlags.isNewKeyguardSwipeAnimationEnabled) {
+ return
+ }
+
val dismissAmount = keyguardStateController.dismissAmount
// Hide the keyguard if we're fully dismissed, or if we're swiping to dismiss and have
@@ -382,6 +388,10 @@
* know if it needs to do something as a result.
*/
private fun updateSmartSpaceTransition() {
+ if (!featureFlags.isSmartSpaceSharedElementTransitionEnabled) {
+ return
+ }
+
val dismissAmount = keyguardStateController.dismissAmount
// If we've begun a swipe, and are capable of doing the SmartSpace transition, start it!
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 3276983..c6fd20e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -119,6 +119,7 @@
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.DeviceConfigProxy;
import java.io.FileDescriptor;
@@ -257,6 +258,9 @@
/** TrustManager for letting it know when we change visibility */
private final TrustManager mTrustManager;
+ /** UserSwitcherController for creating guest user on boot complete */
+ private final UserSwitcherController mUserSwitcherController;
+
/**
* Used to keep the device awake while to ensure the keyguard finishes opening before
* we sleep.
@@ -805,6 +809,7 @@
KeyguardUpdateMonitor keyguardUpdateMonitor, DumpManager dumpManager,
@UiBackground Executor uiBgExecutor, PowerManager powerManager,
TrustManager trustManager,
+ UserSwitcherController userSwitcherController,
DeviceConfigProxy deviceConfig,
NavigationModeController navigationModeController,
KeyguardDisplayManager keyguardDisplayManager,
@@ -825,6 +830,7 @@
mUpdateMonitor = keyguardUpdateMonitor;
mPM = powerManager;
mTrustManager = trustManager;
+ mUserSwitcherController = userSwitcherController;
mKeyguardDisplayManager = keyguardDisplayManager;
dumpManager.registerDumpable(getClass().getName(), this);
mDeviceConfig = deviceConfig;
@@ -1671,8 +1677,8 @@
* Disable notification shade background blurs until the keyguard is dismissed.
* (Used during app launch animations)
*/
- public void disableBlursUntilHidden() {
- mNotificationShadeDepthController.get().setIgnoreShadeBlurUntilHidden(true);
+ public void setBlursDisabledForAppLaunch(boolean disabled) {
+ mNotificationShadeDepthController.get().setBlursDisabledForAppLaunch(disabled);
}
public boolean isSecure() {
@@ -2558,6 +2564,11 @@
@Override
public void onBootCompleted() {
synchronized (this) {
+ if (mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_guestUserAutoCreated)) {
+ // TODO(b/191067027): Move post-boot guest creation to system_server
+ mUserSwitcherController.guaranteeGuestPresent();
+ }
mBootCompleted = true;
adjustStatusBarLocked(false, true);
if (mBootSendUserPresent) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
index 2e03d9a..6f878d1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
@@ -67,7 +67,7 @@
@Nullable
private final IWallpaperManager mWallpaperManagerService;
- private int mWakefulness = WAKEFULNESS_ASLEEP;
+ private int mWakefulness = WAKEFULNESS_AWAKE;
private @PowerManager.WakeReason int mLastWakeReason = PowerManager.WAKE_REASON_UNKNOWN;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 05d1361..8a383b9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -54,6 +54,7 @@
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.settings.GlobalSettings;
@@ -92,6 +93,7 @@
DumpManager dumpManager,
PowerManager powerManager,
TrustManager trustManager,
+ UserSwitcherController userSwitcherController,
@UiBackground Executor uiBgExecutor,
DeviceConfigProxy deviceConfig,
NavigationModeController navigationModeController,
@@ -114,6 +116,7 @@
uiBgExecutor,
powerManager,
trustManager,
+ userSwitcherController,
deviceConfig,
navigationModeController,
keyguardDisplayManager,
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 77d78929..2bf102f7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -186,11 +186,9 @@
}
private fun hideMediaPlayer() {
- if (useSplitShade) {
- setVisibility(splitShadeContainer, View.GONE)
- } else {
- setVisibility(singlePaneContainer, View.GONE)
- }
+ // always hide splitShadeContainer as it's initially visible and may influence layout
+ setVisibility(splitShadeContainer, View.GONE)
+ setVisibility(singlePaneContainer, View.GONE)
}
private fun setVisibility(view: ViewGroup?, newVisibility: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 1004e25..8c6a3ca 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -32,6 +32,7 @@
private const val TAG = "MediaCarouselController"
private val settingsIntent = Intent().setAction(ACTION_MEDIA_CONTROLS_SETTINGS)
+private const val DEBUG = false
/**
* Class that is responsible for keeping the view carousel up to date.
@@ -156,6 +157,12 @@
}
}
+ /**
+ * Update MediaCarouselScrollHandler.visibleToUser to reflect media card container visibility.
+ * It will be called when the container is out of view.
+ */
+ lateinit var updateUserVisibility: () -> Unit
+
init {
mediaFrame = inflateMediaCarousel()
mediaCarousel = mediaFrame.requireViewById(R.id.media_carousel_scroller)
@@ -177,6 +184,12 @@
keysNeedRemoval.forEach { removePlayer(it) }
keysNeedRemoval.clear()
+ // Update user visibility so that no extra impression will be logged when
+ // activeMediaIndex resets to 0
+ if (this::updateUserVisibility.isInitialized) {
+ updateUserVisibility()
+ }
+
// Let's reset our scroll position
mediaCarouselScrollHandler.scrollToStart()
}
@@ -187,16 +200,24 @@
key: String,
oldKey: String?,
data: MediaData,
- immediately: Boolean
+ immediately: Boolean,
+ isSsReactivated: Boolean
) {
if (addOrUpdatePlayer(key, oldKey, data)) {
- MediaPlayerData.getMediaPlayer(key, null)?.let {
+ MediaPlayerData.getMediaPlayer(key)?.let {
logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
it.mInstanceId,
/* isRecommendationCard */ false,
- it.surfaceForSmartspaceLogging)
+ it.surfaceForSmartspaceLogging,
+ rank = MediaPlayerData.getMediaPlayerIndex(key))
}
}
+ if (mediaCarouselScrollHandler.visibleToUser &&
+ isSsReactivated && !mediaCarouselScrollHandler.qsExpanded) {
+ // It could happen that reactived media player isn't visible to user because
+ // of it is a resumption card.
+ logSmartspaceImpression(mediaCarouselScrollHandler.qsExpanded)
+ }
val canRemove = data.isPlaying?.let { !it } ?: data.isClearable && !data.active
if (canRemove && !Utils.useMediaResumption(context)) {
// This view isn't playing, let's remove this! This happens e.g when
@@ -217,17 +238,24 @@
data: SmartspaceMediaData,
shouldPrioritize: Boolean
) {
- Log.d(TAG, "My Smartspace media update is here")
+ if (DEBUG) Log.d(TAG, "Loading Smartspace media update")
if (data.isActive) {
addSmartspaceMediaRecommendations(key, data, shouldPrioritize)
- MediaPlayerData.getMediaPlayer(key, null)?.let {
+ MediaPlayerData.getMediaPlayer(key)?.let {
logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED
it.mInstanceId,
/* isRecommendationCard */ true,
- it.surfaceForSmartspaceLogging)
- }
- if (mediaCarouselScrollHandler.visibleToUser) {
- logSmartspaceImpression()
+ it.surfaceForSmartspaceLogging,
+ rank = MediaPlayerData.getMediaPlayerIndex(key))
+
+ if (mediaCarouselScrollHandler.visibleToUser &&
+ mediaCarouselScrollHandler.visibleMediaIndex ==
+ MediaPlayerData.getMediaPlayerIndex(key)) {
+ logSmartspaceCardReported(800, // SMARTSPACE_CARD_SEEN
+ it.mInstanceId,
+ /* isRecommendationCard */ true,
+ it.surfaceForSmartspaceLogging)
+ }
}
} else {
onSmartspaceMediaDataRemoved(data.targetId, immediately = true)
@@ -239,7 +267,7 @@
}
override fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean) {
- Log.d(TAG, "My Smartspace media removal request is received")
+ if (DEBUG) Log.d(TAG, "My Smartspace media removal request is received")
if (immediately || visualStabilityManager.isReorderingAllowed) {
onMediaDataRemoved(key)
} else {
@@ -316,7 +344,8 @@
// Returns true if new player is added
private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData): Boolean {
val dataCopy = data.copy(backgroundColor = bgColor)
- val existingPlayer = MediaPlayerData.getMediaPlayer(key, oldKey)
+ MediaPlayerData.moveIfExists(oldKey, key)
+ val existingPlayer = MediaPlayerData.getMediaPlayer(key)
val curVisibleMediaKey = MediaPlayerData.playerKeys()
.elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex)
if (existingPlayer == null) {
@@ -357,8 +386,8 @@
data: SmartspaceMediaData,
shouldPrioritize: Boolean
) {
- Log.d(TAG, "Updating smartspace target in carousel")
- if (MediaPlayerData.getMediaPlayer(key, null) != null) {
+ if (DEBUG) Log.d(TAG, "Updating smartspace target in carousel")
+ if (MediaPlayerData.getMediaPlayer(key) != null) {
Log.w(TAG, "Skip adding smartspace target in carousel")
return
}
@@ -644,17 +673,17 @@
}
/**
- * Log the user impression for media card.
+ * Log the user impression for media card at visibleMediaIndex.
*/
- fun logSmartspaceImpression() {
+ fun logSmartspaceImpression(qsExpanded: Boolean) {
val visibleMediaIndex = mediaCarouselScrollHandler.visibleMediaIndex
if (MediaPlayerData.players().size > visibleMediaIndex) {
val mediaControlPanel = MediaPlayerData.players().elementAt(visibleMediaIndex)
- val isMediaActive =
- MediaPlayerData.playerKeys().elementAt(visibleMediaIndex).data?.active
+ val hasActiveMediaOrRecommendationCard =
+ MediaPlayerData.hasActiveMediaOrRecommendationCard()
val isRecommendationCard = mediaControlPanel.recommendationViewHolder != null
- if (!isRecommendationCard && !isMediaActive) {
- // Media control card time out or swiped away
+ if (!hasActiveMediaOrRecommendationCard && !qsExpanded) {
+ // Skip logging if on LS or QQS, and there is no active media card
return
}
logSmartspaceCardReported(800, // SMARTSPACE_CARD_SEEN
@@ -672,6 +701,13 @@
surface: Int,
rank: Int = mediaCarouselScrollHandler.visibleMediaIndex
) {
+ // Only log media resume card when Smartspace data is available
+ if (!isRecommendationCard &&
+ !mediaManager.smartspaceMediaData.isActive &&
+ MediaPlayerData.smartspaceMediaData == null) {
+ return
+ }
+
/* ktlint-disable max-line-length */
SysUiStatsLog.write(SysUiStatsLog.SMARTSPACE_CARD_REPORTED,
eventId,
@@ -760,14 +796,29 @@
smartspaceMediaData = data
}
- fun getMediaPlayer(key: String, oldKey: String?): MediaControlPanel? {
- // If the key was changed, update entry
- oldKey?.let {
- if (it != key) {
- mediaData.remove(it)?.let { sortKey -> mediaData.put(key, sortKey) }
+ fun moveIfExists(oldKey: String?, newKey: String) {
+ if (oldKey == null || oldKey == newKey) {
+ return
+ }
+
+ mediaData.remove(oldKey)?.let {
+ removeMediaPlayer(newKey)
+ mediaData.put(newKey, it)
+ }
+ }
+
+ fun getMediaPlayer(key: String): MediaControlPanel? {
+ return mediaData.get(key)?.let { mediaPlayers.get(it) }
+ }
+
+ fun getMediaPlayerIndex(key: String): Int {
+ val sortKey = mediaData.get(key)
+ mediaPlayers.entries.forEachIndexed { index, e ->
+ if (e.key == sortKey) {
+ return index
}
}
- return mediaData.get(key)?.let { mediaPlayers.get(it) }
+ return -1
}
fun removeMediaPlayer(key: String) = mediaData.remove(key)?.let {
@@ -808,4 +859,15 @@
mediaData.clear()
mediaPlayers.clear()
}
+
+ /* Returns true if there is active media player card or recommendation card */
+ fun hasActiveMediaOrRecommendationCard(): Boolean {
+ if (smartspaceMediaData != null && smartspaceMediaData?.isActive!!) {
+ return true
+ }
+ if (firstActiveMediaIndex() != -1) {
+ return true
+ }
+ return false
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
index eb354978..cbcec95 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
@@ -62,7 +62,7 @@
private val closeGuts: (immediate: Boolean) -> Unit,
private val falsingCollector: FalsingCollector,
private val falsingManager: FalsingManager,
- private val logSmartspaceImpression: () -> Unit
+ private val logSmartspaceImpression: (Boolean) -> Unit
) {
/**
* Is the view in RTL
@@ -195,18 +195,22 @@
if (playerWidthPlusPadding == 0) {
return
}
+
val relativeScrollX = scrollView.relativeScrollX
onMediaScrollingChanged(relativeScrollX / playerWidthPlusPadding,
relativeScrollX % playerWidthPlusPadding)
}
}
+ /**
+ * Whether the media card is visible to user if any
+ */
var visibleToUser: Boolean = false
- set(value) {
- if (field != value) {
- field = value
- }
- }
+
+ /**
+ * Whether the quick setting is expanded or not
+ */
+ var qsExpanded: Boolean = false
init {
gestureDetector = GestureDetectorCompat(scrollView.context, gestureListener)
@@ -232,7 +236,7 @@
}
private fun updateSettingsPresentation() {
- if (showsSettingsButton) {
+ if (showsSettingsButton && settingsButton.width > 0) {
val settingsOffset = MathUtils.map(
0.0f,
getMaxTranslation().toFloat(),
@@ -471,7 +475,7 @@
val oldIndex = visibleMediaIndex
visibleMediaIndex = newIndex
if (oldIndex != visibleMediaIndex && visibleToUser) {
- logSmartspaceImpression()
+ logSmartspaceImpression(qsExpanded)
}
closeGuts(false)
updatePlayerVisibilities()
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index c2b5807..19a67e9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -46,6 +46,7 @@
import androidx.annotation.UiThread;
import androidx.constraintlayout.widget.ConstraintSet;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.settingslib.widget.AdaptiveIcon;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -265,7 +266,7 @@
}
mKey = key;
MediaSession.Token token = data.getToken();
- mInstanceId = data.getPackageName().hashCode();
+ mInstanceId = SmallHash.hash(data.getPackageName());
mBackgroundColor = data.getBackgroundColor();
if (mToken == null || !mToken.equals(token)) {
@@ -468,7 +469,8 @@
TransitionLayout player) {
// TODO(b/174236650): Make sure that the carousel indicator also fades out.
// TODO(b/174236650): Instrument the animation to measure jank.
- return new GhostedViewLaunchAnimatorController(player) {
+ return new GhostedViewLaunchAnimatorController(player,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER) {
@Override
protected float getCurrentTopCornerRadius() {
return ((IlluminationDrawable) player.getBackground()).getCornerRadius();
@@ -502,7 +504,7 @@
return;
}
- mInstanceId = data.getTargetId().hashCode();
+ mInstanceId = SmallHash.hash(data.getTargetId());
mBackgroundColor = data.getBackgroundColor();
TransitionLayout recommendationCard = mRecommendationViewHolder.getRecommendations();
recommendationCard.setBackgroundTintList(ColorStateList.valueOf(mBackgroundColor));
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
index ee1d3ea..296bfda 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
@@ -31,7 +31,8 @@
key: String,
oldKey: String?,
data: MediaData,
- immediately: Boolean
+ immediately: Boolean,
+ isSsReactivated: Boolean
) {
if (oldKey != null && oldKey != key && entries.contains(oldKey)) {
entries[key] = data to entries.remove(oldKey)?.second
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index a611b60..c8deb01 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -83,7 +83,8 @@
key: String,
oldKey: String?,
data: MediaData,
- immediately: Boolean
+ immediately: Boolean,
+ isSsReactivated: Boolean
) {
if (oldKey != null && oldKey != key) {
allEntries.remove(oldKey)
@@ -101,7 +102,7 @@
// Notify listeners
listeners.forEach {
- it.onMediaDataLoaded(key, oldKey, data)
+ it.onMediaDataLoaded(key, oldKey, data, isSsReactivated = isSsReactivated)
}
}
@@ -118,6 +119,8 @@
// Override the pass-in value here, as the order of Smartspace card is only determined here.
var shouldPrioritizeMutable = false
smartspaceMediaData = data
+ // Override the pass-in value here, as the Smartspace reactivation could only happen here.
+ var isSsReactivated = false
// Before forwarding the smartspace target, first check if we have recently inactive media
val sorted = userEntries.toSortedMap(compareBy {
@@ -137,9 +140,13 @@
// Notify listeners to consider this media active
Log.d(TAG, "reactivating $lastActiveKey instead of smartspace")
reactivatedKey = lastActiveKey
+ if (MediaPlayerData.firstActiveMediaIndex() == -1) {
+ isSsReactivated = true
+ }
val mediaData = sorted.get(lastActiveKey)!!.copy(active = true)
listeners.forEach {
- it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData)
+ it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData,
+ isSsReactivated = isSsReactivated)
}
} else {
// Mark to prioritize Smartspace card if no recent media.
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 5b1e039..df1b07f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -39,6 +39,7 @@
import android.net.Uri
import android.os.Parcelable
import android.os.UserHandle
+import android.provider.Settings
import android.service.notification.StatusBarNotification
import android.text.TextUtils
import android.util.Log
@@ -54,6 +55,7 @@
import com.android.systemui.plugins.BcSmartspaceDataPlugin
import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
import com.android.systemui.statusbar.notification.row.HybridGroupManager
+import com.android.systemui.tuner.TunerService
import com.android.systemui.util.Assert
import com.android.systemui.util.Utils
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -114,7 +116,8 @@
private val smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
private var useMediaResumption: Boolean,
private val useQsMediaPlayer: Boolean,
- private val systemClock: SystemClock
+ private val systemClock: SystemClock,
+ private val tunerService: TunerService
) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener {
companion object {
@@ -145,8 +148,9 @@
private val internalListeners: MutableSet<Listener> = mutableSetOf()
private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
// There should ONLY be at most one Smartspace media recommendation.
- private var smartspaceMediaData: SmartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA
+ var smartspaceMediaData: SmartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA
private var smartspaceSession: SmartspaceSession? = null
+ private var allowMediaRecommendations = Utils.allowMediaRecommendations(context)
@Inject
constructor(
@@ -164,12 +168,13 @@
mediaDataFilter: MediaDataFilter,
activityStarter: ActivityStarter,
smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
- clock: SystemClock
+ clock: SystemClock,
+ tunerService: TunerService
) : this(context, backgroundExecutor, foregroundExecutor, mediaControllerFactory,
broadcastDispatcher, dumpManager, mediaTimeoutListener, mediaResumeListener,
mediaSessionBasedFilter, mediaDeviceManager, mediaDataCombineLatest, mediaDataFilter,
activityStarter, smartspaceMediaDataProvider, Utils.useMediaResumption(context),
- Utils.useQsMediaPlayer(context), clock)
+ Utils.useQsMediaPlayer(context), clock, tunerService)
private val appChangeReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
@@ -243,6 +248,14 @@
})
}
smartspaceSession?.let { it.requestSmartspaceUpdate() }
+ tunerService.addTunable(object : TunerService.Tunable {
+ override fun onTuningChanged(key: String?, newValue: String?) {
+ allowMediaRecommendations = Utils.allowMediaRecommendations(context)
+ if (!allowMediaRecommendations) {
+ dismissSmartspaceRecommendation(key = smartspaceMediaData.targetId, delay = 0L)
+ }
+ }
+ }, Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION)
}
fun destroy() {
@@ -440,7 +453,8 @@
if (smartspaceMediaData.targetId != key) {
return
}
- Log.d(TAG, "Dismissing Smartspace media target")
+
+ if (DEBUG) Log.d(TAG, "Dismissing Smartspace media target")
if (smartspaceMediaData.isActive) {
smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA.copy(
targetId = smartspaceMediaData.targetId)
@@ -695,19 +709,20 @@
}
override fun onSmartspaceTargetsUpdated(targets: List<Parcelable>) {
- if (!Utils.allowMediaRecommendations(context)) {
- Log.d(TAG, "Smartspace recommendation is disabled in Settings.")
+ if (!allowMediaRecommendations) {
+ if (DEBUG) Log.d(TAG, "Smartspace recommendation is disabled in Settings.")
return
}
val mediaTargets = targets.filterIsInstance<SmartspaceTarget>()
when (mediaTargets.size) {
0 -> {
- Log.d(TAG, "Empty Smartspace media target")
if (!smartspaceMediaData.isActive) {
return
}
- Log.d(TAG, "Set Smartspace media to be inactive for the data update")
+ if (DEBUG) {
+ Log.d(TAG, "Set Smartspace media to be inactive for the data update")
+ }
smartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA.copy(
targetId = smartspaceMediaData.targetId)
notifySmartspaceMediaDataRemoved(smartspaceMediaData.targetId, immediately = false)
@@ -716,13 +731,12 @@
val newMediaTarget = mediaTargets.get(0)
if (smartspaceMediaData.targetId == newMediaTarget.smartspaceTargetId) {
// The same Smartspace updates can be received. Skip the duplicate updates.
- Log.d(TAG, "Same Smartspace media update exists. Skip loading data.")
- } else {
- Log.d(TAG, "Forwarding Smartspace media update.")
- smartspaceMediaData = toSmartspaceMediaData(newMediaTarget, isActive = true)
- notifySmartspaceMediaDataLoaded(
- smartspaceMediaData.targetId, smartspaceMediaData)
+ return
}
+ if (DEBUG) Log.d(TAG, "Forwarding Smartspace media update.")
+ smartspaceMediaData = toSmartspaceMediaData(newMediaTarget, isActive = true)
+ notifySmartspaceMediaDataLoaded(
+ smartspaceMediaData.targetId, smartspaceMediaData)
}
else -> {
// There should NOT be more than 1 Smartspace media update. When it happens, it
@@ -812,12 +826,16 @@
* @param immediately indicates should apply the UI changes immediately, otherwise wait
* until the next refresh-round before UI becomes visible. True by default to take in place
* immediately.
+ *
+ * @param isSsReactivated indicates transition from a state with no active media players to
+ * a state with active media players upon receiving Smartspace media data.
*/
fun onMediaDataLoaded(
key: String,
oldKey: String?,
data: MediaData,
- immediately: Boolean = true
+ immediately: Boolean = true,
+ isSsReactivated: Boolean = false
) {}
/**
@@ -867,7 +885,7 @@
private fun packageName(target: SmartspaceTarget): String? {
val recommendationList = target.iconGrid
if (recommendationList == null || recommendationList.isEmpty()) {
- Log.d(TAG, "Empty or media recommendation list.")
+ Log.w(TAG, "Empty or null media recommendation list.")
return null
}
for (recommendation in recommendationList) {
@@ -877,7 +895,7 @@
packageName -> return packageName }
}
}
- Log.d(TAG, "No valid package name is provided.")
+ Log.w(TAG, "No valid package name is provided.")
return null
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index 52ecbea..292b0e2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -67,7 +67,8 @@
key: String,
oldKey: String?,
data: MediaData,
- immediately: Boolean
+ immediately: Boolean,
+ isSsReactivated: Boolean
) {
if (oldKey != null && oldKey != key) {
val oldEntry = entries.remove(oldKey)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 075bc70..186f961 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -187,6 +187,12 @@
private var currentAttachmentLocation = -1
/**
+ * Is there any active media in the carousel?
+ */
+ private var hasActiveMedia: Boolean = false
+ get() = mediaHosts.get(LOCATION_QQS)?.visible == true
+
+ /**
* Are we currently waiting on an animation to start?
*/
private var animationPending: Boolean = false
@@ -214,14 +220,11 @@
set(value) {
if (field != value) {
field = value
+ mediaCarouselController.mediaCarouselScrollHandler.qsExpanded = value
}
// qs is expanded on LS shade and HS shade
if (value && (isLockScreenShadeVisibleToUser() || isHomeScreenShadeVisibleToUser())) {
- mediaCarouselController.logSmartspaceImpression()
- }
- // Release shade and back to lock screen
- if (isLockScreenVisibleToUser()) {
- mediaCarouselController.logSmartspaceImpression()
+ mediaCarouselController.logSmartspaceImpression(value)
}
mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser()
}
@@ -403,7 +406,7 @@
updateTargetState()
// Enters shade from lock screen
if (newState == StatusBarState.SHADE_LOCKED && isLockScreenShadeVisibleToUser()) {
- mediaCarouselController.logSmartspaceImpression()
+ mediaCarouselController.logSmartspaceImpression(qsExpanded)
}
mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser()
}
@@ -417,7 +420,7 @@
dozeAnimationRunning = false
// Enters lock screen from screen off
if (isLockScreenVisibleToUser()) {
- mediaCarouselController.logSmartspaceImpression()
+ mediaCarouselController.logSmartspaceImpression(qsExpanded)
}
} else {
updateDesiredLocation()
@@ -430,11 +433,7 @@
override fun onExpandedChanged(isExpanded: Boolean) {
// Enters shade from home screen
if (isHomeScreenShadeVisibleToUser()) {
- mediaCarouselController.logSmartspaceImpression()
- }
- // Back to lock screen from bouncer
- if (isLockScreenVisibleToUser()) {
- mediaCarouselController.logSmartspaceImpression()
+ mediaCarouselController.logSmartspaceImpression(qsExpanded)
}
mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser()
}
@@ -459,6 +458,10 @@
goingToSleep = false
}
})
+
+ mediaCarouselController.updateUserVisibility = {
+ mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser()
+ }
}
private fun updateConfiguration() {
@@ -476,8 +479,12 @@
val viewHost = createUniqueObjectHost()
mediaObject.hostView = viewHost
mediaObject.addVisibilityChangeListener {
+ // If QQS changes visibility, we need to force an update to ensure the transition
+ // goes into the correct state
+ val stateUpdate = mediaObject.location == LOCATION_QQS
+
// Never animate because of a visibility change, only state changes should do that
- updateDesiredLocation(forceNoAnimation = true)
+ updateDesiredLocation(forceNoAnimation = true, forceStateUpdate = stateUpdate)
}
mediaHosts[mediaObject.location] = mediaObject
if (mediaObject.location == desiredLocation) {
@@ -521,10 +528,15 @@
* going from the old desired location to the new one.
*
* @param forceNoAnimation optional parameter telling the system not to animate
+ * @param forceStateUpdate optional parameter telling the system to update transition state
+ * even if location did not change
*/
- private fun updateDesiredLocation(forceNoAnimation: Boolean = false) {
+ private fun updateDesiredLocation(
+ forceNoAnimation: Boolean = false,
+ forceStateUpdate: Boolean = false
+ ) {
val desiredLocation = calculateLocation()
- if (desiredLocation != this.desiredLocation) {
+ if (desiredLocation != this.desiredLocation || forceStateUpdate) {
if (this.desiredLocation >= 0) {
previousLocation = this.desiredLocation
}
@@ -648,15 +660,11 @@
return true
}
- if (statusbarState == StatusBarState.KEYGUARD) {
- if (currentLocation == LOCATION_LOCKSCREEN &&
- previousLocation == LOCATION_QS ||
- (currentLocation == LOCATION_QS &&
- previousLocation == LOCATION_LOCKSCREEN)) {
- // We're always fading from lockscreen to keyguard in situations where the player
- // is already fully hidden
- return false
- }
+ if (statusbarState == StatusBarState.KEYGUARD && (currentLocation == LOCATION_LOCKSCREEN ||
+ previousLocation == LOCATION_LOCKSCREEN)) {
+ // We're always fading from lockscreen to keyguard in situations where the player
+ // is already fully hidden
+ return false
}
return mediaFrame.isShownNotFaded || animator.isRunning || animationPending
}
@@ -784,7 +792,7 @@
private fun getQSTransformationProgress(): Float {
val currentHost = getHost(desiredLocation)
val previousHost = getHost(previousLocation)
- if (currentHost?.location == LOCATION_QS) {
+ if (hasActiveMedia && currentHost?.location == LOCATION_QS) {
if (previousHost?.location == LOCATION_QQS) {
if (previousHost.visible || statusbarState != StatusBarState.KEYGUARD) {
return qsExpansion
@@ -917,6 +925,7 @@
val location = when {
qsExpansion > 0.0f && !onLockscreen -> LOCATION_QS
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
+ !hasActiveMedia -> LOCATION_QS
onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
onLockscreen && allowedOnLockscreen -> LOCATION_LOCKSCREEN
else -> LOCATION_QQS
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index 43e2142..ff085c36 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -60,7 +60,8 @@
key: String,
oldKey: String?,
data: MediaData,
- immediately: Boolean
+ immediately: Boolean,
+ isSsReactivated: Boolean
) {
if (immediately) {
updateViewVisibility()
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
index 0da84fb..ab568c8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -159,7 +159,8 @@
key: String,
oldKey: String?,
data: MediaData,
- immediately: Boolean
+ immediately: Boolean,
+ isSsReactivated: Boolean
) {
if (useMediaResumption) {
// If this had been started from a resume state, disconnect now that it's live
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
index a4f33e3..8bddde8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
@@ -95,7 +95,8 @@
key: String,
oldKey: String?,
data: MediaData,
- immediately: Boolean
+ immediately: Boolean,
+ isSsReactivated: Boolean
) {
backgroundExecutor.execute {
data.token?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
index bbea140..9a39193 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
@@ -54,7 +54,8 @@
key: String,
oldKey: String?,
data: MediaData,
- immediately: Boolean
+ immediately: Boolean,
+ isSsReactivated: Boolean
) {
var reusedListener: PlaybackStateListener? = null
diff --git a/packages/SystemUI/src/com/android/systemui/media/SmallHash.java b/packages/SystemUI/src/com/android/systemui/media/SmallHash.java
new file mode 100644
index 0000000..de7aac6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/SmallHash.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media;
+
+import java.util.Objects;
+
+/**
+ * A simple hash function for use in privacy-sensitive logging.
+ */
+public final class SmallHash {
+ // Hashes will be in the range [0, MAX_HASH).
+ public static final int MAX_HASH = (1 << 13);
+
+ /** Return Small hash of the string, if non-null, or 0 otherwise. */
+ public static int hash(String in) {
+ return hash(Objects.hashCode(in));
+ }
+
+ /**
+ * Maps in to the range [0, MAX_HASH), keeping similar values distinct.
+ *
+ * @param in An arbitrary integer.
+ * @return in mod MAX_HASH, signs chosen to stay in the range [0, MAX_HASH).
+ */
+ public static int hash(int in) {
+ return Math.abs(Math.floorMod(in, MAX_HASH));
+ }
+
+ private SmallHash() {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 0d5faff..391dff63 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -46,7 +46,7 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private ViewGroup mConnectedItem;
- private boolean mInclueDynamicGroup;
+ private boolean mIncludeDynamicGroup;
public MediaOutputAdapter(MediaOutputController controller) {
super(controller);
@@ -56,7 +56,6 @@
public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
int viewType) {
super.onCreateViewHolder(viewGroup, viewType);
-
return new MediaDeviceViewHolder(mHolderView);
}
@@ -66,7 +65,7 @@
if (position == size && mController.isZeroMode()) {
viewHolder.onBind(CUSTOMIZED_ITEM_PAIR_NEW, false /* topMargin */,
true /* bottomMargin */);
- } else if (mInclueDynamicGroup) {
+ } else if (mIncludeDynamicGroup) {
if (position == 0) {
viewHolder.onBind(CUSTOMIZED_ITEM_DYNAMIC_GROUP, true /* topMargin */,
false /* bottomMargin */);
@@ -76,11 +75,12 @@
// from "position - 1".
viewHolder.onBind(((List<MediaDevice>) (mController.getMediaDevices()))
.get(position - 1),
- false /* topMargin */, position == size /* bottomMargin */);
+ false /* topMargin */, position == size /* bottomMargin */, position);
}
} else if (position < size) {
viewHolder.onBind(((List<MediaDevice>) (mController.getMediaDevices())).get(position),
- position == 0 /* topMargin */, position == (size - 1) /* bottomMargin */);
+ position == 0 /* topMargin */, position == (size - 1) /* bottomMargin */,
+ position);
} else if (DEBUG) {
Log.d(TAG, "Incorrect position: " + position);
}
@@ -88,8 +88,8 @@
@Override
public int getItemCount() {
- mInclueDynamicGroup = mController.getSelectedMediaDevice().size() > 1;
- if (mController.isZeroMode() || mInclueDynamicGroup) {
+ mIncludeDynamicGroup = mController.getSelectedMediaDevice().size() > 1;
+ if (mController.isZeroMode() || mIncludeDynamicGroup) {
// Add extra one for "pair new" or dynamic group
return mController.getMediaDevices().size() + 1;
}
@@ -120,15 +120,17 @@
}
@Override
- void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
- super.onBind(device, topMargin, bottomMargin);
- final boolean currentlyConnected = !mInclueDynamicGroup && isCurrentlyConnected(device);
+ void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) {
+ super.onBind(device, topMargin, bottomMargin, position);
+ final boolean currentlyConnected = !mIncludeDynamicGroup
+ && isCurrentlyConnected(device);
if (currentlyConnected) {
mConnectedItem = mContainerLayout;
}
mBottomDivider.setVisibility(View.GONE);
mCheckBox.setVisibility(View.GONE);
- if (currentlyConnected && mController.isActiveRemoteDevice(device)) {
+ if (currentlyConnected && mController.isActiveRemoteDevice(device)
+ && mController.getSelectableMediaDevice().size() > 0) {
// Init active device layout
mDivider.setVisibility(View.VISIBLE);
mDivider.setTransitionAlpha(1);
@@ -140,6 +142,9 @@
mDivider.setVisibility(View.GONE);
mAddIcon.setVisibility(View.GONE);
}
+ if (mCurrentActivePosition == position) {
+ mCurrentActivePosition = -1;
+ }
if (mController.isTransferring()) {
if (device.getState() == MediaDeviceState.STATE_CONNECTING
&& !mController.hasAdjustVolumeUserRestriction()) {
@@ -160,6 +165,7 @@
setTwoLineLayout(device, true /* bFocused */, true /* showSeekBar */,
false /* showProgressBar */, false /* showSubtitle */);
initSeekbar(device);
+ mCurrentActivePosition = position;
} else {
setSingleLineLayout(getItemTitle(device), false /* bFocused */);
mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
@@ -186,11 +192,16 @@
mConnectedItem = mContainerLayout;
mBottomDivider.setVisibility(View.GONE);
mCheckBox.setVisibility(View.GONE);
- mDivider.setVisibility(View.VISIBLE);
- mDivider.setTransitionAlpha(1);
- mAddIcon.setVisibility(View.VISIBLE);
- mAddIcon.setTransitionAlpha(1);
- mAddIcon.setOnClickListener(v -> onEndItemClick());
+ if (mController.getSelectableMediaDevice().size() > 0) {
+ mDivider.setVisibility(View.VISIBLE);
+ mDivider.setTransitionAlpha(1);
+ mAddIcon.setVisibility(View.VISIBLE);
+ mAddIcon.setTransitionAlpha(1);
+ mAddIcon.setOnClickListener(v -> onEndItemClick());
+ } else {
+ mDivider.setVisibility(View.GONE);
+ mAddIcon.setVisibility(View.GONE);
+ }
mTitleIcon.setImageDrawable(getSpeakerDrawable());
final CharSequence sessionName = mController.getSessionName();
final CharSequence title = TextUtils.isEmpty(sessionName)
@@ -206,6 +217,7 @@
return;
}
+ mCurrentActivePosition = -1;
playSwitchingAnim(mConnectedItem, view);
mController.connectDevice(device);
device.setState(MediaDeviceState.STATE_CONNECTING);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index bcef43c..0890841 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -64,10 +64,12 @@
Context mContext;
View mHolderView;
boolean mIsDragging;
+ int mCurrentActivePosition;
public MediaOutputBaseAdapter(MediaOutputController controller) {
mController = controller;
mIsDragging = false;
+ mCurrentActivePosition = -1;
}
@Override
@@ -99,6 +101,10 @@
return mIsAnimating;
}
+ int getCurrentActivePosition() {
+ return mCurrentActivePosition;
+ }
+
/**
* ViewHolder for binding device view.
*/
@@ -136,7 +142,7 @@
mCheckBox = view.requireViewById(R.id.check_box);
}
- void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
+ void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) {
mDeviceId = device.getId();
ThreadUtils.postOnBackgroundThread(() -> {
Icon icon = mController.getDeviceIconCompat(device).toIcon(mContext);
@@ -214,6 +220,9 @@
}
void initSeekbar(MediaDevice device) {
+ if (!mController.isVolumeControlEnabled(device)) {
+ disableSeekBar();
+ }
mSeekBar.setMax(device.getMaxVolume());
mSeekBar.setMin(0);
final int currentVolume = device.getCurrentVolume();
@@ -242,6 +251,7 @@
}
void initSessionSeekbar() {
+ disableSeekBar();
mSeekBar.setMax(mController.getSessionVolumeMax());
mSeekBar.setMin(0);
final int currentVolume = mController.getSessionVolume();
@@ -330,5 +340,10 @@
PorterDuff.Mode.SRC_IN));
return BluetoothUtils.buildAdvancedDrawable(mContext, drawable);
}
+
+ private void disableSeekBar() {
+ mSeekBar.setEnabled(false);
+ mSeekBar.setOnTouchListener((v, event) -> true);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 8a9a6e6..cdcdf9a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -174,7 +174,12 @@
mHeaderTitle.setGravity(Gravity.NO_GRAVITY);
}
if (!mAdapter.isDragging() && !mAdapter.isAnimating()) {
- mAdapter.notifyDataSetChanged();
+ int currentActivePosition = mAdapter.getCurrentActivePosition();
+ if (currentActivePosition >= 0) {
+ mAdapter.notifyItemChanged(currentActivePosition);
+ } else {
+ mAdapter.notifyDataSetChanged();
+ }
}
// Show when remote media session is available
mStopButton.setVisibility(getStopButtonVisibility());
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 8fee925..5293c88 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -465,6 +465,10 @@
|| features.contains(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK));
}
+ boolean isVolumeControlEnabled(@NonNull MediaDevice device) {
+ return !device.getFeatures().contains(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK);
+ }
+
private final MediaController.Callback mCb = new MediaController.Callback() {
@Override
public void onMetadataChanged(MediaMetadata metadata) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
index 24e076b..968c350 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java
@@ -68,7 +68,7 @@
final int size = mGroupMediaDevices.size();
if (newPosition < size) {
viewHolder.onBind(mGroupMediaDevices.get(newPosition), false /* topMargin */,
- newPosition == (size - 1) /* bottomMargin */);
+ newPosition == (size - 1) /* bottomMargin */, position);
return;
}
if (DEBUG) {
@@ -94,8 +94,8 @@
}
@Override
- void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
- super.onBind(device, topMargin, bottomMargin);
+ void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) {
+ super.onBind(device, topMargin, bottomMargin, position);
mDivider.setVisibility(View.GONE);
mAddIcon.setVisibility(View.GONE);
mBottomDivider.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index da09793..711bb56 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -129,6 +129,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.AutoHideUiElement;
@@ -199,6 +200,7 @@
private final Handler mHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
+ private final UserTracker mUserTracker;
private Bundle mSavedState;
private NavigationBarView mNavigationBarView;
@@ -232,7 +234,6 @@
private boolean mTransientShown;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
- private int mA11yBtnMode;
private LightBarController mLightBarController;
private AutoHideController mAutoHideController;
@@ -459,7 +460,8 @@
SystemActions systemActions,
@Main Handler mainHandler,
NavigationBarOverlayController navbarOverlayController,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ UserTracker userTracker) {
mContext = context;
mWindowManager = windowManager;
mAccessibilityManager = accessibilityManager;
@@ -484,10 +486,10 @@
mHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
+ mUserTracker = userTracker;
mNavBarMode = mNavigationModeController.addListener(this);
mAccessibilityButtonModeObserver.addListener(this);
- mA11yBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
}
public NavigationBarView getView() {
@@ -1375,8 +1377,9 @@
private void setAccessibilityFloatingMenuModeIfNeeded() {
if (QuickStepContract.isGesturalMode(mNavBarMode)) {
- Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
- ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+ Settings.Secure.putIntForUser(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU, UserHandle.USER_CURRENT);
}
}
@@ -1437,7 +1440,8 @@
// If accessibility button is floating menu mode, click and long click state should be
// disabled.
- if (mA11yBtnMode == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+ if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
+ == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
return 0;
}
@@ -1450,12 +1454,14 @@
.getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
boolean longPressDefault = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_assistLongPressHomeEnabledDefault);
- mLongPressHomeEnabled = Settings.Secure.getInt(mContentResolver,
- Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0) != 0;
+ mLongPressHomeEnabled = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.ASSIST_LONG_PRESS_HOME_ENABLED, longPressDefault ? 1 : 0,
+ mUserTracker.getUserId()) != 0;
boolean gestureDefault = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_assistTouchGestureEnabledDefault);
- mAssistantTouchGestureEnabled = Settings.Secure.getInt(mContentResolver,
- Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0) != 0;
+ mAssistantTouchGestureEnabled = Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED, gestureDefault ? 1 : 0,
+ mUserTracker.getUserId()) != 0;
if (mOverviewProxyService.getProxy() != null) {
try {
mOverviewProxyService.getProxy().onAssistantAvailable(mAssistantAvailable
@@ -1546,7 +1552,6 @@
@Override
public void onAccessibilityButtonModeChanged(int mode) {
- mA11yBtnMode = mode;
updateAccessibilityServicesState(mAccessibilityManager);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 8b5a537..5359210 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -57,6 +57,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -116,6 +117,7 @@
private final TaskbarDelegate mTaskbarDelegate;
private int mNavMode;
private boolean mIsTablet;
+ private final UserTracker mUserTracker;
/** A displayId - nav bar maps. */
@VisibleForTesting
@@ -151,7 +153,8 @@
@Main Handler mainHandler,
UiEventLogger uiEventLogger,
NavigationBarOverlayController navBarOverlayController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ UserTracker userTracker) {
mContext = context;
mWindowManager = windowManager;
mAssistManagerLazy = assistManagerLazy;
@@ -184,6 +187,7 @@
mNavigationModeController.addListener(this);
mTaskbarDelegate = new TaskbarDelegate(mOverviewProxyService);
mIsTablet = isTablet(mContext.getResources().getConfiguration());
+ mUserTracker = userTracker;
}
@Override
@@ -361,7 +365,8 @@
mSystemActions,
mHandler,
mNavBarOverlayController,
- mUiEventLogger);
+ mUiEventLogger,
+ mUserTracker);
mNavigationBars.put(displayId, navBar);
View navigationBarView = navBar.createView(savedState);
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 0d9749e..ff5d0b1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -251,8 +251,9 @@
private float mMLResults;
// For debugging
- private ArrayDeque<String> mPredictionLog = new ArrayDeque<>();
- private ArrayDeque<String> mGestureLog = new ArrayDeque<>();
+ private LogArray mPredictionLog = new LogArray(MAX_NUM_LOGGED_PREDICTIONS);
+ private LogArray mGestureLogInsideInsets = new LogArray(MAX_NUM_LOGGED_GESTURES);
+ private LogArray mGestureLogOutsideInsets = new LogArray(MAX_NUM_LOGGED_GESTURES);
private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
@@ -631,7 +632,7 @@
return mMLResults >= mMLModelThreshold ? 1 : 0;
}
- private boolean isWithinTouchRegion(int x, int y) {
+ private boolean isWithinInsets(int x, int y) {
// Disallow if we are in the bottom gesture area
if (y >= (mDisplaySize.y - mBottomGestureHeight)) {
return false;
@@ -644,7 +645,10 @@
&& x < (mDisplaySize.x - 2 * (mEdgeWidthRight + mRightInset))) {
return false;
}
+ return true;
+ }
+ private boolean isWithinTouchRegion(int x, int y) {
// If the point is inside the PiP or Nav bar overlay excluded bounds, then ignore the back
// gesture
final boolean isInsidePip = mIsInPipMode && mPipExcludedBounds.contains(x, y);
@@ -675,14 +679,8 @@
}
// For debugging purposes
- if (mPredictionLog.size() >= MAX_NUM_LOGGED_PREDICTIONS) {
- mPredictionLog.removeFirst();
- }
- mPredictionLog.addLast(String.format("Prediction [%d,%d,%d,%d,%f,%d]",
+ mPredictionLog.log(String.format("Prediction [%d,%d,%d,%d,%f,%d]",
System.currentTimeMillis(), x, y, app, mMLResults, withinRange ? 1 : 0));
- if (DEBUG_MISSING_GESTURE) {
- Log.d(DEBUG_MISSING_GESTURE_TAG, mPredictionLog.peekLast());
- }
// Always allow if the user is in a transient sticky immersive state
if (mIsNavBarShownTransiently) {
@@ -755,7 +753,8 @@
mMLResults = 0;
mLogGesture = false;
mInRejectedExclusion = false;
- mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed
+ boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY());
+ mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed && isWithinInsets
&& !mGestureBlockingActivityRunning
&& !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
&& isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
@@ -769,18 +768,13 @@
mThresholdCrossed = false;
}
- // For debugging purposes
- if (mGestureLog.size() >= MAX_NUM_LOGGED_GESTURES) {
- mGestureLog.removeFirst();
- }
- mGestureLog.addLast(String.format(
+ // For debugging purposes, only log edge points
+ (isWithinInsets ? mGestureLogInsideInsets : mGestureLogOutsideInsets).log(String.format(
"Gesture [%d,alw=%B,%B,%B,%B,disp=%s,wl=%d,il=%d,wr=%d,ir=%d,excl=%s]",
- System.currentTimeMillis(), mAllowGesture, mIsOnLeftEdge, mIsBackGestureAllowed,
+ System.currentTimeMillis(), mAllowGesture, mIsOnLeftEdge,
+ mIsBackGestureAllowed,
QuickStepContract.isBackGestureDisabled(mSysUiFlags), mDisplaySize,
mEdgeWidthLeft, mLeftInset, mEdgeWidthRight, mRightInset, mExcludeRegion));
- if (DEBUG_MISSING_GESTURE) {
- Log.d(DEBUG_MISSING_GESTURE_TAG, mGestureLog.peekLast());
- }
} else if (mAllowGesture || mLogGesture) {
if (!mThresholdCrossed) {
mEndPoint.x = (int) ev.getX();
@@ -907,7 +901,7 @@
pw.println(" mUseMLModel=" + mUseMLModel);
pw.println(" mDisabledForQuickstep=" + mDisabledForQuickstep);
pw.println(" mStartingQuickstepRotation=" + mStartingQuickstepRotation);
- pw.println(" mInRejectedExclusion" + mInRejectedExclusion);
+ pw.println(" mInRejectedExclusion=" + mInRejectedExclusion);
pw.println(" mExcludeRegion=" + mExcludeRegion);
pw.println(" mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion);
pw.println(" mIsInPipMode=" + mIsInPipMode);
@@ -922,7 +916,8 @@
pw.println(" mTouchSlop=" + mTouchSlop);
pw.println(" mBottomGestureHeight=" + mBottomGestureHeight);
pw.println(" mPredictionLog=" + String.join("\n", mPredictionLog));
- pw.println(" mGestureLog=" + String.join("\n", mGestureLog));
+ pw.println(" mGestureLogInsideInsets=" + String.join("\n", mGestureLogInsideInsets));
+ pw.println(" mGestureLogOutsideInsets=" + String.join("\n", mGestureLogOutsideInsets));
pw.println(" mEdgeBackPlugin=" + mEdgeBackPlugin);
}
@@ -945,4 +940,23 @@
}
proto.edgeBackGestureHandler.allowGesture = mAllowGesture;
}
+
+
+ private static class LogArray extends ArrayDeque<String> {
+ private final int mLength;
+
+ LogArray(int length) {
+ mLength = length;
+ }
+
+ void log(String message) {
+ if (size() >= mLength) {
+ removeFirst();
+ }
+ addLast(message);
+ if (DEBUG_MISSING_GESTURE) {
+ Log.d(DEBUG_MISSING_GESTURE_TAG, message);
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java b/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java
index b5ac908..d863dcc 100644
--- a/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/NotificationHelper.java
@@ -240,9 +240,16 @@
/** Returns whether {@code entry} is suppressed from shade, meaning we should not show it. */
public static boolean shouldFilterOut(
Optional<Bubbles> bubblesOptional, NotificationEntry entry) {
- return bubblesOptional.isPresent()
- && bubblesOptional.get().isBubbleNotificationSuppressedFromShade(
- entry.getKey(), entry.getSbn().getGroupKey());
+ boolean isSuppressed = false;
+ //TODO(b/190822282): Investigate what is causing the NullPointerException
+ try {
+ isSuppressed = bubblesOptional.isPresent()
+ && bubblesOptional.get().isBubbleNotificationSuppressedFromShade(
+ entry.getKey(), entry.getSbn().getGroupKey());
+ } catch (Exception e) {
+ Log.e(TAG, "Exception checking if notification is suppressed: " + e);
+ }
+ return isSuppressed;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index d9e2648..93a3f81 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -89,7 +89,7 @@
// The Tile preview has colorBackground as its background. Change it so it's different
// than the activity's background.
- LinearLayout item = findViewById(R.id.item);
+ LinearLayout item = findViewById(android.R.id.background);
GradientDrawable shape = (GradientDrawable) item.getBackground();
final TypedArray ta = mContext.getTheme().obtainStyledAttributes(
new int[]{com.android.internal.R.attr.colorSurface});
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index e53cc0b..917a060 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -494,7 +494,8 @@
* match the data by {@link ContactsContract.ContactsColumns#LOOKUP_KEY} key to ensure proper
* matching across all the Contacts DB tables.
*/
- private static List<String> getContactLookupKeysWithBirthdaysToday(Context context) {
+ @VisibleForTesting
+ public static List<String> getContactLookupKeysWithBirthdaysToday(Context context) {
List<String> lookupKeysWithBirthdaysToday = new ArrayList<>(1);
String today = new SimpleDateFormat("MM-dd").format(new Date());
String[] projection = new String[]{
@@ -503,14 +504,20 @@
String where =
ContactsContract.Data.MIMETYPE
+ "= ? AND " + ContactsContract.CommonDataKinds.Event.TYPE + "="
- + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY + " AND substr("
- + ContactsContract.CommonDataKinds.Event.START_DATE + ",6) = ?";
+ + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY + " AND (substr("
+ // Birthdays stored with years will match this format
+ + ContactsContract.CommonDataKinds.Event.START_DATE + ",6) = ? OR substr("
+ // Birthdays stored without years will match this format
+ + ContactsContract.CommonDataKinds.Event.START_DATE + ",3) = ? )";
String[] selection =
- new String[]{ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE, today};
+ new String[]{ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE, today,
+ today};
Cursor cursor = null;
try {
- cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
- projection, where, selection, null);
+ cursor = context
+ .getContentResolver()
+ .query(ContactsContract.Data.CONTENT_URI,
+ projection, where, selection, null);
while (cursor != null && cursor.moveToNext()) {
String lookupKey = cursor.getString(
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY));
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java b/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java
index 96aeb60..4ee951f 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java
@@ -211,7 +211,8 @@
@Override
public void setColorFilter(ColorFilter colorFilter) {
- // unimplemented
+ if (mAvatar != null) mAvatar.setColorFilter(colorFilter);
+ if (mBadgeIcon != null) mBadgeIcon.setColorFilter(colorFilter);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index 32fdf0e..221bb15 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -29,6 +29,9 @@
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
+import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_SIZES;
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
+import static android.util.TypedValue.COMPLEX_UNIT_PX;
import static com.android.systemui.people.PeopleSpaceUtils.STARRED_CONTACT;
import static com.android.systemui.people.PeopleSpaceUtils.VALID_CONTACT;
@@ -41,25 +44,33 @@
import android.app.people.PeopleSpaceTile;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Configuration;
import android.graphics.Bitmap;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.graphics.text.LineBreaker;
import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
+import android.text.StaticLayout;
+import android.text.TextPaint;
import android.text.TextUtils;
import android.util.IconDrawableFactory;
import android.util.Log;
import android.util.Pair;
+import android.util.SizeF;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.widget.RemoteViews;
import android.widget.TextView;
+import androidx.annotation.DimenRes;
+import androidx.annotation.Px;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+import androidx.core.math.MathUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.launcher3.icons.FastBitmapDrawable;
@@ -75,8 +86,10 @@
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -163,28 +176,64 @@
private Locale mLocale;
private NumberFormat mIntegerFormat;
- public PeopleTileViewHelper(Context context, @Nullable PeopleSpaceTile tile,
- int appWidgetId, Bundle options, PeopleTileKey key) {
+ PeopleTileViewHelper(Context context, @Nullable PeopleSpaceTile tile,
+ int appWidgetId, int width, int height, PeopleTileKey key) {
mContext = context;
mTile = tile;
mKey = key;
mAppWidgetId = appWidgetId;
mDensity = mContext.getResources().getDisplayMetrics().density;
- int display = mContext.getResources().getConfiguration().orientation;
- mWidth = display == Configuration.ORIENTATION_PORTRAIT
- ? options.getInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.default_width)) : options.getInt(
- OPTION_APPWIDGET_MAX_WIDTH,
- getSizeInDp(R.dimen.default_width));
- mHeight = display == Configuration.ORIENTATION_PORTRAIT ? options.getInt(
- OPTION_APPWIDGET_MAX_HEIGHT,
- getSizeInDp(R.dimen.default_height))
- : options.getInt(OPTION_APPWIDGET_MIN_HEIGHT,
- getSizeInDp(R.dimen.default_height));
+ mWidth = width;
+ mHeight = height;
mLayoutSize = getLayoutSize();
}
- public RemoteViews getViews() {
+ /**
+ * Creates a {@link RemoteViews} for the specified arguments. The RemoteViews will support all
+ * the sizes present in {@code options.}.
+ */
+ public static RemoteViews createRemoteViews(Context context, @Nullable PeopleSpaceTile tile,
+ int appWidgetId, Bundle options, PeopleTileKey key) {
+ List<SizeF> widgetSizes = getWidgetSizes(context, options);
+ Map<SizeF, RemoteViews> sizeToRemoteView =
+ widgetSizes
+ .stream()
+ .distinct()
+ .collect(Collectors.toMap(
+ Function.identity(),
+ size -> new PeopleTileViewHelper(
+ context, tile, appWidgetId,
+ (int) size.getWidth(),
+ (int) size.getHeight(),
+ key)
+ .getViews()));
+ return new RemoteViews(sizeToRemoteView);
+ }
+
+ private static List<SizeF> getWidgetSizes(Context context, Bundle options) {
+ float density = context.getResources().getDisplayMetrics().density;
+ List<SizeF> widgetSizes = options.getParcelableArrayList(OPTION_APPWIDGET_SIZES);
+ // If the full list of sizes was provided in the options bundle, use that.
+ if (widgetSizes != null && !widgetSizes.isEmpty()) return widgetSizes;
+
+ // Otherwise, create a list using the portrait/landscape sizes.
+ int defaultWidth = getSizeInDp(context, R.dimen.default_width, density);
+ int defaultHeight = getSizeInDp(context, R.dimen.default_height, density);
+ widgetSizes = new ArrayList<>(2);
+
+ int portraitWidth = options.getInt(OPTION_APPWIDGET_MIN_WIDTH, defaultWidth);
+ int portraitHeight = options.getInt(OPTION_APPWIDGET_MAX_HEIGHT, defaultHeight);
+ widgetSizes.add(new SizeF(portraitWidth, portraitHeight));
+
+ int landscapeWidth = options.getInt(OPTION_APPWIDGET_MAX_WIDTH, defaultWidth);
+ int landscapeHeight = options.getInt(OPTION_APPWIDGET_MIN_HEIGHT, defaultHeight);
+ widgetSizes.add(new SizeF(landscapeWidth, landscapeHeight));
+
+ return widgetSizes;
+ }
+
+ @VisibleForTesting
+ RemoteViews getViews() {
RemoteViews viewsForTile = getViewForTile();
int maxAvatarSize = getMaxAvatarSize(viewsForTile);
RemoteViews views = setCommonRemoteViewsFields(viewsForTile, maxAvatarSize);
@@ -197,12 +246,16 @@
*/
private RemoteViews getViewForTile() {
if (DEBUG) Log.d(TAG, "Creating view for tile key: " + mKey.toString());
- if (mTile == null || mTile.isPackageSuspended() || mTile.isUserQuieted()
- || isDndBlockingTileData(mTile)) {
+ if (mTile == null || mTile.isPackageSuspended() || mTile.isUserQuieted()) {
if (DEBUG) Log.d(TAG, "Create suppressed view: " + mTile);
return createSuppressedView();
}
+ if (isDndBlockingTileData(mTile)) {
+ if (DEBUG) Log.d(TAG, "Create dnd view");
+ return createDndRemoteViews().mRemoteViews;
+ }
+
if (Objects.equals(mTile.getNotificationCategory(), CATEGORY_MISSED_CALL)) {
if (DEBUG) Log.d(TAG, "Create missed call view");
return createMissedCallRemoteViews();
@@ -236,7 +289,9 @@
return createLastInteractionRemoteViews();
}
- private boolean isDndBlockingTileData(PeopleSpaceTile tile) {
+ private static boolean isDndBlockingTileData(@Nullable PeopleSpaceTile tile) {
+ if (tile == null) return false;
+
int notificationPolicyState = tile.getNotificationPolicyState();
if ((notificationPolicyState & PeopleSpaceTile.SHOW_CONVERSATIONS) != 0) {
// Not in DND, or all conversations
@@ -265,7 +320,7 @@
private RemoteViews createSuppressedView() {
RemoteViews views;
- if (mTile.isUserQuieted()) {
+ if (mTile != null && mTile.isUserQuieted()) {
views = new RemoteViews(mContext.getPackageName(),
R.layout.people_tile_work_profile_quiet_layout);
} else {
@@ -413,6 +468,11 @@
int avatarWidthSpace = mWidth - (14 + 14);
avatarSize = Math.min(avatarHeightSpace, avatarWidthSpace);
}
+
+ if (isDndBlockingTileData(mTile)) {
+ avatarSize = createDndRemoteViews().mAvatarSize;
+ }
+
return Math.min(avatarSize,
getSizeInDp(R.dimen.max_people_avatar_size));
}
@@ -432,7 +492,6 @@
views.setViewVisibility(R.id.availability, View.GONE);
}
- views.setBoolean(R.id.image, "setClipToOutline", true);
views.setImageViewBitmap(R.id.person_icon,
getPersonIconBitmap(mContext, mTile, maxAvatarSize));
return views;
@@ -460,7 +519,7 @@
PeopleSpaceWidgetProvider.EXTRA_NOTIFICATION_KEY,
mTile.getNotificationKey());
}
- views.setOnClickPendingIntent(R.id.item, PendingIntent.getActivity(
+ views.setOnClickPendingIntent(android.R.id.background, PendingIntent.getActivity(
mContext,
mAppWidgetId,
activityIntent,
@@ -473,6 +532,87 @@
return views;
}
+ private RemoteViewsAndSizes createDndRemoteViews() {
+ boolean isHorizontal = mLayoutSize == LAYOUT_MEDIUM;
+ int layoutId = isHorizontal
+ ? R.layout.people_tile_with_suppression_detail_content_horizontal
+ : R.layout.people_tile_with_suppression_detail_content_vertical;
+ RemoteViews views = new RemoteViews(mContext.getPackageName(), layoutId);
+
+ int outerPadding = mLayoutSize == LAYOUT_LARGE ? 16 : 8;
+ int outerPaddingPx = dpToPx(outerPadding);
+ views.setViewPadding(
+ android.R.id.background,
+ outerPaddingPx,
+ outerPaddingPx,
+ outerPaddingPx,
+ outerPaddingPx);
+
+ int mediumAvatarSize = getSizeInDp(R.dimen.avatar_size_for_medium);
+ int maxAvatarSize = getSizeInDp(R.dimen.max_people_avatar_size);
+
+ String text = mContext.getString(R.string.paused_by_dnd);
+ views.setTextViewText(R.id.text_content, text);
+
+ int textSizeResId =
+ mLayoutSize == LAYOUT_LARGE
+ ? R.dimen.content_text_size_for_large
+ : R.dimen.content_text_size_for_medium;
+ float textSizePx = mContext.getResources().getDimension(textSizeResId);
+ views.setTextViewTextSize(R.id.text_content, COMPLEX_UNIT_PX, textSizePx);
+ int lineHeight = getLineHeightFromResource(textSizeResId);
+
+ int avatarSize;
+ if (isHorizontal) {
+ int maxTextHeight = mHeight - outerPadding;
+ views.setInt(R.id.text_content, "setMaxLines", maxTextHeight / lineHeight);
+ avatarSize = mediumAvatarSize;
+ } else {
+ int iconSize =
+ getSizeInDp(
+ mLayoutSize == LAYOUT_SMALL
+ ? R.dimen.regular_predefined_icon
+ : R.dimen.largest_predefined_icon);
+ int heightWithoutIcon = mHeight - 2 * outerPadding - iconSize;
+ int paddingBetweenElements =
+ getSizeInDp(R.dimen.padding_between_suppressed_layout_items);
+ int maxTextWidth = mWidth - outerPadding * 2;
+ int maxTextHeight = heightWithoutIcon - mediumAvatarSize - paddingBetweenElements * 2;
+
+ int availableAvatarHeight;
+ int textHeight = estimateTextHeight(text, textSizeResId, maxTextWidth);
+ if (textHeight <= maxTextHeight) {
+ // If the text will fit, then display it and deduct its height from the space we
+ // have for the avatar.
+ availableAvatarHeight = heightWithoutIcon - textHeight - paddingBetweenElements * 2;
+ views.setViewVisibility(R.id.text_content, View.VISIBLE);
+ views.setInt(R.id.text_content, "setMaxLines", maxTextHeight / lineHeight);
+ views.setContentDescription(R.id.predefined_icon, null);
+ } else {
+ // If the height doesn't fit, then hide it. The dnd icon will still show.
+ availableAvatarHeight = heightWithoutIcon - paddingBetweenElements;
+ views.setViewVisibility(R.id.text_content, View.GONE);
+ // If we don't show the dnd text, set it as the content description on the icon
+ // for a11y.
+ views.setContentDescription(R.id.predefined_icon, text);
+ }
+
+ int availableAvatarWidth = mWidth - outerPadding * 2;
+ avatarSize =
+ MathUtils.clamp(
+ /* value= */ Math.min(availableAvatarWidth, availableAvatarHeight),
+ /* min= */ dpToPx(10),
+ /* max= */ maxAvatarSize);
+
+ views.setViewLayoutWidth(R.id.predefined_icon, iconSize, COMPLEX_UNIT_DIP);
+ views.setViewLayoutHeight(R.id.predefined_icon, iconSize, COMPLEX_UNIT_DIP);
+ views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_qs_dnd_on);
+ }
+
+ return new RemoteViewsAndSizes(views, avatarSize);
+ }
+
+
private RemoteViews createMissedCallRemoteViews() {
RemoteViews views = setViewForContentLayout(new RemoteViews(mContext.getPackageName(),
getLayoutForContent()));
@@ -486,8 +626,8 @@
views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_phone_missed);
if (mLayoutSize == LAYOUT_LARGE) {
views.setInt(R.id.content, "setGravity", Gravity.BOTTOM);
- views.setViewLayoutHeightDimen(R.id.predefined_icon, R.dimen.large_predefined_icon);
- views.setViewLayoutWidthDimen(R.id.predefined_icon, R.dimen.large_predefined_icon);
+ views.setViewLayoutHeightDimen(R.id.predefined_icon, R.dimen.larger_predefined_icon);
+ views.setViewLayoutWidthDimen(R.id.predefined_icon, R.dimen.larger_predefined_icon);
}
setAvailabilityDotPadding(views, R.dimen.availability_dot_notification_padding);
return views;
@@ -932,6 +1072,14 @@
Drawable personDrawable = storyIcon.getPeopleTileDrawable(roundedDrawable,
tile.getPackageName(), getUserId(tile), tile.isImportantConversation(),
hasNewStory);
+
+ if (isDndBlockingTileData(tile)) {
+ // If DND is blocking the conversation, then display the icon in grayscale.
+ ColorMatrix colorMatrix = new ColorMatrix();
+ colorMatrix.setSaturation(0);
+ personDrawable.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
+ }
+
return convertDrawableToBitmap(personDrawable);
}
@@ -960,4 +1108,69 @@
return context.getString(R.string.over_two_weeks_timestamp);
}
}
+
+ /**
+ * Estimates the height (in dp) which the text will have given the text size and the available
+ * width. Returns Integer.MAX_VALUE if the estimation couldn't be obtained, as this is intended
+ * to be used an estimate of the maximum.
+ */
+ private int estimateTextHeight(
+ CharSequence text,
+ @DimenRes int textSizeResId,
+ int availableWidthDp) {
+ StaticLayout staticLayout = buildStaticLayout(text, textSizeResId, availableWidthDp);
+ if (staticLayout == null) {
+ // Return max value (rather than e.g. -1) so the value can be used with <= bound checks.
+ return Integer.MAX_VALUE;
+ }
+ return pxToDp(staticLayout.getHeight());
+ }
+
+ /**
+ * Builds a StaticLayout for the text given the text size and available width. This can be used
+ * to obtain information about how TextView will lay out the text. Returns null if any error
+ * occurred creating a TextView.
+ */
+ @Nullable
+ private StaticLayout buildStaticLayout(
+ CharSequence text,
+ @DimenRes int textSizeResId,
+ int availableWidthDp) {
+ try {
+ TextView textView = new TextView(mContext);
+ textView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+ mContext.getResources().getDimension(textSizeResId));
+ textView.setTextAppearance(android.R.style.TextAppearance_DeviceDefault);
+ TextPaint paint = textView.getPaint();
+ return StaticLayout.Builder.obtain(
+ text, 0, text.length(), paint, dpToPx(availableWidthDp))
+ // Simple break strategy avoids hyphenation unless there's a single word longer
+ // than the line width. We use this break strategy so that we consider text to
+ // "fit" only if it fits in a nice way (i.e. without hyphenation in the middle
+ // of words).
+ .setBreakStrategy(LineBreaker.BREAK_STRATEGY_SIMPLE)
+ .build();
+ } catch (Exception e) {
+ Log.e(TAG, "Could not create static layout: " + e);
+ return null;
+ }
+ }
+
+ private int dpToPx(float dp) {
+ return (int) (dp * mDensity);
+ }
+
+ private int pxToDp(@Px float px) {
+ return (int) (px / mDensity);
+ }
+
+ private static final class RemoteViewsAndSizes {
+ final RemoteViews mRemoteViews;
+ final int mAvatarSize;
+
+ RemoteViewsAndSizes(RemoteViews remoteViews, int avatarSize) {
+ mRemoteViews = remoteViews;
+ mAvatarSize = avatarSize;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index 6888847..62a0df2 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -22,6 +22,7 @@
import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.content.Intent.ACTION_BOOT_COMPLETED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE;
import static com.android.systemui.people.NotificationHelper.getContactUri;
@@ -62,6 +63,7 @@
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
@@ -172,6 +174,7 @@
public void init() {
synchronized (mLock) {
if (!mRegisteredReceivers) {
+ if (DEBUG) Log.d(TAG, "Register receivers");
IntentFilter filter = new IntentFilter();
filter.addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
filter.addAction(ACTION_BOOT_COMPLETED);
@@ -185,6 +188,14 @@
mBroadcastDispatcher.registerReceiver(mBaseBroadcastReceiver, filter,
null /* executor */, UserHandle.ALL);
+ IntentFilter perAppFilter = new IntentFilter(ACTION_PACKAGE_REMOVED);
+ perAppFilter.addDataScheme("package");
+ // BroadcastDispatcher doesn't allow data schemes.
+ mContext.registerReceiver(mBaseBroadcastReceiver, perAppFilter);
+ IntentFilter bootComplete = new IntentFilter(ACTION_BOOT_COMPLETED);
+ bootComplete.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ // BroadcastDispatcher doesn't allow priority.
+ mContext.registerReceiver(mBaseBroadcastReceiver, bootComplete);
mRegisteredReceivers = true;
}
}
@@ -286,8 +297,8 @@
Log.e(TAG, "Cannot update invalid widget");
return;
}
- RemoteViews views = new PeopleTileViewHelper(mContext, tile, appWidgetId,
- options, key).getViews();
+ RemoteViews views = PeopleTileViewHelper.createRemoteViews(mContext, tile, appWidgetId,
+ options, key);
// Tell the AppWidgetManager to perform an update on the current app widget.
mAppWidgetManager.updateAppWidget(appWidgetId, views);
@@ -303,10 +314,6 @@
/** Updates tile in app widget options and the current view. */
public void updateAppWidgetOptionsAndView(int appWidgetId, PeopleSpaceTile tile) {
- if (tile == null) {
- if (DEBUG) Log.w(TAG, "Requested to store null tile");
- return;
- }
synchronized (mTiles) {
mTiles.put(appWidgetId, tile);
}
@@ -320,6 +327,17 @@
*/
@Nullable
public PeopleSpaceTile getTileForExistingWidget(int appWidgetId) {
+ try {
+ return getTileForExistingWidgetThrowing(appWidgetId);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to retrieve conversation for tile: " + e);
+ return null;
+ }
+ }
+
+ @Nullable
+ private PeopleSpaceTile getTileForExistingWidgetThrowing(int appWidgetId) throws
+ PackageManager.NameNotFoundException {
// First, check if tile is cached in memory.
PeopleSpaceTile tile;
synchronized (mTiles) {
@@ -348,7 +366,8 @@
* If a {@link PeopleTileKey} is not provided, fetch one from {@link SharedPreferences}.
*/
@Nullable
- public PeopleSpaceTile getTileFromPersistentStorage(PeopleTileKey key, int appWidgetId) {
+ public PeopleSpaceTile getTileFromPersistentStorage(PeopleTileKey key, int appWidgetId) throws
+ PackageManager.NameNotFoundException {
if (!key.isValid()) {
Log.e(TAG, "PeopleTileKey invalid: " + key.toString());
return null;
@@ -358,7 +377,6 @@
Log.d(TAG, "System services are null");
return null;
}
-
try {
if (DEBUG) Log.d(TAG, "Retrieving Tile from storage: " + key.toString());
ConversationChannel channel = mIPeopleManager.getConversation(
@@ -383,9 +401,9 @@
}
// Add current state.
- return updateWithCurrentState(storedTile.build(), ACTION_BOOT_COMPLETED);
- } catch (Exception e) {
- Log.e(TAG, "Failed to retrieve conversation for tile: " + e);
+ return getTileWithCurrentState(storedTile.build(), ACTION_BOOT_COMPLETED);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not retrieve data: " + e);
return null;
}
}
@@ -397,7 +415,6 @@
public void updateWidgetsWithNotificationChanged(StatusBarNotification sbn,
PeopleSpaceUtils.NotificationAction notificationAction) {
if (DEBUG) {
- Log.d(TAG, "updateWidgetsWithNotificationChanged called");
if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) {
Log.d(TAG, "Notification posted, key: " + sbn.getKey());
} else {
@@ -414,7 +431,6 @@
PeopleTileKey key = new PeopleTileKey(
sbn.getShortcutId(), sbn.getUser().getIdentifier(), sbn.getPackageName());
if (!key.isValid()) {
- Log.d(TAG, "Sbn doesn't contain valid PeopleTileKey: " + key.toString());
return;
}
int[] widgetIds = mAppWidgetManager.getAppWidgetIds(
@@ -775,7 +791,13 @@
/** Adds a widget based on {@code key} mapped to {@code appWidgetId}. */
public void addNewWidget(int appWidgetId, PeopleTileKey key) {
if (DEBUG) Log.d(TAG, "addNewWidget called with key for appWidgetId: " + appWidgetId);
- PeopleSpaceTile tile = getTileFromPersistentStorage(key, appWidgetId);
+ PeopleSpaceTile tile = null;
+ try {
+ tile = getTileFromPersistentStorage(key, appWidgetId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Cannot add widget since app was uninstalled");
+ return;
+ }
if (tile == null) {
return;
}
@@ -1009,80 +1031,93 @@
Optional.empty());
if (DEBUG) Log.i(TAG, "Returning tile preview for shortcutId: " + shortcutId);
- return new PeopleTileViewHelper(mContext, augmentedTile, 0, options,
- new PeopleTileKey(augmentedTile)).getViews();
+ return PeopleTileViewHelper.createRemoteViews(mContext, augmentedTile, 0, options,
+ new PeopleTileKey(augmentedTile));
}
protected final BroadcastReceiver mBaseBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (DEBUG) Log.d(TAG, "Update widgets from: " + action);
- mBgExecutor.execute(() -> updateWidgetsOnStateChange(action));
+ if (DEBUG) Log.d(TAG, "Update widgets from: " + intent.getAction());
+ mBgExecutor.execute(() -> updateWidgetsFromBroadcastInBackground(intent.getAction()));
}
};
- /** Updates any app widget based on the current state. */
+ /** Updates any app widget to the current state, triggered by a broadcast update. */
@VisibleForTesting
- void updateWidgetsOnStateChange(String entryPoint) {
+ void updateWidgetsFromBroadcastInBackground(String entryPoint) {
int[] appWidgetIds = mAppWidgetManager.getAppWidgetIds(
new ComponentName(mContext, PeopleSpaceWidgetProvider.class));
if (appWidgetIds == null) {
return;
}
- synchronized (mLock) {
- for (int appWidgetId : appWidgetIds) {
- PeopleSpaceTile tile = getTileForExistingWidget(appWidgetId);
- if (tile == null) {
- Log.e(TAG, "Matching conversation not found for shortcut ID");
- } else {
- tile = updateWithCurrentState(tile, entryPoint);
+ for (int appWidgetId : appWidgetIds) {
+ PeopleSpaceTile existingTile = null;
+ PeopleSpaceTile updatedTile = null;
+ try {
+ synchronized (mLock) {
+ existingTile = getTileForExistingWidgetThrowing(appWidgetId);
+ if (existingTile == null) {
+ Log.e(TAG, "Matching conversation not found for shortcut ID");
+ return;
+ }
+ updatedTile = getTileWithCurrentState(existingTile, entryPoint);
+ updateAppWidgetOptionsAndView(appWidgetId, updatedTile);
}
- updateAppWidgetOptionsAndView(appWidgetId, tile);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Delete data for uninstalled widgets.
+ Log.e(TAG, "Package no longer found for tile: " + e);
+ synchronized (mLock) {
+ updateAppWidgetOptionsAndView(appWidgetId, updatedTile);
+ }
+ deleteWidgets(new int[]{appWidgetId});
}
}
}
- /** Checks the current state of {@code tile} dependencies, updating fields as necessary. */
+ /** Checks the current state of {@code tile} dependencies, modifying fields as necessary. */
@Nullable
- private PeopleSpaceTile updateWithCurrentState(PeopleSpaceTile tile,
- String entryPoint) {
+ private PeopleSpaceTile getTileWithCurrentState(PeopleSpaceTile tile,
+ String entryPoint) throws
+ PackageManager.NameNotFoundException {
PeopleSpaceTile.Builder updatedTile = tile.toBuilder();
- try {
- switch (entryPoint) {
- case NotificationManager
- .ACTION_INTERRUPTION_FILTER_CHANGED:
- updatedTile.setNotificationPolicyState(getNotificationPolicyState());
- break;
- case Intent.ACTION_PACKAGES_SUSPENDED:
- case Intent.ACTION_PACKAGES_UNSUSPENDED:
- updatedTile.setIsPackageSuspended(getPackageSuspended(tile));
- break;
- case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
- case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
- case Intent.ACTION_USER_UNLOCKED:
- updatedTile.setIsUserQuieted(getUserQuieted(tile));
- break;
- case Intent.ACTION_LOCALE_CHANGED:
- break;
- case ACTION_BOOT_COMPLETED:
- default:
- updatedTile.setIsUserQuieted(getUserQuieted(tile)).setIsPackageSuspended(
- getPackageSuspended(tile)).setNotificationPolicyState(
- getNotificationPolicyState());
- }
- } catch (Exception e) {
- Log.e(TAG, "Package no longer found for tile: " + tile.toString() + e);
- return null;
+ switch (entryPoint) {
+ case NotificationManager
+ .ACTION_INTERRUPTION_FILTER_CHANGED:
+ updatedTile.setNotificationPolicyState(getNotificationPolicyState());
+ break;
+ case Intent.ACTION_PACKAGES_SUSPENDED:
+ case Intent.ACTION_PACKAGES_UNSUSPENDED:
+ updatedTile.setIsPackageSuspended(getPackageSuspended(tile));
+ break;
+ case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
+ case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
+ case Intent.ACTION_USER_UNLOCKED:
+ updatedTile.setIsUserQuieted(getUserQuieted(tile));
+ break;
+ case Intent.ACTION_LOCALE_CHANGED:
+ break;
+ case ACTION_BOOT_COMPLETED:
+ default:
+ updatedTile.setIsUserQuieted(getUserQuieted(tile)).setIsPackageSuspended(
+ getPackageSuspended(tile)).setNotificationPolicyState(
+ getNotificationPolicyState());
}
return updatedTile.build();
}
- private boolean getPackageSuspended(PeopleSpaceTile tile) throws Exception {
+ private boolean getPackageSuspended(PeopleSpaceTile tile) throws
+ PackageManager.NameNotFoundException {
boolean packageSuspended = !TextUtils.isEmpty(tile.getPackageName())
&& mPackageManager.isPackageSuspended(tile.getPackageName());
if (DEBUG) Log.d(TAG, "Package suspended: " + packageSuspended);
+ // isPackageSuspended() only throws an exception if the app has been uninstalled, and the
+ // app data has also been cleared. We want to empty the layout when the app is uninstalled
+ // regardless of app data clearing, which getApplicationInfoAsUser() handles.
+ mPackageManager.getApplicationInfoAsUser(
+ tile.getPackageName(), PackageManager.GET_META_DATA,
+ PeopleSpaceUtils.getUserId(tile));
return packageSuspended;
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/PrivacyChipDrawable.java b/packages/SystemUI/src/com/android/systemui/privacy/television/PrivacyChipDrawable.java
new file mode 100644
index 0000000..e5479ba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/television/PrivacyChipDrawable.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy.television;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+
+/**
+ * Drawable that can go from being the background of the privacy icons to a small dot.
+ * The icons are not included.
+ */
+public class PrivacyChipDrawable extends Drawable {
+
+ private static final String TAG = PrivacyChipDrawable.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private float mWidth;
+ private float mHeight;
+ private float mMarginEnd;
+ private float mRadius;
+ private int mDotAlpha;
+ private int mBgAlpha;
+
+ private float mTargetWidth;
+ private final int mMinWidth;
+ private final int mIconWidth;
+ private final int mIconPadding;
+ private final int mBgWidth;
+ private final int mBgHeight;
+ private final int mBgRadius;
+ private final int mDotSize;
+
+ private final AnimatorSet mFadeIn;
+ private final AnimatorSet mFadeOut;
+ private final AnimatorSet mCollapse;
+ private final AnimatorSet mExpand;
+ private Animator mWidthAnimator;
+
+ private final Paint mChipPaint;
+ private final Paint mBgPaint;
+
+ private boolean mIsRtl;
+
+ private boolean mIsExpanded = true;
+
+ private PrivacyChipDrawableListener mListener;
+
+ interface PrivacyChipDrawableListener {
+ void onFadeOutFinished();
+ }
+
+ public PrivacyChipDrawable(Context context) {
+ mChipPaint = new Paint();
+ mChipPaint.setStyle(Paint.Style.FILL);
+ mChipPaint.setColor(context.getColor(R.color.privacy_circle));
+ mChipPaint.setAlpha(mDotAlpha);
+ mChipPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
+
+ mBgPaint = new Paint();
+ mBgPaint.setStyle(Paint.Style.FILL);
+ mBgPaint.setColor(context.getColor(R.color.privacy_chip_dot_bg_tint));
+ mBgPaint.setAlpha(mBgAlpha);
+ mBgPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
+
+ mBgWidth = context.getResources().getDimensionPixelSize(R.dimen.privacy_chip_dot_bg_width);
+ mBgHeight = context.getResources().getDimensionPixelSize(
+ R.dimen.privacy_chip_dot_bg_height);
+ mBgRadius = context.getResources().getDimensionPixelSize(
+ R.dimen.privacy_chip_dot_bg_radius);
+
+ mMinWidth = context.getResources().getDimensionPixelSize(R.dimen.privacy_chip_min_width);
+ mIconWidth = context.getResources().getDimensionPixelSize(R.dimen.privacy_chip_icon_size);
+ mIconPadding = context.getResources().getDimensionPixelSize(
+ R.dimen.privacy_chip_icon_margin_in_between);
+ mDotSize = context.getResources().getDimensionPixelSize(R.dimen.privacy_chip_dot_size);
+
+ mWidth = mMinWidth;
+ mHeight = context.getResources().getDimensionPixelSize(R.dimen.privacy_chip_height);
+ mRadius = context.getResources().getDimensionPixelSize(R.dimen.privacy_chip_radius);
+
+ mExpand = (AnimatorSet) AnimatorInflater.loadAnimator(context,
+ R.anim.tv_privacy_chip_expand);
+ mExpand.setTarget(this);
+
+ mCollapse = (AnimatorSet) AnimatorInflater.loadAnimator(context,
+ R.anim.tv_privacy_chip_collapse);
+ mCollapse.setTarget(this);
+
+ mFadeIn = (AnimatorSet) AnimatorInflater.loadAnimator(context,
+ R.anim.tv_privacy_chip_fade_in);
+ mFadeIn.setTarget(this);
+
+ mFadeOut = (AnimatorSet) AnimatorInflater.loadAnimator(context,
+ R.anim.tv_privacy_chip_fade_out);
+ mFadeOut.setTarget(this);
+ mFadeOut.addListener(new Animator.AnimatorListener() {
+ private boolean mCancelled;
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mCancelled = false;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mCancelled && mListener != null) {
+ if (DEBUG) Log.d(TAG, "Fade-out complete");
+ mListener.onFadeOutFinished();
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ // no-op
+ }
+ });
+ }
+
+ /**
+ * Pass null to remove listener.
+ */
+ public void setListener(@Nullable PrivacyChipDrawableListener listener) {
+ this.mListener = listener;
+ }
+
+ /**
+ * Call once the view that is showing the drawable is visible to start fading the chip in.
+ */
+ public void startInitialFadeIn() {
+ if (DEBUG) Log.d(TAG, "initial fade-in");
+ mFadeIn.start();
+ }
+
+ @Override
+ public void draw(@NonNull Canvas canvas) {
+ Rect bounds = getBounds();
+
+ int centerVertical = (bounds.bottom - bounds.top) / 2;
+ // Dot background
+ RectF bgBounds = new RectF(
+ mIsRtl ? bounds.left : bounds.right - mBgWidth,
+ centerVertical - mBgHeight / 2f,
+ mIsRtl ? bounds.left + mBgWidth : bounds.right,
+ centerVertical + mBgHeight / 2f);
+ if (DEBUG) Log.v(TAG, "bg: " + bgBounds.toShortString());
+ canvas.drawRoundRect(bgBounds, mBgRadius, mBgRadius, mBgPaint);
+
+ // Icon background / dot
+ RectF greenBounds = new RectF(
+ mIsRtl ? bounds.left + mMarginEnd : bounds.right - mWidth - mMarginEnd,
+ centerVertical - mHeight / 2,
+ mIsRtl ? bounds.left + mWidth + mMarginEnd : bounds.right - mMarginEnd,
+ centerVertical + mHeight / 2);
+ if (DEBUG) Log.v(TAG, "green: " + greenBounds.toShortString());
+ canvas.drawRoundRect(greenBounds, mRadius, mRadius, mChipPaint);
+ }
+
+ private void animateToNewTargetWidth(float width) {
+ if (DEBUG) Log.d(TAG, "new target width: " + width);
+ if (width != mTargetWidth) {
+ mTargetWidth = width;
+ Animator newWidthAnimator = ObjectAnimator.ofFloat(this, "width", mTargetWidth);
+ newWidthAnimator.start();
+ if (mWidthAnimator != null) {
+ mWidthAnimator.cancel();
+ }
+ mWidthAnimator = newWidthAnimator;
+ }
+ }
+
+ private void expand() {
+ if (DEBUG) Log.d(TAG, "expanding");
+ if (mIsExpanded) {
+ return;
+ }
+ mIsExpanded = true;
+
+ mExpand.start();
+ mCollapse.cancel();
+ }
+
+ /**
+ * Starts the animation to a dot.
+ */
+ public void collapse() {
+ if (DEBUG) Log.d(TAG, "collapsing");
+ if (!mIsExpanded) {
+ return;
+ }
+ mIsExpanded = false;
+
+ animateToNewTargetWidth(mDotSize);
+ mCollapse.start();
+ mExpand.cancel();
+ }
+
+ /**
+ * Fades out the view if 0 icons are to be shown, expands the chip if it has been collapsed and
+ * makes the width of the chip adjust to the amount of icons to be shown.
+ * Should not be called when only the order of the icons was changed as the chip will expand
+ * again without there being any real update.
+ *
+ * @param iconCount Can be 0 to fade out the chip.
+ */
+ public void updateIcons(int iconCount) {
+ if (DEBUG) Log.d(TAG, "updating icons: " + iconCount);
+
+ // calculate chip size and use it for end value of animation that is specified in code,
+ // not xml
+ if (iconCount == 0) {
+ // fade out if there are no icons
+ mFadeOut.start();
+
+ mWidthAnimator.cancel();
+ mFadeIn.cancel();
+ mExpand.cancel();
+ mCollapse.cancel();
+ return;
+ }
+
+ mFadeOut.cancel();
+ expand();
+ animateToNewTargetWidth(mMinWidth + (iconCount - 1) * (mIconWidth + mIconPadding));
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ setDotAlpha(alpha);
+ setBgAlpha(alpha);
+ }
+
+ @Override
+ public int getAlpha() {
+ return mDotAlpha;
+ }
+
+ /**
+ * Set alpha value the green part of the chip.
+ */
+ @Keep
+ public void setDotAlpha(int alpha) {
+ if (DEBUG) Log.v(TAG, "dot alpha updated to: " + alpha);
+ mDotAlpha = alpha;
+ mChipPaint.setAlpha(alpha);
+ }
+
+ @Keep
+ public int getDotAlpha() {
+ return mDotAlpha;
+ }
+
+ /**
+ * Set alpha value of the background of the chip.
+ */
+ @Keep
+ public void setBgAlpha(int alpha) {
+ if (DEBUG) Log.v(TAG, "bg alpha updated to: " + alpha);
+ mBgAlpha = alpha;
+ mBgPaint.setAlpha(alpha);
+ }
+
+ @Keep
+ public int getBgAlpha() {
+ return mBgAlpha;
+ }
+
+ @Override
+ public void setColorFilter(@Nullable ColorFilter colorFilter) {
+ // no-op
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ /**
+ * The radius of the green part of the chip, not the background.
+ */
+ @Keep
+ public void setRadius(float radius) {
+ mRadius = radius;
+ invalidateSelf();
+ }
+
+ /**
+ * @return The radius of the green part of the chip, not the background.
+ */
+ @Keep
+ public float getRadius() {
+ return mRadius;
+ }
+
+ /**
+ * Height of the green part of the chip, not including the background.
+ */
+ @Keep
+ public void setHeight(float height) {
+ mHeight = height;
+ invalidateSelf();
+ }
+
+ /**
+ * @return Height of the green part of the chip, not including the background.
+ */
+ @Keep
+ public float getHeight() {
+ return mHeight;
+ }
+
+ /**
+ * Width of the green part of the chip, not including the background.
+ */
+ @Keep
+ public void setWidth(float width) {
+ mWidth = width;
+ invalidateSelf();
+ }
+
+ /**
+ * @return Width of the green part of the chip, not including the background.
+ */
+ @Keep
+ public float getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * Margin at the end of the green part of the chip, so that it will be placed in the middle of
+ * the rounded rectangle in the background.
+ */
+ @Keep
+ public void setMarginEnd(float marginEnd) {
+ mMarginEnd = marginEnd;
+ invalidateSelf();
+ }
+
+ /**
+ * @return Margin at the end of the green part of the chip, so that it will be placed in the
+ * middle of the rounded rectangle in the background.
+ */
+ @Keep
+ public float getMarginEnd() {
+ return mMarginEnd;
+ }
+
+ /**
+ * Sets the layout direction.
+ */
+ public void setRtl(boolean isRtl) {
+ mIsRtl = isRtl;
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
index 5ab7bd8..e4f5cde 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
+++ b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java
@@ -25,9 +25,10 @@
import android.annotation.UiThread;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -38,15 +39,20 @@
import android.widget.ImageView;
import android.widget.LinearLayout;
+import androidx.annotation.NonNull;
+
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.privacy.PrivacyChipBuilder;
import com.android.systemui.privacy.PrivacyItem;
import com.android.systemui.privacy.PrivacyItemController;
+import com.android.systemui.privacy.PrivacyType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
@@ -56,9 +62,10 @@
* recording audio, accessing the camera or accessing the location.
*/
@SysUISingleton
-public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemController.Callback {
+public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemController.Callback,
+ PrivacyChipDrawable.PrivacyChipDrawableListener {
private static final String TAG = "TvOngoingPrivacyChip";
- static final boolean DEBUG = false;
+ private static final boolean DEBUG = false;
// This title is used in CameraMicIndicatorsPermissionTest and
// RecognitionServiceMicIndicatorTest.
@@ -68,7 +75,8 @@
@IntDef(prefix = {"STATE_"}, value = {
STATE_NOT_SHOWN,
STATE_APPEARING,
- STATE_SHOWN,
+ STATE_EXPANDED,
+ STATE_COLLAPSED,
STATE_DISAPPEARING
})
public @interface State {
@@ -76,46 +84,58 @@
private static final int STATE_NOT_SHOWN = 0;
private static final int STATE_APPEARING = 1;
- private static final int STATE_SHOWN = 2;
- private static final int STATE_DISAPPEARING = 3;
+ private static final int STATE_EXPANDED = 2;
+ private static final int STATE_COLLAPSED = 3;
+ private static final int STATE_DISAPPEARING = 4;
- private static final int ANIMATION_DURATION_MS = 200;
+ private static final int EXPANDED_DURATION_MS = 4000;
+ public final int mAnimationDurationMs;
private final Context mContext;
private final PrivacyItemController mPrivacyItemController;
- private View mIndicatorView;
+ private ViewGroup mIndicatorView;
private boolean mViewAndWindowAdded;
private ObjectAnimator mAnimator;
private boolean mMicCameraIndicatorFlagEnabled;
- private boolean mLocationIndicatorEnabled;
- private List<PrivacyItem> mPrivacyItems;
+ private boolean mAllIndicatorsEnabled;
+
+ @NonNull
+ private List<PrivacyItem> mPrivacyItems = Collections.emptyList();
private LinearLayout mIconsContainer;
private final int mIconSize;
private final int mIconMarginStart;
+ private PrivacyChipDrawable mChipDrawable;
+
+ private final Handler mUiThreadHandler = new Handler(Looper.getMainLooper());
+ private final Runnable mCollapseRunnable = this::collapseChip;
+
@State
private int mState = STATE_NOT_SHOWN;
@Inject
public TvOngoingPrivacyChip(Context context, PrivacyItemController privacyItemController) {
super(context);
- Log.d(TAG, "Privacy chip running without id");
+ if (DEBUG) Log.d(TAG, "Privacy chip running");
mContext = context;
mPrivacyItemController = privacyItemController;
Resources res = mContext.getResources();
- mIconMarginStart = Math.round(res.getDimension(R.dimen.privacy_chip_icon_margin));
+ mIconMarginStart = Math.round(
+ res.getDimension(R.dimen.privacy_chip_icon_margin_in_between));
mIconSize = res.getDimensionPixelSize(R.dimen.privacy_chip_icon_size);
+ mAnimationDurationMs = res.getInteger(R.integer.privacy_chip_animation_millis);
+
mMicCameraIndicatorFlagEnabled = privacyItemController.getMicCameraAvailable();
- mLocationIndicatorEnabled = privacyItemController.getLocationAvailable();
+ mAllIndicatorsEnabled = privacyItemController.getAllIndicatorsAvailable();
if (DEBUG) {
Log.d(TAG, "micCameraIndicators: " + mMicCameraIndicatorFlagEnabled);
- Log.d(TAG, "locationIndicators: " + mLocationIndicatorEnabled);
+ Log.d(TAG, "allIndicators: " + mAllIndicatorsEnabled);
}
}
@@ -125,69 +145,145 @@
}
@Override
- public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) {
+ public void onPrivacyItemsChanged(@NonNull List<PrivacyItem> privacyItems) {
if (DEBUG) Log.d(TAG, "PrivacyItemsChanged");
- mPrivacyItems = privacyItems;
- updateUI();
+
+ List<PrivacyItem> updatedPrivacyItems = new ArrayList<>(privacyItems);
+ // Never show the location indicator on tv.
+ if (updatedPrivacyItems.removeIf(
+ privacyItem -> privacyItem.getPrivacyType() == PrivacyType.TYPE_LOCATION)) {
+ if (DEBUG) Log.v(TAG, "Removed the location item");
+ }
+
+ if (isChipDisabled()) {
+ fadeOutIndicator();
+ mPrivacyItems = updatedPrivacyItems;
+ return;
+ }
+
+ // Do they have the same elements? (order doesn't matter)
+ if (updatedPrivacyItems.size() == mPrivacyItems.size()
+ && mPrivacyItems.containsAll(updatedPrivacyItems)) {
+ if (DEBUG) Log.d(TAG, "List wasn't updated");
+ return;
+ }
+
+ mPrivacyItems = updatedPrivacyItems;
+ updateChip();
+ }
+
+ private void updateChip() {
+ if (DEBUG) Log.d(TAG, mPrivacyItems.size() + " privacy items");
+
+ if (mPrivacyItems.isEmpty()) {
+ if (DEBUG) Log.d(TAG, "removing indicator (state: " + stateToString(mState) + ")");
+ fadeOutIndicator();
+ return;
+ }
+
+ if (DEBUG) Log.d(TAG, "Current state: " + stateToString(mState));
+ switch (mState) {
+ case STATE_NOT_SHOWN:
+ createAndShowIndicator();
+ break;
+ case STATE_APPEARING:
+ case STATE_EXPANDED:
+ updateIcons();
+ collapseLater();
+ break;
+ case STATE_COLLAPSED:
+ case STATE_DISAPPEARING:
+ mState = STATE_EXPANDED;
+ updateIcons();
+ animateIconAppearance();
+ break;
+ }
+ }
+
+ /**
+ * Collapse the chip EXPANDED_DURATION_MS from now.
+ */
+ private void collapseLater() {
+ mUiThreadHandler.removeCallbacks(mCollapseRunnable);
+ if (DEBUG) Log.d(TAG, "chip will collapse in " + EXPANDED_DURATION_MS + "ms");
+ mUiThreadHandler.postDelayed(mCollapseRunnable, EXPANDED_DURATION_MS);
+ }
+
+ private void collapseChip() {
+ if (DEBUG) Log.d(TAG, "collapseChip");
+
+ if (mState != STATE_EXPANDED) {
+ return;
+ }
+ mState = STATE_COLLAPSED;
+
+ if (mChipDrawable != null) {
+ mChipDrawable.collapse();
+ }
+ animateIconDisappearance();
}
@Override
public void onFlagMicCameraChanged(boolean flag) {
if (DEBUG) Log.d(TAG, "mic/camera indicators enabled: " + flag);
mMicCameraIndicatorFlagEnabled = flag;
+ updateChipOnFlagChanged();
}
@Override
- public void onFlagLocationChanged(boolean flag) {
- if (DEBUG) Log.d(TAG, "location indicators enabled: " + flag);
- mLocationIndicatorEnabled = flag;
+ public void onFlagAllChanged(boolean flag) {
+ if (DEBUG) Log.d(TAG, "all indicators enabled: " + flag);
+ mAllIndicatorsEnabled = flag;
+ updateChipOnFlagChanged();
}
- private void updateUI() {
- if (DEBUG) Log.d(TAG, mPrivacyItems.size() + " privacy items");
+ private boolean isChipDisabled() {
+ return !(mMicCameraIndicatorFlagEnabled || mAllIndicatorsEnabled);
+ }
- if ((mMicCameraIndicatorFlagEnabled || mLocationIndicatorEnabled)
- && !mPrivacyItems.isEmpty()) {
- if (mState == STATE_NOT_SHOWN || mState == STATE_DISAPPEARING) {
- showIndicator();
- } else {
- if (DEBUG) Log.d(TAG, "only updating icons");
- PrivacyChipBuilder builder = new PrivacyChipBuilder(mContext, mPrivacyItems);
- setIcons(builder.generateIcons(), mIconsContainer);
- mIconsContainer.requestLayout();
- }
+ private void updateChipOnFlagChanged() {
+ if (isChipDisabled()) {
+ fadeOutIndicator();
} else {
- hideIndicatorIfNeeded();
+ updateChip();
}
}
@UiThread
- private void hideIndicatorIfNeeded() {
+ private void fadeOutIndicator() {
if (mState == STATE_NOT_SHOWN || mState == STATE_DISAPPEARING) return;
+ mUiThreadHandler.removeCallbacks(mCollapseRunnable);
+
if (mViewAndWindowAdded) {
mState = STATE_DISAPPEARING;
- animateDisappearance();
+ animateIconDisappearance();
} else {
// Appearing animation has not started yet, as we were still waiting for the View to be
// laid out.
mState = STATE_NOT_SHOWN;
removeIndicatorView();
}
+ if (mChipDrawable != null) {
+ mChipDrawable.updateIcons(0);
+ }
}
@UiThread
- private void showIndicator() {
+ private void createAndShowIndicator() {
mState = STATE_APPEARING;
+ if (mIndicatorView != null || mViewAndWindowAdded) {
+ removeIndicatorView();
+ }
+
// Inflate the indicator view
- mIndicatorView = LayoutInflater.from(mContext).inflate(
+ mIndicatorView = (ViewGroup) LayoutInflater.from(mContext).inflate(
R.layout.tv_ongoing_privacy_chip, null);
- // 1. Set alpha to 0.
+ // 1. Set icon alpha to 0.
// 2. Wait until the window is shown and the view is laid out.
// 3. Start a "fade in" (alpha) animation.
- mIndicatorView.setAlpha(0f);
mIndicatorView
.getViewTreeObserver()
.addOnGlobalLayoutListener(
@@ -196,20 +292,35 @@
public void onGlobalLayout() {
// State could have changed to NOT_SHOWN (if all the recorders are
// already gone)
- if (mState != STATE_APPEARING) return;
+ if (mState != STATE_APPEARING) {
+ return;
+ }
mViewAndWindowAdded = true;
// Remove the observer
mIndicatorView.getViewTreeObserver().removeOnGlobalLayoutListener(
this);
- animateAppearance();
+ animateIconAppearance();
+ mChipDrawable.startInitialFadeIn();
}
});
+ final boolean isRtl = mContext.getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_RTL;
+ if (DEBUG) Log.d(TAG, "is RTL: " + isRtl);
+
+ mChipDrawable = new PrivacyChipDrawable(mContext);
+ mChipDrawable.setListener(this);
+ mChipDrawable.setRtl(isRtl);
+ ImageView chipBackground = mIndicatorView.findViewById(R.id.chip_drawable);
+ if (chipBackground != null) {
+ chipBackground.setImageDrawable(mChipDrawable);
+ }
+
mIconsContainer = mIndicatorView.findViewById(R.id.icons_container);
- PrivacyChipBuilder builder = new PrivacyChipBuilder(mContext, mPrivacyItems);
- setIcons(builder.generateIcons(), mIconsContainer);
+ mIconsContainer.setAlpha(0f);
+ updateIcons();
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
WRAP_CONTENT,
@@ -217,19 +328,19 @@
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
- layoutParams.gravity = Gravity.TOP | Gravity.END;
+ layoutParams.gravity = Gravity.TOP | (isRtl ? Gravity.LEFT : Gravity.RIGHT);
layoutParams.setTitle(LAYOUT_PARAMS_TITLE);
layoutParams.packageName = mContext.getPackageName();
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
windowManager.addView(mIndicatorView, layoutParams);
-
}
- private void setIcons(List<Drawable> icons, ViewGroup iconsContainer) {
- iconsContainer.removeAllViews();
+ private void updateIcons() {
+ List<Drawable> icons = new PrivacyChipBuilder(mContext, mPrivacyItems).generateIcons();
+ mIconsContainer.removeAllViews();
for (int i = 0; i < icons.size(); i++) {
Drawable icon = icons.get(i);
- icon.mutate().setTint(Color.WHITE);
+ icon.mutate().setTint(mContext.getColor(R.color.privacy_icon_tint));
ImageView imageView = new ImageView(mContext);
imageView.setImageDrawable(icon);
imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
@@ -241,22 +352,25 @@
imageView.setLayoutParams(layoutParams);
}
}
+ if (mChipDrawable != null) {
+ mChipDrawable.updateIcons(icons.size());
+ }
}
- private void animateAppearance() {
- animateAlphaTo(1f);
+ private void animateIconAppearance() {
+ animateIconAlphaTo(1f);
}
- private void animateDisappearance() {
- animateAlphaTo(0f);
+ private void animateIconDisappearance() {
+ animateIconAlphaTo(0f);
}
- private void animateAlphaTo(final float endValue) {
+ private void animateIconAlphaTo(float endValue) {
if (mAnimator == null) {
if (DEBUG) Log.d(TAG, "set up animator");
mAnimator = new ObjectAnimator();
- mAnimator.setTarget(mIndicatorView);
+ mAnimator.setTarget(mIconsContainer);
mAnimator.setProperty(View.ALPHA);
mAnimator.addListener(new AnimatorListenerAdapter() {
boolean mCancelled;
@@ -280,7 +394,7 @@
// and then onAnimationEnd(...). We, however, only want to proceed here if the
// animation ended "naturally".
if (!mCancelled) {
- onAnimationFinished();
+ onIconAnimationFinished();
}
}
});
@@ -289,19 +403,37 @@
mAnimator.cancel();
}
- final float currentValue = mIndicatorView.getAlpha();
+ final float currentValue = mIconsContainer.getAlpha();
+ if (currentValue == endValue) {
+ if (DEBUG) Log.d(TAG, "alpha not changing");
+ return;
+ }
if (DEBUG) Log.d(TAG, "animate alpha to " + endValue + " from " + currentValue);
- mAnimator.setDuration((int) (Math.abs(currentValue - endValue) * ANIMATION_DURATION_MS));
+ mAnimator.setDuration(mAnimationDurationMs);
mAnimator.setFloatValues(endValue);
mAnimator.start();
}
- private void onAnimationFinished() {
- if (DEBUG) Log.d(TAG, "onAnimationFinished");
+ @Override
+ public void onFadeOutFinished() {
+ if (DEBUG) Log.d(TAG, "drawable fade-out finished");
+
+ if (mState == STATE_DISAPPEARING) {
+ removeIndicatorView();
+ mState = STATE_NOT_SHOWN;
+ }
+ }
+
+ private void onIconAnimationFinished() {
+ if (DEBUG) Log.d(TAG, "onAnimationFinished (icon fade)");
+
+ if (mState == STATE_APPEARING || mState == STATE_EXPANDED) {
+ collapseLater();
+ }
if (mState == STATE_APPEARING) {
- mState = STATE_SHOWN;
+ mState = STATE_EXPANDED;
} else if (mState == STATE_DISAPPEARING) {
removeIndicatorView();
mState = STATE_NOT_SHOWN;
@@ -312,14 +444,39 @@
if (DEBUG) Log.d(TAG, "removeIndicatorView");
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
- if (windowManager != null) {
+ if (windowManager != null && mIndicatorView != null) {
windowManager.removeView(mIndicatorView);
}
mIndicatorView = null;
mAnimator = null;
+ if (mChipDrawable != null) {
+ mChipDrawable.setListener(null);
+ mChipDrawable = null;
+ }
+
mViewAndWindowAdded = false;
}
+ /**
+ * Used in debug logs.
+ */
+ private String stateToString(@State int state) {
+ switch (state) {
+ case STATE_NOT_SHOWN:
+ return "NOT_SHOWN";
+ case STATE_APPEARING:
+ return "APPEARING";
+ case STATE_EXPANDED:
+ return "EXPANDED";
+ case STATE_COLLAPSED:
+ return "COLLAPSED";
+ case STATE_DISAPPEARING:
+ return "DISAPPEARING";
+ default:
+ return "INVALID";
+ }
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index edfbed0..6660081 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -297,7 +297,7 @@
// start margin of next page).
qsPanelController.setPageMargin(mSideMargins);
} else if (view == mHeader) {
- // No content padding for the header.
+ quickStatusBarHeaderController.setContentMargins(mContentPadding, mContentPadding);
} else {
view.setPaddingRelative(
mContentPadding,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 2d0d87d..bcce87a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -46,6 +46,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
public class QSDetail extends LinearLayout {
@@ -83,6 +84,8 @@
private boolean mSwitchState;
private QSFooter mFooter;
+ private NotificationsQuickSettingsContainer mContainer;
+
public QSDetail(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@@ -115,6 +118,10 @@
mClipper = new QSDetailClipper(this);
}
+ public void setContainer(NotificationsQuickSettingsContainer container) {
+ mContainer = container;
+ }
+
/** */
public void setQsPanel(QSPanelController panelController, QuickStatusBarHeader header,
QSFooter footer) {
@@ -242,6 +249,9 @@
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
animateDetailVisibleDiff(x, y, visibleDiff, listener);
+ if (mContainer != null) {
+ mContainer.setDetailShowing(showingDetail);
+ }
}
protected void animateDetailVisibleDiff(int x, int y, boolean visibleDiff, AnimatorListener listener) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index f6d9389..929aeda 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -28,6 +28,7 @@
import android.widget.TextView;
import android.widget.Toast;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto;
@@ -73,7 +74,7 @@
private final PageIndicator mPageIndicator;
private final View mPowerMenuLite;
private final boolean mShowPMLiteButton;
- private GlobalActionsDialogLite mGlobalActionsDialog;
+ private final GlobalActionsDialogLite mGlobalActionsDialog;
private final UiEventLogger mUiEventLogger;
private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
@@ -272,7 +273,8 @@
private void startSettingsActivity() {
ActivityLaunchAnimator.Controller animationController =
mSettingsButtonContainer != null ? ActivityLaunchAnimator.Controller.fromView(
- mSettingsButtonContainer) : null;
+ mSettingsButtonContainer,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON) : null;
mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
true /* dismissShade */, animationController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index e1a66b2..c28c649 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -293,6 +293,7 @@
public void setContainer(ViewGroup container) {
if (container instanceof NotificationsQuickSettingsContainer) {
mQSCustomizerController.setContainer((NotificationsQuickSettingsContainer) container);
+ mQSDetail.setContainer((NotificationsQuickSettingsContainer) container);
}
}
@@ -539,8 +540,9 @@
private void pinToBottom(float absoluteBottomPosition, MediaHost mediaHost, boolean expanded) {
View hostView = mediaHost.getHostView();
- // on keyguard we cross-fade to expanded, so no need to pin it.
- if (mLastQSExpansion > 0 && !isKeyguardState()) {
+ // On keyguard we cross-fade to expanded, so no need to pin it.
+ // If the collapsed qs isn't visible, we also just keep it at the laid out position.
+ if (mLastQSExpansion > 0 && !isKeyguardState() && mQqsMediaHost.getVisible()) {
float targetPosition = absoluteBottomPosition - getTotalBottomMargin(hostView)
- hostView.getHeight();
float currentPosition = mediaHost.getCurrentBounds().top
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 0bb0a3f..c70eaff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -425,7 +425,7 @@
LinearLayout.LayoutParams layoutParams = (LayoutParams) hostView.getLayoutParams();
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
layoutParams.width = horizontal ? 0 : ViewGroup.LayoutParams.MATCH_PARENT;
- layoutParams.weight = horizontal ? 1.2f : 0;
+ layoutParams.weight = horizontal ? 1f : 0;
// Add any bottom margin, such that the total spacing is correct. This is only
// necessary if the view isn't horizontal, since otherwise the padding is
// carried in the parent of this view (to ensure correct vertical alignment)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 525bad8..6ddf2a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -45,7 +45,9 @@
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.qs.external.CustomTileStatePersister;
import com.android.systemui.qs.external.TileLifecycleManager;
+import com.android.systemui.qs.external.TileServiceKey;
import com.android.systemui.qs.external.TileServices;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.UserTracker;
@@ -93,6 +95,7 @@
private final QSLogger mQSLogger;
private final UiEventLogger mUiEventLogger;
private final InstanceIdSequence mInstanceIdSequence;
+ private final CustomTileStatePersister mCustomTileStatePersister;
private final List<Callback> mCallbacks = new ArrayList<>();
private AutoTileManager mAutoTiles;
@@ -119,7 +122,8 @@
QSLogger qsLogger,
UiEventLogger uiEventLogger,
UserTracker userTracker,
- SecureSettings secureSettings) {
+ SecureSettings secureSettings,
+ CustomTileStatePersister customTileStatePersister) {
mIconController = iconController;
mContext = context;
mUserContext = context;
@@ -139,6 +143,7 @@
mDumpManager.registerDumpable(TAG, this);
mUserTracker = userTracker;
mSecureSettings = secureSettings;
+ mCustomTileStatePersister = customTileStatePersister;
mainHandler.post(() -> {
// This is technically a hack to avoid circular dependency of
@@ -418,6 +423,11 @@
changeTiles(mTileSpecs, newSpecs);
}
+ /**
+ * Change the tiles triggered by the user editing.
+ * <p>
+ * This is not called on device start, or on user change.
+ */
public void changeTiles(List<String> previousTiles, List<String> newTiles) {
final List<String> copy = new ArrayList<>(previousTiles);
final int NP = copy.size();
@@ -433,6 +443,7 @@
mBroadcastDispatcher);
lifecycleManager.onStopListening();
lifecycleManager.onTileRemoved();
+ mCustomTileStatePersister.removeState(new TileServiceKey(component, mCurrentUser));
TileLifecycleManager.setTileAdded(mContext, component, false);
lifecycleManager.flushMessagesAndUnbind();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index e24acf2..659475d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -228,24 +228,5 @@
}
}
}
-
- @Override
- public void setExpansion(float expansion) {
- if (expansion > 0f && expansion < 1f) {
- return;
- }
- boolean selected = expansion == 0f;
- // Expansion == 0f is when QQS is fully showing (as opposed to 1f, which is QS). At this
- // point we want them to be selected so the tiles will marquee (but not at other points
- // of expansion.
- // We set it as not important while we change this, so setting each tile as selected
- // will not cause them to announce themselves until the user has actually selected the
- // item.
- setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
- for (int i = 0; i < getChildCount(); i++) {
- getChildAt(i).setSelected(selected);
- }
- setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 7cc6ecd..997b966 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -399,12 +399,14 @@
mClockIconsSeparatorLayoutParams.width = 0;
setSeparatorVisibility(false);
mShowClockIconsSeparator = false;
+ mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
} else {
datePrivacySeparatorLayoutParams.width = topCutout.width();
mDatePrivacySeparator.setVisibility(View.VISIBLE);
mClockIconsSeparatorLayoutParams.width = topCutout.width();
mShowClockIconsSeparator = true;
setSeparatorVisibility(mKeyguardExpansionFraction == 0f);
+ mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ON);
}
}
mDatePrivacySeparator.setLayoutParams(datePrivacySeparatorLayoutParams);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index cbdcad5..76076f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -177,7 +177,6 @@
mView.onAttach(mIconManager, mQSExpansionPathInterpolator);
mDemoModeController.addCallback(mDemoModeReceiver);
- mHeaderQsPanelController.setContentMargins(0, 0);
}
@Override
@@ -253,6 +252,10 @@
return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled;
}
+ public void setContentMargins(int marginStart, int marginEnd) {
+ mHeaderQsPanelController.setContentMargins(marginStart, marginEnd);
+ }
+
private static class ClockDemoModeReceiver implements DemoMode {
private Clock mClockView;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 10eea82..396eca5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -46,6 +46,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -85,6 +86,7 @@
private final IQSTileService mService;
private final TileServiceManager mServiceManager;
private final int mUser;
+ private final CustomTileStatePersister mCustomTileStatePersister;
private android.graphics.drawable.Icon mDefaultIcon;
private CharSequence mDefaultLabel;
@@ -94,6 +96,8 @@
private boolean mIsTokenGranted;
private boolean mIsShowingDialog;
+ private final TileServiceKey mKey;
+
private CustomTile(
QSHost host,
Looper backgroundLooper,
@@ -104,7 +108,8 @@
ActivityStarter activityStarter,
QSLogger qsLogger,
String action,
- Context userContext
+ Context userContext,
+ CustomTileStatePersister customTileStatePersister
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
@@ -113,15 +118,29 @@
mTile = new Tile();
mUserContext = userContext;
mUser = mUserContext.getUserId();
- updateDefaultTileAndIcon();
+ mKey = new TileServiceKey(mComponent, mUser);
+
mServiceManager = host.getTileServices().getTileWrapper(this);
+ mService = mServiceManager.getTileService();
+ mCustomTileStatePersister = customTileStatePersister;
+ }
+
+ @Override
+ protected void handleInitialize() {
+ updateDefaultTileAndIcon();
if (mServiceManager.isToggleableTile()) {
// Replace states with BooleanState
resetStates();
}
-
- mService = mServiceManager.getTileService();
mServiceManager.setTileChangeListener(this);
+ if (mServiceManager.isActiveTile()) {
+ Tile t = mCustomTileStatePersister.readState(mKey);
+ if (t != null) {
+ applyTileState(t, /* overwriteNulls */ false);
+ mServiceManager.clearPendingBind();
+ refreshState();
+ }
+ }
}
@Override
@@ -191,7 +210,7 @@
@Override
public void onTileChanged(ComponentName tile) {
- updateDefaultTileAndIcon();
+ mHandler.post(this::updateDefaultTileAndIcon);
}
@Override
@@ -213,16 +232,44 @@
}
public Tile getQsTile() {
+ // TODO(b/191145007) Move to background thread safely
updateDefaultTileAndIcon();
return mTile;
}
- public void updateState(Tile tile) {
- mTile.setIcon(tile.getIcon());
- mTile.setLabel(tile.getLabel());
- mTile.setSubtitle(tile.getSubtitle());
- mTile.setContentDescription(tile.getContentDescription());
- mTile.setStateDescription(tile.getStateDescription());
+ /**
+ * Update state of {@link this#mTile} from a remote {@link TileService}.
+ * @param tile tile populated with state to apply
+ */
+ public void updateTileState(Tile tile) {
+ // This comes from a binder call IQSService.updateQsTile
+ mHandler.post(() -> handleUpdateTileState(tile));
+ }
+
+ private void handleUpdateTileState(Tile tile) {
+ applyTileState(tile, /* overwriteNulls */ true);
+ if (mServiceManager.isActiveTile()) {
+ mCustomTileStatePersister.persistState(mKey, tile);
+ }
+ }
+
+ @WorkerThread
+ private void applyTileState(Tile tile, boolean overwriteNulls) {
+ if (tile.getIcon() != null || overwriteNulls) {
+ mTile.setIcon(tile.getIcon());
+ }
+ if (tile.getLabel() != null || overwriteNulls) {
+ mTile.setLabel(tile.getLabel());
+ }
+ if (tile.getSubtitle() != null || overwriteNulls) {
+ mTile.setSubtitle(tile.getSubtitle());
+ }
+ if (tile.getContentDescription() != null || overwriteNulls) {
+ mTile.setContentDescription(tile.getContentDescription());
+ }
+ if (tile.getStateDescription() != null || overwriteNulls) {
+ mTile.setStateDescription(tile.getStateDescription());
+ }
mTile.setState(tile.getState());
}
@@ -459,6 +506,7 @@
final StatusBarStateController mStatusBarStateController;
final ActivityStarter mActivityStarter;
final QSLogger mQSLogger;
+ final CustomTileStatePersister mCustomTileStatePersister;
Context mUserContext;
String mSpec = "";
@@ -472,7 +520,8 @@
MetricsLogger metricsLogger,
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
- QSLogger qsLogger
+ QSLogger qsLogger,
+ CustomTileStatePersister customTileStatePersister
) {
mQSHostLazy = hostLazy;
mBackgroundLooper = backgroundLooper;
@@ -482,6 +531,7 @@
mStatusBarStateController = statusBarStateController;
mActivityStarter = activityStarter;
mQSLogger = qsLogger;
+ mCustomTileStatePersister = customTileStatePersister;
}
Builder setSpec(@NonNull String spec) {
@@ -509,7 +559,8 @@
mActivityStarter,
mQSLogger,
action,
- mUserContext
+ mUserContext,
+ mCustomTileStatePersister
);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt
new file mode 100644
index 0000000..021e632
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.external
+
+import android.content.ComponentName
+import android.content.Context
+import android.service.quicksettings.Tile
+import android.util.Log
+import com.android.internal.annotations.VisibleForTesting
+import org.json.JSONException
+import org.json.JSONObject
+import javax.inject.Inject
+
+data class TileServiceKey(val componentName: ComponentName, val user: Int) {
+ private val string = "${componentName.flattenToString()}:$user"
+ override fun toString() = string
+}
+private const val STATE = "state"
+private const val LABEL = "label"
+private const val SUBTITLE = "subtitle"
+private const val CONTENT_DESCRIPTION = "content_description"
+private const val STATE_DESCRIPTION = "state_description"
+
+/**
+ * Persists and retrieves state for [CustomTile].
+ *
+ * This class will persists to a fixed [SharedPreference] file a state for a pair of [ComponentName]
+ * and user id ([TileServiceKey]).
+ *
+ * It persists the state from a [Tile] necessary to present the view in the same state when
+ * retrieved, with the exception of the icon.
+ */
+class CustomTileStatePersister @Inject constructor(context: Context) {
+ companion object {
+ private const val FILE_NAME = "custom_tiles_state"
+ }
+
+ private val sharedPreferences = context.getSharedPreferences(FILE_NAME, 0)
+
+ /**
+ * Read the state from [SharedPreferences].
+ *
+ * Returns `null` if the tile has no saved state.
+ *
+ * Any fields that have not been saved will be set to `null`
+ */
+ fun readState(key: TileServiceKey): Tile? {
+ val state = sharedPreferences.getString(key.toString(), null) ?: return null
+ return try {
+ readTileFromString(state)
+ } catch (e: JSONException) {
+ Log.e("TileServicePersistence", "Bad saved state: $state", e)
+ null
+ }
+ }
+
+ /**
+ * Persists the state into [SharedPreferences].
+ *
+ * The implementation does not store fields that are `null` or icons.
+ */
+ fun persistState(key: TileServiceKey, tile: Tile) {
+ val state = writeToString(tile)
+
+ sharedPreferences.edit().putString(key.toString(), state).apply()
+ }
+
+ /**
+ * Removes the state for a given tile, user pair.
+ *
+ * Used when the tile is removed by the user.
+ */
+ fun removeState(key: TileServiceKey) {
+ sharedPreferences.edit().remove(key.toString()).apply()
+ }
+}
+
+@VisibleForTesting
+internal fun readTileFromString(stateString: String): Tile {
+ val json = JSONObject(stateString)
+ return Tile().apply {
+ state = json.getInt(STATE)
+ label = json.getStringOrNull(LABEL)
+ subtitle = json.getStringOrNull(SUBTITLE)
+ contentDescription = json.getStringOrNull(CONTENT_DESCRIPTION)
+ stateDescription = json.getStringOrNull(STATE_DESCRIPTION)
+ }
+}
+
+// Properties with null values will not be saved to the Json string in any way. This makes sure
+// to properly retrieve a null in that case.
+private fun JSONObject.getStringOrNull(name: String): String? {
+ return if (has(name)) getString(name) else null
+}
+
+@VisibleForTesting
+internal fun writeToString(tile: Tile): String {
+ // Not storing the icon
+ return with(tile) {
+ JSONObject()
+ .put(STATE, state)
+ .put(LABEL, label)
+ .put(SUBTITLE, subtitle)
+ .put(CONTENT_DESCRIPTION, contentDescription)
+ .put(STATE_DESCRIPTION, stateDescription)
+ .toString()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index aa51771..1d791f5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -57,6 +57,12 @@
private static final String TAG = "TileLifecycleManager";
+ private static final int META_DATA_QUERY_FLAGS =
+ PackageManager.GET_META_DATA
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE;
+
private static final int MSG_ON_ADDED = 0;
private static final int MSG_ON_REMOVED = 1;
private static final int MSG_ON_CLICK = 2;
@@ -130,7 +136,7 @@
public boolean isActiveTile() {
try {
ServiceInfo info = mPackageManagerAdapter.getServiceInfo(mIntent.getComponent(),
- PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA);
+ META_DATA_QUERY_FLAGS);
return info.metaData != null
&& info.metaData.getBoolean(TileService.META_DATA_ACTIVE_TILE, false);
} catch (PackageManager.NameNotFoundException e) {
@@ -148,7 +154,7 @@
public boolean isToggleableTile() {
try {
ServiceInfo info = mPackageManagerAdapter.getServiceInfo(mIntent.getComponent(),
- PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA);
+ META_DATA_QUERY_FLAGS);
return info.metaData != null
&& info.metaData.getBoolean(TileService.META_DATA_TOGGLEABLE_TILE, false);
} catch (PackageManager.NameNotFoundException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 35cf2a1..a7cd113 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -204,7 +204,7 @@
tileServiceManager.clearPendingBind();
tileServiceManager.setLastUpdate(System.currentTimeMillis());
}
- customTile.updateState(tile);
+ customTile.updateTileState(tile);
customTile.refreshState();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 8f7c493..842fd6c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -160,7 +160,8 @@
public QSTile createTile(String tileSpec) {
QSTileImpl tile = createTileInternal(tileSpec);
if (tile != null) {
- tile.handleStale(); // Tile was just created, must be stale.
+ tile.initialize();
+ tile.postStale(); // Tile was just created, must be stale.
}
return tile;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index a938821..4616be8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -50,6 +50,7 @@
import androidx.lifecycle.LifecycleRegistry;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
@@ -158,6 +159,15 @@
*/
abstract public int getMetricsCategory();
+ /**
+ * Performs initialization of the tile
+ *
+ * Use this to perform initialization of the tile. Empty by default.
+ */
+ protected void handleInitialize() {
+
+ }
+
protected QSTileImpl(
QSHost host,
Looper backgroundLooper,
@@ -346,6 +356,15 @@
mHandler.sendEmptyMessage(H.DESTROY);
}
+ /**
+ * Schedules initialization of the tile.
+ *
+ * Should be called upon creation of the tile, before performing other operations
+ */
+ public void initialize() {
+ mHandler.sendEmptyMessage(H.INITIALIZE);
+ }
+
public TState getState() {
return mState;
}
@@ -370,6 +389,13 @@
}
/**
+ * Posts a stale message to the background thread.
+ */
+ public void postStale() {
+ mHandler.sendEmptyMessage(H.STALE);
+ }
+
+ /**
* Handles secondary click on the tile.
*
* Defaults to {@link QSTileImpl#handleClick}
@@ -389,7 +415,8 @@
*/
protected void handleLongClick(@Nullable View view) {
ActivityLaunchAnimator.Controller animationController =
- view != null ? ActivityLaunchAnimator.Controller.fromView(view) : null;
+ view != null ? ActivityLaunchAnimator.Controller.fromView(view,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE) : null;
mActivityStarter.postStartActivityDismissingKeyguard(getLongClickIntent(), 0,
animationController);
}
@@ -580,6 +607,7 @@
private static final int SET_LISTENING = 13;
@VisibleForTesting
protected static final int STALE = 14;
+ private static final int INITIALIZE = 15;
@VisibleForTesting
protected H(Looper looper) {
@@ -638,6 +666,9 @@
} else if (msg.what == STALE) {
name = "handleStale";
handleStale();
+ } else if (msg.what == INITIALIZE) {
+ name = "initialize";
+ handleInitialize();
} else {
throw new IllegalArgumentException("Unknown msg: " + msg.what);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 8aed0a8..d31e67c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -190,6 +190,9 @@
if (collapsed) {
labelContainer.ignoreLastView = true
secondaryLabel.alpha = 0f
+ // Do not marque in QQS
+ label.ellipsize = TextUtils.TruncateAt.END
+ secondaryLabel.ellipsize = TextUtils.TruncateAt.END
}
setLabelColor(getLabelColorForState(QSTile.State.DEFAULT_STATE))
setSecondaryLabelColor(getSecondaryLabelColorForState(QSTile.State.DEFAULT_STATE))
@@ -492,11 +495,7 @@
}
return if (state.state == Tile.STATE_UNAVAILABLE || state is BooleanState) {
- val resName = "$TILE_STATE_RES_PREFIX${state.spec}"
- var arrayResId = resources.getIdentifier(resName, "array", context.packageName)
- if (arrayResId == 0) {
- arrayResId = R.array.tile_states_default
- }
+ var arrayResId = SubtitleArrayMapping.getSubtitleId(state.spec)
val array = resources.getStringArray(arrayResId)
array[state.state]
} else {
@@ -557,6 +556,41 @@
private fun getChevronColorForState(state: Int): Int = getSecondaryLabelColorForState(state)
}
+@VisibleForTesting
+internal object SubtitleArrayMapping {
+ private val subtitleIdsMap = mapOf<String?, Int>(
+ "internet" to R.array.tile_states_internet,
+ "wifi" to R.array.tile_states_wifi,
+ "cell" to R.array.tile_states_cell,
+ "battery" to R.array.tile_states_battery,
+ "dnd" to R.array.tile_states_dnd,
+ "flashlight" to R.array.tile_states_flashlight,
+ "rotation" to R.array.tile_states_rotation,
+ "bt" to R.array.tile_states_bt,
+ "airplane" to R.array.tile_states_airplane,
+ "location" to R.array.tile_states_location,
+ "hotspot" to R.array.tile_states_hotspot,
+ "inversion" to R.array.tile_states_inversion,
+ "saver" to R.array.tile_states_saver,
+ "dark" to R.array.tile_states_dark,
+ "work" to R.array.tile_states_work,
+ "cast" to R.array.tile_states_cast,
+ "night" to R.array.tile_states_night,
+ "screenrecord" to R.array.tile_states_screenrecord,
+ "reverse" to R.array.tile_states_reverse,
+ "reduce_brightness" to R.array.tile_states_reduce_brightness,
+ "cameratoggle" to R.array.tile_states_cameratoggle,
+ "mictoggle" to R.array.tile_states_mictoggle,
+ "controls" to R.array.tile_states_controls,
+ "wallet" to R.array.tile_states_wallet,
+ "alarm" to R.array.tile_states_alarm
+ )
+
+ fun getSubtitleId(spec: String?): Int {
+ return subtitleIdsMap.getOrDefault(spec, R.array.tile_states_default)
+ }
+}
+
private fun colorValuesHolder(name: String, vararg values: Int): PropertyValuesHolder {
return PropertyValuesHolder.ofInt(name, *values).apply {
setEvaluator(ArgbEvaluator.getInstance())
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
index 69d49d4..73d1370 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
@@ -11,6 +11,7 @@
import android.text.format.DateFormat
import android.view.View
import androidx.annotation.VisibleForTesting
+import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.MetricsLogger
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
@@ -70,7 +71,10 @@
}
override fun handleClick(view: View?) {
- val animationController = view?.let { ActivityLaunchAnimator.Controller.fromView(it) }
+ val animationController = view?.let {
+ ActivityLaunchAnimator.Controller.fromView(
+ it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE)
+ }
val pendingIntent = lastAlarmInfo?.showIntent
if (pendingIntent != null) {
mActivityStarter.postStartActivityDismissingKeyguard(pendingIntent, animationController)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index 6d3190f..f66b722 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -22,6 +22,7 @@
import android.os.Looper
import android.service.quicksettings.Tile
import android.view.View
+import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.MetricsLogger
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
@@ -106,7 +107,8 @@
putExtra(ControlsUiController.EXTRA_ANIMATE, true)
}
val animationController = view?.let {
- ActivityLaunchAnimator.Controller.fromView(it)
+ ActivityLaunchAnimator.Controller.fromView(
+ it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE)
}
mUiHandler.post {
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 0e4434b..98cd88a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -37,6 +37,7 @@
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -120,7 +121,8 @@
@Override
protected void handleClick(@Nullable View view) {
ActivityLaunchAnimator.Controller animationController =
- view == null ? null : ActivityLaunchAnimator.Controller.fromView(view);
+ view == null ? null : ActivityLaunchAnimator.Controller.fromView(view,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE);
mUiHandler.post(() -> {
if (mSelectedCard != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index c6b5eb7..5bb3413 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -291,19 +291,24 @@
? res.getString(R.string.screenrecord_ongoing_screen_only)
: res.getString(R.string.screenrecord_ongoing_screen_and_audio);
- Intent stopIntent = getNotificationIntent(this);
+ PendingIntent pendingIntent = PendingIntent.getService(
+ this,
+ REQUEST_CODE,
+ getNotificationIntent(this),
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ Notification.Action stopAction = new Notification.Action.Builder(
+ Icon.createWithResource(this, R.drawable.ic_android),
+ getResources().getString(R.string.screenrecord_stop_label),
+ pendingIntent).build();
Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_screenrecord)
.setContentTitle(notificationTitle)
- .setContentText(getResources().getString(R.string.screenrecord_stop_text))
.setUsesChronometer(true)
.setColorized(true)
.setColor(getResources().getColor(R.color.GM2_red_700))
.setOngoing(true)
.setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE)
- .setContentIntent(
- PendingIntent.getService(this, REQUEST_CODE, stopIntent,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
+ .addAction(stopAction)
.addExtras(extras);
startForeground(NOTIFICATION_RECORDING_ID, builder.build());
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
index 9e11451..0a60f6d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -62,6 +62,7 @@
private final float mCropTouchMargin;
private final Paint mShadePaint;
private final Paint mHandlePaint;
+ private final Paint mContainerBackgroundPaint;
// Crop rect with each element represented as [0,1] along its proper axis.
private RectF mCrop = new RectF(0, 0, 1, 1);
@@ -79,6 +80,9 @@
// The allowable values for the current boundary being dragged
private Range<Float> mMotionRange;
+ // Value [0,1] indicating progress in animateEntrance()
+ private float mEntranceInterpolation = 1f;
+
private CropInteractionListener mCropInteractionListener;
private final ExploreByTouchHelper mExploreByTouchHelper;
@@ -92,6 +96,9 @@
attrs, R.styleable.CropView, 0, 0);
mShadePaint = new Paint();
mShadePaint.setColor(t.getColor(R.styleable.CropView_scrimColor, Color.TRANSPARENT));
+ mContainerBackgroundPaint = new Paint();
+ mContainerBackgroundPaint.setColor(t.getColor(R.styleable.CropView_containerBackgroundColor,
+ Color.TRANSPARENT));
mHandlePaint = new Paint();
mHandlePaint.setColor(t.getColor(R.styleable.CropView_handleColor, Color.BLACK));
mHandlePaint.setStrokeCap(Paint.Cap.ROUND);
@@ -125,10 +132,22 @@
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
- drawShade(canvas, 0, 0, 1, mCrop.top);
- drawShade(canvas, 0, mCrop.bottom, 1, 1);
+ // Top and bottom borders reflect the boundary between the (scrimmed) image and the
+ // opaque container background. This is only meaningful during an entrance transition.
+ float topBorder = MathUtils.lerp(mCrop.top, 0, mEntranceInterpolation);
+ float bottomBorder = MathUtils.lerp(mCrop.bottom, 1, mEntranceInterpolation);
+ drawShade(canvas, 0, topBorder, 1, mCrop.top);
+ drawShade(canvas, 0, mCrop.bottom, 1, bottomBorder);
drawShade(canvas, 0, mCrop.top, mCrop.left, mCrop.bottom);
drawShade(canvas, mCrop.right, mCrop.top, 1, mCrop.bottom);
+
+ // Entrance transition expects the crop bounds to be full width, so we only draw container
+ // background on the top and bottom.
+ drawContainerBackground(canvas, 0, 0, 1, topBorder);
+ drawContainerBackground(canvas, 0, bottomBorder, 1, 1);
+
+ mHandlePaint.setAlpha((int) (mEntranceInterpolation * 255));
+
drawHorizontalHandle(canvas, mCrop.top, /* draw the handle tab up */ true);
drawHorizontalHandle(canvas, mCrop.bottom, /* draw the handle tab down */ false);
drawVerticalHandle(canvas, mCrop.left, /* left */ true);
@@ -282,6 +301,22 @@
}
/**
+ * Fade in crop bounds, animate reveal of cropped-out area from current crop bounds.
+ */
+ public void animateEntrance() {
+ mEntranceInterpolation = 0;
+ ValueAnimator animator = new ValueAnimator();
+ animator.addUpdateListener(animation -> {
+ mEntranceInterpolation = animation.getAnimatedFraction();
+ invalidate();
+ });
+ animator.setFloatValues(0f, 1f);
+ animator.setDuration(750);
+ animator.setInterpolator(new FastOutSlowInInterpolator());
+ animator.start();
+ }
+
+ /**
* Set additional top and bottom padding for the image being cropped (used when the
* corresponding ImageView doesn't take the full height).
*/
@@ -369,6 +404,13 @@
fractionToVerticalPixels(bottom), mShadePaint);
}
+ private void drawContainerBackground(Canvas canvas, float left, float top, float right,
+ float bottom) {
+ canvas.drawRect(fractionToHorizontalPixels(left), fractionToVerticalPixels(top),
+ fractionToHorizontalPixels(right),
+ fractionToVerticalPixels(bottom), mContainerBackgroundPaint);
+ }
+
private void drawHorizontalHandle(Canvas canvas, float frac, boolean handleTabUp) {
int y = fractionToVerticalPixels(frac);
canvas.drawLine(fractionToHorizontalPixels(mCrop.left), y,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
index 4aead817f..55602a9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
@@ -111,30 +111,21 @@
}
/**
- * Stores the given Bitmap to a temp file.
+ * Writes the given Bitmap to outputFile.
*/
- ListenableFuture<File> exportAsTempFile(Executor executor, Bitmap bitmap) {
+ ListenableFuture<File> exportToRawFile(Executor executor, Bitmap bitmap,
+ final File outputFile) {
return CallbackToFutureAdapter.getFuture(
(completer) -> {
executor.execute(() -> {
- File cachePath;
- try {
- cachePath = File.createTempFile("long_screenshot_cache_", ".tmp");
- try (FileOutputStream stream = new FileOutputStream(cachePath)) {
- bitmap.compress(mCompressFormat, mQuality, stream);
- } catch (IOException e) {
- if (cachePath.exists()) {
- //noinspection ResultOfMethodCallIgnored
- cachePath.delete();
- cachePath = null;
- }
- completer.setException(e);
- }
- if (cachePath != null) {
- completer.set(cachePath);
- }
+ try (FileOutputStream stream = new FileOutputStream(outputFile)) {
+ bitmap.compress(mCompressFormat, mQuality, stream);
+ completer.set(outputFile);
} catch (IOException e) {
- // Failed to create a new file
+ if (outputFile.exists()) {
+ //noinspection ResultOfMethodCallIgnored
+ outputFile.delete();
+ }
completer.setException(e);
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
index 730702e..51cc32a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
@@ -20,6 +20,7 @@
import android.graphics.HardwareRenderer;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
+import android.graphics.Region;
import android.graphics.RenderNode;
import android.graphics.drawable.Drawable;
import android.os.Handler;
@@ -46,7 +47,6 @@
private static final String TAG = "ImageTileSet";
- private CallbackRegistry<OnBoundsChangedListener, ImageTileSet, Rect> mOnBoundsListeners;
private CallbackRegistry<OnContentChangedListener, ImageTileSet, Rect> mContentListeners;
@Inject
@@ -54,14 +54,6 @@
mHandler = handler;
}
- interface OnBoundsChangedListener {
- /**
- * Reports an update to the bounding box that contains all active tiles. These are virtual
- * (capture) coordinates which can be either negative or positive.
- */
- void onBoundsChanged(int left, int top, int right, int bottom);
- }
-
interface OnContentChangedListener {
/**
* Mark as dirty and rebuild display list.
@@ -70,25 +62,9 @@
}
private final List<ImageTile> mTiles = new ArrayList<>();
- private final Rect mBounds = new Rect();
+ private final Region mRegion = new Region();
private final Handler mHandler;
- void addOnBoundsChangedListener(OnBoundsChangedListener listener) {
- if (mOnBoundsListeners == null) {
- mOnBoundsListeners = new CallbackRegistry<>(
- new NotifierCallback<OnBoundsChangedListener, ImageTileSet, Rect>() {
- @Override
- public void onNotifyCallback(OnBoundsChangedListener callback,
- ImageTileSet sender,
- int arg, Rect newBounds) {
- callback.onBoundsChanged(newBounds.left, newBounds.top, newBounds.right,
- newBounds.bottom);
- }
- });
- }
- mOnBoundsListeners.add(listener);
- }
-
void addOnContentChangedListener(OnContentChangedListener listener) {
if (mContentListeners == null) {
mContentListeners = new CallbackRegistry<>(
@@ -110,14 +86,8 @@
mHandler.post(() -> addTile(tile));
return;
}
- final Rect newBounds = new Rect(mBounds);
- final Rect newRect = tile.getLocation();
mTiles.add(tile);
- newBounds.union(newRect);
- if (!newBounds.equals(mBounds)) {
- mBounds.set(newBounds);
- notifyBoundsChanged(mBounds);
- }
+ mRegion.op(tile.getLocation(), mRegion, Region.Op.UNION);
notifyContentChanged();
}
@@ -127,12 +97,6 @@
}
}
- private void notifyBoundsChanged(Rect bounds) {
- if (mOnBoundsListeners != null) {
- mOnBoundsListeners.notifyCallbacks(this, 0, bounds);
- }
- }
-
/**
* Returns a drawable to paint the combined contents of the tiles. Drawable dimensions are
* zero-based and map directly to {@link #getLeft()}, {@link #getTop()}, {@link #getRight()},
@@ -153,6 +117,15 @@
return mTiles.size();
}
+ /**
+ * @return the bounding rect around any gaps in the tiles.
+ */
+ Rect getGaps() {
+ Region difference = new Region();
+ difference.op(mRegion.getBounds(), mRegion, Region.Op.DIFFERENCE);
+ return difference.getBounds();
+ }
+
ImageTile get(int i) {
return mTiles.get(i);
}
@@ -182,41 +155,40 @@
}
int getLeft() {
- return mBounds.left;
+ return mRegion.getBounds().left;
}
int getTop() {
- return mBounds.top;
+ return mRegion.getBounds().top;
}
int getRight() {
- return mBounds.right;
+ return mRegion.getBounds().right;
}
int getBottom() {
- return mBounds.bottom;
+ return mRegion.getBounds().bottom;
}
int getWidth() {
- return mBounds.width();
+ return mRegion.getBounds().width();
}
int getHeight() {
- return mBounds.height();
+ return mRegion.getBounds().height();
}
void clear() {
if (mTiles.isEmpty()) {
return;
}
- mBounds.setEmpty();
+ mRegion.setEmpty();
Iterator<ImageTile> i = mTiles.iterator();
while (i.hasNext()) {
ImageTile next = i.next();
next.close();
i.remove();
}
- notifyBoundsChanged(mBounds);
notifyContentChanged();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index d5b4032..07f6d36 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -32,8 +32,6 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.text.TextUtils;
-import android.transition.Transition;
-import android.transition.TransitionListenerAdapter;
import android.util.Log;
import android.view.ScrollCaptureResponse;
import android.view.View;
@@ -74,7 +72,7 @@
private final Executor mUiExecutor;
private final Executor mBackgroundExecutor;
private final ImageExporter mImageExporter;
- private final LongScreenshotHolder mLongScreenshotHolder;
+ private final LongScreenshotData mLongScreenshotHolder;
private ImageView mPreview;
private ImageView mTransitionView;
@@ -103,7 +101,7 @@
@Inject
public LongScreenshotActivity(UiEventLogger uiEventLogger, ImageExporter imageExporter,
@Main Executor mainExecutor, @Background Executor bgExecutor,
- LongScreenshotHolder longScreenshotHolder) {
+ LongScreenshotData longScreenshotHolder) {
mUiEventLogger = uiEventLogger;
mUiExecutor = mainExecutor;
mBackgroundExecutor = bgExecutor;
@@ -116,7 +114,6 @@
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate(savedInstanceState = " + savedInstanceState + ")");
super.onCreate(savedInstanceState);
- postponeEnterTransition();
setContentView(R.layout.long_screenshot);
mPreview = requireViewById(R.id.preview);
@@ -192,7 +189,6 @@
mLongScreenshot = longScreenshot;
Drawable drawable = mLongScreenshot.getDrawable();
mPreview.setImageDrawable(drawable);
- mCropView.setVisibility(View.VISIBLE);
mMagnifierView.setDrawable(mLongScreenshot.getDrawable(),
mLongScreenshot.getWidth(), mLongScreenshot.getHeight());
// Original boundaries go from the image tile set's y=0 to y=pageSize, so
@@ -211,30 +207,29 @@
public boolean onPreDraw() {
mEnterTransitionView.getViewTreeObserver().removeOnPreDrawListener(this);
updateImageDimensions();
- startPostponedEnterTransition();
- if (isActivityTransitionRunning()) {
- getWindow().getSharedElementEnterTransition().addListener(
- new TransitionListenerAdapter() {
- @Override
- public void onTransitionEnd(Transition transition) {
- super.onTransitionEnd(transition);
- mPreview.animate().alpha(1f);
- mCropView.animateBoundaryTo(
- CropView.CropBoundary.TOP, topFraction);
- mCropView.animateBoundaryTo(
- CropView.CropBoundary.BOTTOM, bottomFraction);
- setButtonsEnabled(true);
- mEnterTransitionView.setVisibility(View.GONE);
- }
+ mEnterTransitionView.post(() -> {
+ Rect dest = new Rect();
+ mEnterTransitionView.getBoundsOnScreen(dest);
+ mLongScreenshotHolder.takeTransitionDestinationCallback()
+ .setTransitionDestination(dest, () -> {
+ mPreview.animate().alpha(1f);
+ mCropView.setBoundaryPosition(
+ CropView.CropBoundary.TOP, topFraction);
+ mCropView.setBoundaryPosition(
+ CropView.CropBoundary.BOTTOM, bottomFraction);
+ mCropView.animateEntrance();
+ mCropView.setVisibility(View.VISIBLE);
+ setButtonsEnabled(true);
+ mEnterTransitionView.setVisibility(View.GONE);
});
- }
+ });
return true;
}
});
// Immediately export to temp image file for saved state
- mCacheSaveFuture = mImageExporter.exportAsTempFile(mBackgroundExecutor,
- mLongScreenshot.toBitmap());
+ mCacheSaveFuture = mImageExporter.exportToRawFile(mBackgroundExecutor,
+ mLongScreenshot.toBitmap(), new File(getCacheDir(), "long_screenshot_cache.png"));
mCacheSaveFuture.addListener(() -> {
try {
// Get the temp file path to persist, used in onSavedInstanceState
@@ -250,6 +245,7 @@
Log.d(TAG, "onCachedImageLoaded(imageResult=" + imageResult + ")");
BitmapDrawable drawable = new BitmapDrawable(getResources(), imageResult.bitmap);
mPreview.setImageDrawable(drawable);
+ mPreview.setAlpha(1f);
mMagnifierView.setDrawable(drawable, imageResult.bitmap.getWidth(),
imageResult.bitmap.getHeight());
mCropView.setVisibility(View.VISIBLE);
@@ -476,19 +472,21 @@
params.height = boundaries.height();
mTransitionView.setLayoutParams(params);
- ConstraintLayout.LayoutParams enterTransitionParams =
- (ConstraintLayout.LayoutParams) mEnterTransitionView.getLayoutParams();
- float topFraction = Math.max(0,
- -mLongScreenshot.getTop() / (float) mLongScreenshot.getHeight());
- enterTransitionParams.width = (int) (scale * drawable.getIntrinsicWidth());
- enterTransitionParams.height = (int) (scale * mLongScreenshot.getPageHeight());
- mEnterTransitionView.setLayoutParams(enterTransitionParams);
+ if (mLongScreenshot != null) {
+ ConstraintLayout.LayoutParams enterTransitionParams =
+ (ConstraintLayout.LayoutParams) mEnterTransitionView.getLayoutParams();
+ float topFraction = Math.max(0,
+ -mLongScreenshot.getTop() / (float) mLongScreenshot.getHeight());
+ enterTransitionParams.width = (int) (scale * drawable.getIntrinsicWidth());
+ enterTransitionParams.height = (int) (scale * mLongScreenshot.getPageHeight());
+ mEnterTransitionView.setLayoutParams(enterTransitionParams);
- Matrix matrix = new Matrix();
- matrix.setScale(scale, scale);
- matrix.postTranslate(0, -scale * drawable.getIntrinsicHeight() * topFraction);
- mEnterTransitionView.setImageMatrix(matrix);
- mEnterTransitionView.setTranslationY(
- topFraction * previewHeight + mPreview.getPaddingTop() + extraPadding);
+ Matrix matrix = new Matrix();
+ matrix.setScale(scale, scale);
+ matrix.postTranslate(0, -scale * drawable.getIntrinsicHeight() * topFraction);
+ mEnterTransitionView.setImageMatrix(matrix);
+ mEnterTransitionView.setTranslationY(
+ topFraction * previewHeight + mPreview.getPaddingTop() + extraPadding);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotHolder.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java
similarity index 63%
rename from packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotHolder.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java
index 39c6f07..f549faf 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java
@@ -23,16 +23,19 @@
import javax.inject.Inject;
/**
- * LongScreenshotHolder holds on to 1 LongScreenshot reference to facilitate indirect in-process
- * passing.
+ * LongScreenshotData holds on to 1 LongScreenshot reference and 1 TransitionDestination
+ * reference, to facilitate indirect in-process passing.
*/
@SysUISingleton
-public class LongScreenshotHolder {
+public class LongScreenshotData {
private final AtomicReference<ScrollCaptureController.LongScreenshot> mLongScreenshot;
+ private final AtomicReference<ScreenshotController.TransitionDestination>
+ mTransitionDestinationCallback;
@Inject
- public LongScreenshotHolder() {
+ public LongScreenshotData() {
mLongScreenshot = new AtomicReference<>();
+ mTransitionDestinationCallback = new AtomicReference<>();
}
/**
@@ -56,4 +59,18 @@
return mLongScreenshot.getAndSet(null);
}
+ /**
+ * Set the holder's TransitionDestination callback.
+ */
+ public void setTransitionDestinationCallback(
+ ScreenshotController.TransitionDestination destination) {
+ mTransitionDestinationCallback.set(destination);
+ }
+
+ /**
+ * Return the current TransitionDestination callback and clear.
+ */
+ public ScreenshotController.TransitionDestination takeTransitionDestinationCallback() {
+ return mTransitionDestinationCallback.getAndSet(null);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 5efa1b2..24cca91 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -64,6 +64,7 @@
import android.view.IRemoteAnimationRunner;
import android.view.KeyEvent;
import android.view.LayoutInflater;
+import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.ScrollCaptureResponse;
import android.view.SurfaceControl;
@@ -72,6 +73,7 @@
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
@@ -203,6 +205,15 @@
void onActionsReady(ScreenshotController.QuickShareData quickShareData);
}
+ interface TransitionDestination {
+ /**
+ * Allows the long screenshot activity to call back with a destination location (the bounds
+ * on screen of the destination for the transitioning view) and a Runnable to be run once
+ * the transition animation is complete.
+ */
+ void setTransitionDestination(Rect transitionDestination, Runnable onTransitionEnd);
+ }
+
// These strings are used for communicating the action invoked to
// ScreenshotNotificationSmartActionsProvider.
static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
@@ -241,7 +252,7 @@
private final PhoneWindow mWindow;
private final DisplayManager mDisplayManager;
private final ScrollCaptureController mScrollCaptureController;
- private final LongScreenshotHolder mLongScreenshotHolder;
+ private final LongScreenshotData mLongScreenshotHolder;
private ScreenshotView mScreenshotView;
private Bitmap mScreenBitmap;
@@ -286,7 +297,7 @@
ImageExporter imageExporter,
@Main Executor mainExecutor,
ScrollCaptureController scrollCaptureController,
- LongScreenshotHolder longScreenshotHolder) {
+ LongScreenshotData longScreenshotHolder) {
mScreenshotSmartActions = screenshotSmartActions;
mNotificationsController = screenshotNotificationsController;
mScrollCaptureClient = scrollCaptureClient;
@@ -475,9 +486,24 @@
private void takeScreenshotInternal(Consumer<Uri> finisher, Rect crop) {
// copy the input Rect, since SurfaceControl.screenshot can mutate it
Rect screenRect = new Rect(crop);
+ Bitmap screenshot = captureScreenshot(crop);
+
+ if (screenshot == null) {
+ Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");
+ mNotificationsController.notifyScreenshotError(
+ R.string.screenshot_failed_to_capture_text);
+ if (mCurrentRequestCallback != null) {
+ mCurrentRequestCallback.reportError();
+ }
+ return;
+ }
+
+ saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, true);
+ }
+
+ private Bitmap captureScreenshot(Rect crop) {
int width = crop.width();
int height = crop.height();
-
Bitmap screenshot = null;
final Display display = getDefaultDisplay();
final DisplayAddress address = display.getAddress();
@@ -498,18 +524,7 @@
SurfaceControl.captureDisplay(captureArgs);
screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
}
-
- if (screenshot == null) {
- Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_capture_text);
- if (mCurrentRequestCallback != null) {
- mCurrentRequestCallback.reportError();
- }
- return;
- }
-
- saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, true);
+ return screenshot;
}
private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
@@ -573,6 +588,10 @@
mScreenshotView.updateDisplayCutoutMargins(
mWindowManager.getCurrentWindowMetrics().getWindowInsets()
.getDisplayCutout());
+ // screenshot animation calculations won't be valid anymore, so just end
+ if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
+ mScreenshotAnimation.end();
+ }
}
});
});
@@ -629,33 +648,56 @@
final ScrollCaptureResponse response = mLastScrollCaptureResponse;
mScreenshotView.showScrollChip(/* onClick */ () -> {
- mScreenshotView.prepareScrollingTransition(response, mScreenBitmap);
- // Clear the reference to prevent close() in dismissScreenshot
- mLastScrollCaptureResponse = null;
- final ListenableFuture<ScrollCaptureController.LongScreenshot> future =
- mScrollCaptureController.run(response);
- future.addListener(() -> {
- ScrollCaptureController.LongScreenshot longScreenshot;
- try {
- longScreenshot = future.get();
- } catch (CancellationException | InterruptedException | ExecutionException e) {
- Log.e(TAG, "Exception", e);
- return;
- }
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ getDefaultDisplay().getRealMetrics(displayMetrics);
+ Bitmap newScreenshot = captureScreenshot(
+ new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels));
- mLongScreenshotHolder.setLongScreenshot(longScreenshot);
+ mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot);
+ // delay starting scroll capture to make sure the scrim is up before the app moves
+ mScreenshotView.post(() -> {
+ // Clear the reference to prevent close() in dismissScreenshot
+ mLastScrollCaptureResponse = null;
+ final ListenableFuture<ScrollCaptureController.LongScreenshot> future =
+ mScrollCaptureController.run(response);
+ future.addListener(() -> {
+ ScrollCaptureController.LongScreenshot longScreenshot;
+ try {
+ longScreenshot = future.get();
+ } catch (CancellationException
+ | InterruptedException
+ | ExecutionException e) {
+ Log.e(TAG, "Exception", e);
+ return;
+ }
- final Intent intent = new Intent(mContext, LongScreenshotActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mLongScreenshotHolder.setLongScreenshot(longScreenshot);
+ mLongScreenshotHolder.setTransitionDestinationCallback(
+ (transitionDestination, onTransitionEnd) ->
+ mScreenshotView.startLongScreenshotTransition(
+ transitionDestination, onTransitionEnd,
+ longScreenshot));
- Pair<ActivityOptions, ExitTransitionCoordinator> transition =
- ActivityOptions.startSharedElementAnimation(
- mWindow, new ScreenshotExitTransitionCallbacks(), null,
- Pair.create(mScreenshotView.getScrollablePreview(),
- ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME));
- transition.second.startExit();
- mContext.startActivity(intent, transition.first.toBundle());
- }, mMainExecutor);
+ final Intent intent = new Intent(mContext, LongScreenshotActivity.class);
+ intent.setFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+ Pair<ActivityOptions, ExitTransitionCoordinator> transition =
+ ActivityOptions.startSharedElementAnimation(mWindow,
+ new ScreenshotExitTransitionCallbacksSupplier(false).get(),
+ null);
+ transition.second.startExit();
+ mContext.startActivity(intent, transition.first.toBundle());
+ RemoteAnimationAdapter runner = new RemoteAnimationAdapter(
+ SCREENSHOT_REMOTE_RUNNER, 0, 0);
+ try {
+ WindowManagerGlobal.getWindowManagerService()
+ .overridePendingAppTransitionRemote(runner, DEFAULT_DISPLAY);
+ } catch (Exception e) {
+ Log.e(TAG, "Error overriding screenshot app transition", e);
+ }
+ }, mMainExecutor);
+ });
});
} catch (CancellationException e) {
// Ignore
@@ -879,8 +921,8 @@
return () -> {
Pair<ActivityOptions, ExitTransitionCoordinator> transition =
ActivityOptions.startSharedElementAnimation(
- mWindow, new ScreenshotExitTransitionCallbacks(), null,
- Pair.create(mScreenshotView.getScreenshotPreview(),
+ mWindow, new ScreenshotExitTransitionCallbacksSupplier(true).get(),
+ null, Pair.create(mScreenshotView.getScreenshotPreview(),
ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME));
transition.second.startExit();
@@ -966,19 +1008,33 @@
return matchWithinTolerance;
}
- private class ScreenshotExitTransitionCallbacks implements ExitTransitionCallbacks {
- @Override
- public boolean isReturnTransitionAllowed() {
- return false;
+ private class ScreenshotExitTransitionCallbacksSupplier implements
+ Supplier<ExitTransitionCallbacks> {
+ final boolean mDismissOnHideSharedElements;
+
+ ScreenshotExitTransitionCallbacksSupplier(boolean dismissOnHideSharedElements) {
+ mDismissOnHideSharedElements = dismissOnHideSharedElements;
}
@Override
- public void hideSharedElements() {
- finishDismiss();
- }
+ public ExitTransitionCallbacks get() {
+ return new ExitTransitionCallbacks() {
+ @Override
+ public boolean isReturnTransitionAllowed() {
+ return false;
+ }
- @Override
- public void onFinish() {
+ @Override
+ public void hideSharedElements() {
+ if (mDismissOnHideSharedElements) {
+ finishDismiss();
+ }
+ }
+
+ @Override
+ public void onFinish() {
+ }
+ };
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 2a22179..9fe8c84 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -36,8 +36,10 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.BlendMode;
import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Matrix;
@@ -257,10 +259,10 @@
@Override // ViewTreeObserver.OnComputeInternalInsetsListener
public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- inoutInfo.touchableRegion.set(getTouchRegion());
+ inoutInfo.touchableRegion.set(getTouchRegion(true));
}
- private Region getTouchRegion() {
+ private Region getTouchRegion(boolean includeScrim) {
Region touchRegion = new Region();
final Rect tmpRect = new Rect();
@@ -273,6 +275,11 @@
mDismissButton.getBoundsOnScreen(tmpRect);
touchRegion.op(tmpRect, Region.Op.UNION);
+ if (includeScrim && mScrollingScrim.getVisibility() == View.VISIBLE) {
+ mScrollingScrim.getBoundsOnScreen(tmpRect);
+ touchRegion.op(tmpRect, Region.Op.UNION);
+ }
+
if (QuickStepContract.isGesturalMode(mNavMode)) {
final WindowManager wm = mContext.getSystemService(WindowManager.class);
final WindowMetrics windowMetrics = wm.getCurrentWindowMetrics();
@@ -296,7 +303,7 @@
if (ev instanceof MotionEvent) {
MotionEvent event = (MotionEvent) ev;
if (event.getActionMasked() == MotionEvent.ACTION_DOWN
- && !getTouchRegion().contains(
+ && !getTouchRegion(false).contains(
(int) event.getRawX(), (int) event.getRawY())) {
mCallbacks.onTouchOutside();
}
@@ -313,6 +320,10 @@
@Override // ViewGroup
public boolean onInterceptTouchEvent(MotionEvent ev) {
+ // scrolling scrim should not be swipeable; return early if we're on the scrim
+ if (!getTouchRegion(false).contains((int) ev.getRawX(), (int) ev.getRawY())) {
+ return false;
+ }
// always pass through the down event so the swipe handler knows the initial state
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
mSwipeDismissHandler.onTouch(this, ev);
@@ -374,10 +385,6 @@
return mScreenshotPreview;
}
- View getScrollablePreview() {
- return mScrollablePreview;
- }
-
/**
* Set up the logger and callback on dismissal.
*
@@ -453,14 +460,6 @@
mCornerSizeX / (mOrientationPortrait ? bounds.width() : bounds.height());
final float currentScale = 1 / cornerScale;
- mScreenshotPreview.setScaleX(currentScale);
- mScreenshotPreview.setScaleY(currentScale);
-
- if (mAccessibilityManager.isEnabled()) {
- mDismissButton.setAlpha(0);
- mDismissButton.setVisibility(View.VISIBLE);
- }
-
AnimatorSet dropInAnimation = new AnimatorSet();
ValueAnimator flashInAnimator = ValueAnimator.ofFloat(0, 1);
flashInAnimator.setDuration(SCREENSHOT_FLASH_IN_DURATION_MS);
@@ -479,6 +478,11 @@
final PointF finalPos = new PointF(targetPosition.exactCenterX(),
targetPosition.exactCenterY());
+ // Shift to screen coordinates so that the animation runs on top of the entire screen,
+ // including e.g. bars covering the display cutout.
+ int[] locInScreen = mScreenshotPreview.getLocationOnScreen();
+ startPos.offset(targetPosition.left - locInScreen[0], targetPosition.top - locInScreen[1]);
+
if (DEBUG_ANIM) {
Log.d(TAG, "toCorner: startPos=" + startPos);
Log.d(TAG, "toCorner: finalPos=" + finalPos);
@@ -486,6 +490,20 @@
ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1);
toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS);
+
+ toCorner.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mScreenshotPreview.setScaleX(currentScale);
+ mScreenshotPreview.setScaleY(currentScale);
+ mScreenshotPreview.setVisibility(View.VISIBLE);
+ if (mAccessibilityManager.isEnabled()) {
+ mDismissButton.setAlpha(0);
+ mDismissButton.setVisibility(View.VISIBLE);
+ }
+ }
+ });
+
float xPositionPct =
SCREENSHOT_TO_CORNER_X_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
float dismissPct =
@@ -529,13 +547,6 @@
}
});
- toCorner.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mScreenshotPreview.setVisibility(View.VISIBLE);
- }
- });
-
mScreenshotFlash.setAlpha(0f);
mScreenshotFlash.setVisibility(View.VISIBLE);
@@ -759,7 +770,54 @@
return r;
}
- void prepareScrollingTransition(ScrollCaptureResponse response, Bitmap screenBitmap) {
+ void startLongScreenshotTransition(Rect destination, Runnable onTransitionEnd,
+ ScrollCaptureController.LongScreenshot longScreenshot) {
+ mScrollablePreview.setImageBitmap(longScreenshot.toBitmap());
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+ float startX = mScrollablePreview.getX();
+ float startY = mScrollablePreview.getY();
+ int[] locInScreen = mScrollablePreview.getLocationOnScreen();
+ destination.offset((int) startX - locInScreen[0], (int) startY - locInScreen[1]);
+ mScrollablePreview.setPivotX(0);
+ mScrollablePreview.setPivotY(0);
+ mScrollablePreview.setAlpha(1f);
+ float currentScale = mScrollablePreview.getWidth() / (float) longScreenshot.getWidth();
+ Matrix matrix = new Matrix();
+ matrix.setScale(currentScale, currentScale);
+ matrix.postTranslate(
+ longScreenshot.getLeft() * currentScale, longScreenshot.getTop() * currentScale);
+ mScrollablePreview.setImageMatrix(matrix);
+ float destinationScale = destination.width() / (float) mScrollablePreview.getWidth();
+ anim.addUpdateListener(animation -> {
+ float t = animation.getAnimatedFraction();
+ mScrollingScrim.setAlpha(1 - t);
+ float currScale = MathUtils.lerp(1, destinationScale, t);
+ mScrollablePreview.setScaleX(currScale);
+ mScrollablePreview.setScaleY(currScale);
+ mScrollablePreview.setX(MathUtils.lerp(startX, destination.left, t));
+ mScrollablePreview.setY(MathUtils.lerp(startY, destination.top, t));
+ });
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ onTransitionEnd.run();
+ mScrollablePreview.animate().alpha(0).setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mCallbacks.onDismiss();
+ }
+ });
+ }
+ });
+ anim.start();
+ }
+
+ void prepareScrollingTransition(ScrollCaptureResponse response, Bitmap screenBitmap,
+ Bitmap newBitmap) {
+ mScrollingScrim.setImageBitmap(newBitmap);
+ mScrollingScrim.setVisibility(View.VISIBLE);
Rect scrollableArea = scrollableAreaOnScreen(response);
float scale = mCornerSizeX
/ (mOrientationPortrait ? screenBitmap.getWidth() : screenBitmap.getHeight());
@@ -770,17 +828,27 @@
params.height = (int) (scale * scrollableArea.height());
Matrix matrix = new Matrix();
matrix.setScale(scale, scale);
- matrix.postTranslate(0, -scrollableArea.top * scale);
+ matrix.postTranslate(-scrollableArea.left * scale, -scrollableArea.top * scale);
mScrollablePreview.setTranslationX(scale * scrollableArea.left);
mScrollablePreview.setTranslationY(scale * scrollableArea.top);
mScrollablePreview.setImageMatrix(matrix);
- mScrollingScrim.setImageBitmap(screenBitmap);
- mScrollingScrim.setVisibility(View.VISIBLE);
mScrollablePreview.setImageBitmap(screenBitmap);
mScrollablePreview.setVisibility(View.VISIBLE);
- createScreenshotFadeDismissAnimation(true).start();
+ mDismissButton.setVisibility(View.GONE);
+ mActionsContainer.setVisibility(View.GONE);
+ mBackgroundProtection.setVisibility(View.GONE);
+ // set these invisible, but not gone, so that the views are laid out correctly
+ mActionsContainerBackground.setVisibility(View.INVISIBLE);
+ mScreenshotPreviewBorder.setVisibility(View.INVISIBLE);
+ mScreenshotPreview.setVisibility(View.INVISIBLE);
+ mScrollingScrim.setImageTintBlendMode(BlendMode.SRC_ATOP);
+ ValueAnimator anim = ValueAnimator.ofFloat(0, .3f);
+ anim.addUpdateListener(animation -> mScrollingScrim.setImageTintList(
+ ColorStateList.valueOf(Color.argb((float) animation.getAnimatedValue(), 0, 0, 0))));
+ anim.setDuration(200);
+ anim.start();
}
boolean isDismissing() {
@@ -796,10 +864,6 @@
}
private void animateDismissal(Animator dismissAnimation) {
- if (DEBUG_WINDOW) {
- Log.d(TAG, "removing OnComputeInternalInsetsListener");
- }
- getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
mDismissAnimation = dismissAnimation;
mDismissAnimation.addListener(new AnimatorListenerAdapter() {
private boolean mCancelled = false;
@@ -861,6 +925,7 @@
mContext.getResources().getString(R.string.screenshot_preview_description));
mScreenshotPreview.setOnClickListener(null);
mShareChip.setOnClickListener(null);
+ mScrollingScrim.setVisibility(View.GONE);
mEditChip.setOnClickListener(null);
mShareChip.setIsPending(false);
mEditChip.setIsPending(false);
@@ -883,7 +948,7 @@
transition.action.actionIntent.send();
// fade out non-preview UI
- createScreenshotFadeDismissAnimation(false).start();
+ createScreenshotFadeDismissAnimation().start();
} catch (PendingIntent.CanceledException e) {
mPendingSharedTransition = false;
if (transition.onCancelRunnable != null) {
@@ -921,7 +986,7 @@
return animSet;
}
- ValueAnimator createScreenshotFadeDismissAnimation(boolean fadePreview) {
+ ValueAnimator createScreenshotFadeDismissAnimation() {
ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1);
alphaAnim.addUpdateListener(animation -> {
float alpha = 1 - animation.getAnimatedFraction();
@@ -930,9 +995,6 @@
mActionsContainer.setAlpha(alpha);
mBackgroundProtection.setAlpha(alpha);
mScreenshotPreviewBorder.setAlpha(alpha);
- if (fadePreview) {
- mScreenshotPreview.setAlpha(alpha);
- }
});
alphaAnim.setDuration(600);
return alphaAnim;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
index 94e3149..ce6e469 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
@@ -56,6 +56,7 @@
public class ScrollCaptureClient {
private static final int TILE_SIZE_PX_MAX = 4 * (1024 * 1024);
private static final int TILES_PER_PAGE = 2; // increase once b/174571735 is addressed
+ private static final int MAX_TILES = 30;
@VisibleForTesting
static final int MATCH_ANY_TASK = ActivityTaskManager.INVALID_TASK_ID;
@@ -83,11 +84,12 @@
int getMaxTiles();
/**
- * @return the maximum combined capture height for this session, in pixels.
+ * Target pixel height for acquisition this session. Session may yield more or less data
+ * than this, but acquiring this height is considered sufficient for completion.
+ *
+ * @return target height in pixels.
*/
- default int getMaxHeight() {
- return getMaxTiles() * getTileHeight();
- }
+ int getTargetHeight();
/**
* @return the height of each image tile
@@ -234,11 +236,11 @@
private final int mTileWidth;
private Rect mRequestRect;
private boolean mStarted;
+ private final int mTargetHeight;
private ICancellationSignal mCancellationSignal;
private final Rect mWindowBounds;
private final Rect mBoundsInWindow;
- private final int mMaxTiles;
private Completer<Session> mStartCompleter;
private Completer<CaptureResult> mTileRequestCompleter;
@@ -256,7 +258,7 @@
mTileWidth = mBoundsInWindow.width();
mTileHeight = pxPerTile / mBoundsInWindow.width();
- mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE);
+ mTargetHeight = (int) (mBoundsInWindow.height() * maxPages);
if (DEBUG_SCROLL) {
Log.d(TAG, "boundsInWindow: " + mBoundsInWindow);
@@ -285,7 +287,7 @@
private void start(Completer<Session> completer) {
mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888,
- mMaxTiles, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
+ MAX_TILES, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
mStartCompleter = completer;
try {
mCancellationSignal = mConnection.startCapture(mReader.getSurface(), this);
@@ -410,8 +412,13 @@
}
@Override
+ public int getTargetHeight() {
+ return mTargetHeight;
+ }
+
+ @Override
public int getMaxTiles() {
- return mMaxTiles;
+ return MAX_TILES;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index bbcfdbd..4c1f6a1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -199,22 +199,20 @@
Log.d(TAG, "onCaptureResult: " + result + " scrolling " + (mScrollingUp ? "UP" : "DOWN")
+ " finish on boundary: " + mFinishOnBoundary);
boolean emptyResult = result.captured.height() == 0;
- boolean partialResult = !emptyResult
- && result.captured.height() < result.requested.height();
- boolean finish = false;
if (emptyResult) {
// Potentially reached a vertical boundary. Extend in the other direction.
if (mFinishOnBoundary) {
- Log.d(TAG, "Partial/empty: finished!");
- finish = true;
+ Log.d(TAG, "Empty: finished!");
+ finishCapture();
+ return;
} else {
// We hit a boundary, clear the tiles, capture everything in the opposite direction,
// then finish.
mImageTileSet.clear();
mFinishOnBoundary = true;
mScrollingUp = !mScrollingUp;
- Log.d(TAG, "Partial/empty: cleared, switch direction to finish");
+ Log.d(TAG, "Empty: cleared, switch direction to finish");
}
} else {
// Got a non-empty result, but may already have enough bitmap data now
@@ -223,12 +221,14 @@
Log.d(TAG, "Hit max tiles: finished");
// If we ever hit the max tiles, we've got enough bitmap data to finish
// (even if we weren't sure we'd finish on this pass).
- finish = true;
+ finishCapture();
+ return;
} else {
if (mScrollingUp && !mFinishOnBoundary) {
// During the initial scroll up, we only want to acquire the portion described
// by IDEAL_PORTION_ABOVE.
- if (expectedTiles >= mSession.getMaxTiles() * IDEAL_PORTION_ABOVE) {
+ if (mImageTileSet.getHeight() + result.captured.height()
+ >= mSession.getTargetHeight() * IDEAL_PORTION_ABOVE) {
Log.d(TAG, "Hit ideal portion above: clear and switch direction");
// We got enough above the start point, now see how far down it can go.
mImageTileSet.clear();
@@ -246,15 +246,15 @@
+ " - " + mImageTileSet.getRight() + "," + mImageTileSet.getBottom()
+ " (" + mImageTileSet.getWidth() + "x" + mImageTileSet.getHeight() + ")");
-
- // Stop when "too tall"
- if (mImageTileSet.getHeight() > MAX_HEIGHT) {
- Log.d(TAG, "Max height reached.");
- finish = true;
+ Rect gapBounds = mImageTileSet.getGaps();
+ if (!gapBounds.isEmpty()) {
+ Log.d(TAG, "Found gaps in tileset: " + gapBounds + ", requesting " + gapBounds.top);
+ requestNextTile(gapBounds.top);
+ return;
}
- if (finish) {
- Log.d(TAG, "Stop.");
+ if (mImageTileSet.getHeight() >= mSession.getTargetHeight()) {
+ Log.d(TAG, "Target height reached.");
finishCapture();
return;
}
@@ -268,8 +268,8 @@
: result.requested.bottom;
} else {
nextTop = (mScrollingUp)
- ? result.captured.top - mSession.getTileHeight()
- : result.captured.bottom;
+ ? mImageTileSet.getTop() - mSession.getTileHeight()
+ : mImageTileSet.getBottom();
}
requestNextTile(nextTop);
}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index 06c1c6f..a79316d 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -32,6 +32,7 @@
import com.android.internal.app.AlertActivity
import com.android.internal.widget.DialogTitle
import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -46,13 +47,15 @@
class SensorUseStartedActivity @Inject constructor(
private val sensorPrivacyController: IndividualSensorPrivacyController,
private val keyguardStateController: KeyguardStateController,
- private val keyguardDismissUtil: KeyguardDismissUtil
+ private val keyguardDismissUtil: KeyguardDismissUtil,
+ @Background private val bgHandler: Handler
) : AlertActivity(), DialogInterface.OnClickListener {
companion object {
private val LOG_TAG = SensorUseStartedActivity::class.java.simpleName
private const val SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS = 2000L
+ private const val UNLOCK_DELAY_MILLIS = 200L
private const val CAMERA = SensorPrivacyManager.Sensors.CAMERA
private const val MICROPHONE = SensorPrivacyManager.Sensors.MICROPHONE
@@ -179,9 +182,12 @@
BUTTON_POSITIVE -> {
if (keyguardStateController.isMethodSecure && keyguardStateController.isShowing) {
keyguardDismissUtil.executeWhenUnlocked({
- disableSensorPrivacy()
+ bgHandler.postDelayed({
+ disableSensorPrivacy()
+ }, UNLOCK_DELAY_MILLIS)
+
false
- }, false, false)
+ }, false, true)
} else {
disableSensorPrivacy()
}
@@ -201,7 +207,7 @@
sensorPrivacyController
.suppressSensorPrivacyReminders(sensorUsePackageName, false)
} else {
- Handler(mainLooper).postDelayed({
+ bgHandler.postDelayed({
sensorPrivacyController
.suppressSensorPrivacyReminders(sensorUsePackageName, false)
}, SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS)
@@ -218,7 +224,12 @@
}
private fun disableSensorPrivacy() {
- sensorPrivacyController.setSensorBlocked(sensor, false)
+ if (sensor == ALL_SENSORS) {
+ sensorPrivacyController.setSensorBlocked(MICROPHONE, false)
+ sensorPrivacyController.setSensorBlocked(CAMERA, false)
+ } else {
+ sensorPrivacyController.setSensorBlocked(sensor, false)
+ }
unsuppressImmediately = true
setResult(RESULT_OK)
}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java b/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java
new file mode 100644
index 0000000..9d101ef
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.sensorprivacy.television;
+
+import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
+import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
+
+import android.hardware.SensorPrivacyManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+import com.android.systemui.tv.TvBottomSheetActivity;
+
+import javax.inject.Inject;
+
+/**
+ * Bottom sheet that is shown when the camera/mic sensors are blocked by the global toggle and
+ * allows the user to re-enable them.
+ */
+public class TvUnblockSensorActivity extends TvBottomSheetActivity {
+
+ private static final String TAG = TvUnblockSensorActivity.class.getSimpleName();
+
+ private static final int ALL_SENSORS = Integer.MAX_VALUE;
+ private int mSensor = -1;
+
+ private final IndividualSensorPrivacyController mSensorPrivacyController;
+ private IndividualSensorPrivacyController.Callback mSensorPrivacyCallback;
+
+ @Inject
+ public TvUnblockSensorActivity(
+ IndividualSensorPrivacyController individualSensorPrivacyController) {
+ mSensorPrivacyController = individualSensorPrivacyController;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ boolean allSensors = getIntent().getBooleanExtra(SensorPrivacyManager.EXTRA_ALL_SENSORS,
+ false);
+ if (allSensors) {
+ mSensor = ALL_SENSORS;
+ } else {
+ mSensor = getIntent().getIntExtra(SensorPrivacyManager.EXTRA_SENSOR, -1);
+ }
+
+ if (mSensor == -1) {
+ Log.v(TAG, "Invalid extras");
+ finish();
+ return;
+ }
+
+ mSensorPrivacyCallback = (sensor, blocked) -> {
+ if (mSensor == ALL_SENSORS) {
+ if (!mSensorPrivacyController.isSensorBlocked(CAMERA)
+ && !mSensorPrivacyController.isSensorBlocked(MICROPHONE)) {
+ finish();
+ }
+ } else if (this.mSensor == sensor && !blocked) {
+ finish();
+ }
+ };
+
+ initUI();
+ }
+
+ private void initUI() {
+ TextView title = findViewById(R.id.bottom_sheet_title);
+ TextView content = findViewById(R.id.bottom_sheet_body);
+ ImageView icon = findViewById(R.id.bottom_sheet_icon);
+ // mic icon if both icons are shown
+ ImageView secondIcon = findViewById(R.id.bottom_sheet_second_icon);
+ Button unblockButton = findViewById(R.id.bottom_sheet_positive_button);
+ Button cancelButton = findViewById(R.id.bottom_sheet_negative_button);
+
+ switch (mSensor) {
+ case MICROPHONE:
+ title.setText(R.string.sensor_privacy_start_use_mic_dialog_title);
+ content.setText(R.string.sensor_privacy_start_use_mic_dialog_content);
+ icon.setImageResource(com.android.internal.R.drawable.perm_group_microphone);
+ secondIcon.setVisibility(View.GONE);
+ break;
+ case CAMERA:
+ title.setText(R.string.sensor_privacy_start_use_camera_dialog_title);
+ content.setText(R.string.sensor_privacy_start_use_camera_dialog_content);
+ icon.setImageResource(com.android.internal.R.drawable.perm_group_camera);
+ secondIcon.setVisibility(View.GONE);
+ break;
+ case ALL_SENSORS:
+ default:
+ title.setText(R.string.sensor_privacy_start_use_mic_camera_dialog_title);
+ content.setText(R.string.sensor_privacy_start_use_mic_camera_dialog_content);
+ icon.setImageResource(com.android.internal.R.drawable.perm_group_camera);
+ secondIcon.setImageResource(com.android.internal.R.drawable.perm_group_microphone);
+ break;
+ }
+ unblockButton.setText(
+ com.android.internal.R.string.sensor_privacy_start_use_dialog_turn_on_button);
+ unblockButton.setOnClickListener(v -> {
+ if (mSensor == ALL_SENSORS) {
+ mSensorPrivacyController.setSensorBlocked(CAMERA, false);
+ mSensorPrivacyController.setSensorBlocked(MICROPHONE, false);
+ } else {
+ mSensorPrivacyController.setSensorBlocked(mSensor, false);
+ }
+ });
+
+ cancelButton.setText(android.R.string.cancel);
+ cancelButton.setOnClickListener(v -> finish());
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mSensorPrivacyController.addCallback(mSensorPrivacyCallback);
+ }
+
+ @Override
+ public void onPause() {
+ mSensorPrivacyController.removeCallback(mSensorPrivacyCallback);
+ super.onPause();
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
index 8dd6c89..15aa2b7 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
@@ -180,6 +180,8 @@
* Sets the scale for the progress bar (for brightness_progress_drawable.xml)
*
* This will only scale the thick progress bar and not the icon inside
+ *
+ * Used in {@link com.android.systemui.qs.QSAnimator}.
*/
public void setSliderScaleY(float scale) {
if (scale != mScale) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index ce796d9..aafeabc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -20,8 +20,10 @@
import android.app.ActivityManager
import android.content.res.Resources
+import android.os.SystemProperties
import android.util.IndentingPrintWriter
import android.util.MathUtils
+import android.view.CrossWindowBlurListeners
import android.view.SurfaceControl
import android.view.ViewRootImpl
import androidx.annotation.VisibleForTesting
@@ -37,6 +39,7 @@
@SysUISingleton
open class BlurUtils @Inject constructor(
@Main private val resources: Resources,
+ private val crossWindowBlurListeners: CrossWindowBlurListeners,
dumpManager: DumpManager
) : Dumpable {
val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius)
@@ -97,7 +100,9 @@
* @return {@code true} when supported.
*/
open fun supportsBlursOnWindows(): Boolean {
- return CROSS_WINDOW_BLUR_SUPPORTED && ActivityManager.isHighEndGfx()
+ return CROSS_WINDOW_BLUR_SUPPORTED && ActivityManager.isHighEndGfx() &&
+ crossWindowBlurListeners.isCrossWindowBlurEnabled() &&
+ !SystemProperties.getBoolean("persist.sysui.disableBlur", false)
}
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index 7e67619..8e5d47f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -84,4 +84,12 @@
public boolean isSmartspaceDedupingEnabled() {
return isSmartspaceEnabled() && mFlagReader.isEnabled(R.bool.flag_smartspace_deduping);
}
+
+ public boolean isNewKeyguardSwipeAnimationEnabled() {
+ return mFlagReader.isEnabled(R.bool.flag_new_unlock_swipe_animation);
+ }
+
+ public boolean isSmartSpaceSharedElementTransitionEnabled() {
+ return mFlagReader.isEnabled(R.bool.flag_smartspace_shared_element_transition);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 91a0e6f..0bb702f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -112,6 +112,7 @@
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private ViewGroup mIndicationArea;
private KeyguardIndicationTextView mTopIndicationView;
+ private KeyguardIndicationTextView mLockScreenIndicationView;
private final IBatteryStats mBatteryInfo;
private final SettableWakeLock mWakeLock;
private final DockManager mDockManager;
@@ -208,17 +209,21 @@
mKeyguardUpdateMonitor.registerCallback(mTickReceiver);
mStatusBarStateController.addCallback(mStatusBarStateListener);
mKeyguardStateController.addCallback(this);
+
+ mStatusBarStateListener.onDozingChanged(mStatusBarStateController.isDozing());
}
public void setIndicationArea(ViewGroup indicationArea) {
mIndicationArea = indicationArea;
mTopIndicationView = indicationArea.findViewById(R.id.keyguard_indication_text);
+ mLockScreenIndicationView = indicationArea.findViewById(
+ R.id.keyguard_indication_text_bottom);
mInitialTextColorState = mTopIndicationView != null
? mTopIndicationView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
mRotateTextViewController = new KeyguardIndicationRotateTextViewController(
- indicationArea.findViewById(R.id.keyguard_indication_text_bottom),
- mExecutor,
- mStatusBarStateController);
+ mLockScreenIndicationView,
+ mExecutor,
+ mStatusBarStateController);
updateIndication(false /* animate */);
updateDisclosure();
if (mBroadcastReceiver == null) {
@@ -630,6 +635,7 @@
// should be shown based on user or device state
// AoD
if (mDozing) {
+ mLockScreenIndicationView.setVisibility(View.GONE);
mTopIndicationView.setVisibility(VISIBLE);
// When dozing we ignore any text color and use white instead, because
// colors can be hard to read in low brightness.
@@ -659,6 +665,8 @@
// LOCK SCREEN
mTopIndicationView.setVisibility(GONE);
+ mTopIndicationView.setText(null);
+ mLockScreenIndicationView.setVisibility(View.VISIBLE);
updateIndications(animate, KeyguardUpdateMonitor.getCurrentUser());
}
@@ -914,7 +922,8 @@
} else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.showBouncerMessage(errString, mInitialTextColorState);
} else if (mKeyguardUpdateMonitor.isScreenOn()) {
- showTransientIndication(errString);
+ showTransientIndication(errString, /* isError */ true,
+ /* hideOnScreenOff */ true);
// We want to keep this message around in case the screen was off
hideTransientIndicationDelayed(HIDE_DELAY_MS);
} else {
@@ -1032,9 +1041,8 @@
if (mHideTransientMessageOnScreenOff && mDozing) {
hideTransientIndication();
- } else {
- updateIndication(false);
}
+ updateIndication(false);
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 6a5f001..5d8bed5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -51,7 +51,7 @@
private const val OVAL_INITIAL_WIDTH_PERCENT = 0.5f
/** The initial top value of the light oval, in percent of scrim height. */
- private const val OVAL_INITIAL_TOP_PERCENT = 1.05f
+ private const val OVAL_INITIAL_TOP_PERCENT = 1.1f
/** The initial bottom value of the light oval, in percent of scrim height. */
private const val OVAL_INITIAL_BOTTOM_PERCENT = 1.2f
@@ -93,10 +93,10 @@
val endRadius: Float
) : LightRevealEffect {
override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {
- val interpolatedAmount = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(amount)
- val fadeAmount =
- LightRevealEffect.getPercentPastThreshold(interpolatedAmount, 0.75f)
- val radius = startRadius + ((endRadius - startRadius) * interpolatedAmount)
+ // reveal amount updates already have an interpolator, so we intentionally use the
+ // non-interpolated amount
+ val fadeAmount = LightRevealEffect.getPercentPastThreshold(amount, 0.5f)
+ val radius = startRadius + ((endRadius - startRadius) * amount)
scrim.revealGradientEndColorAlpha = 1f - fadeAmount
scrim.setRevealGradientBounds(
centerX - radius /* left */,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 4a4e990..6f4a73e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -18,6 +18,7 @@
import com.android.systemui.Gefingerpoken
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
+import com.android.systemui.biometrics.UdfpsKeyguardViewController
import com.android.systemui.classifier.Classifier
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
@@ -109,6 +110,11 @@
private var nextHideKeyguardNeedsNoAnimation = false
/**
+ * The udfpsKeyguardViewController if it exists.
+ */
+ var udfpsKeyguardViewController: UdfpsKeyguardViewController? = null
+
+ /**
* The touch helper responsible for the drag down animation.
*/
val touchHelper = DragDownHelper(falsingManager, falsingCollector, this, context)
@@ -291,6 +297,7 @@
// Fade out all content only visible on the lockscreen
notificationPanelController.setKeyguardOnlyContentAlpha(1.0f - scrimProgress)
depthController.transitionToFullShadeProgress = scrimProgress
+ udfpsKeyguardViewController?.setTransitionToFullShadeProgress(scrimProgress)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index a69b8d6..25cbdc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -245,7 +245,8 @@
mMediaDataManager.addListener(new MediaDataManager.Listener() {
@Override
public void onMediaDataLoaded(@NonNull String key,
- @Nullable String oldKey, @NonNull MediaData data, boolean immediately) {
+ @Nullable String oldKey, @NonNull MediaData data, boolean immediately,
+ boolean isSsReactivated) {
}
@Override
@@ -318,7 +319,8 @@
mMediaDataManager.addListener(new MediaDataManager.Listener() {
@Override
public void onMediaDataLoaded(@NonNull String key,
- @Nullable String oldKey, @NonNull MediaData data, boolean immediately) {
+ @Nullable String oldKey, @NonNull MediaData data, boolean immediately,
+ boolean isSsReactivated) {
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 8900551..1457012 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -93,8 +93,13 @@
var shadeAnimation = DepthAnimation()
@VisibleForTesting
- var globalActionsSpring = DepthAnimation()
- var showingHomeControls: Boolean = false
+ var brightnessMirrorSpring = DepthAnimation()
+ var brightnessMirrorVisible: Boolean = false
+ set(value) {
+ field = value
+ brightnessMirrorSpring.animateTo(if (value) blurUtils.blurRadiusOfRatio(1f)
+ else 0)
+ }
var qsPanelExpansion = 0f
set(value) {
@@ -117,7 +122,7 @@
* When launching an app from the shade, the animations progress should affect how blurry the
* shade is, overriding the expansion amount.
*/
- var ignoreShadeBlurUntilHidden: Boolean = false
+ var blursDisabledForAppLaunch: Boolean = false
set(value) {
if (field == value) {
return
@@ -128,6 +133,10 @@
if (shadeSpring.radius == 0 && shadeAnimation.radius == 0) {
return
}
+ // Do not remove blurs when we're re-enabling them
+ if (!value) {
+ return
+ }
shadeSpring.animateTo(0)
shadeSpring.finishIfRunning()
@@ -169,30 +178,27 @@
combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(transitionToFullShadeProgress))
var shadeRadius = max(combinedBlur, wakeAndUnlockBlurRadius).toFloat()
- if (ignoreShadeBlurUntilHidden) {
- if (shadeRadius == 0f) {
- ignoreShadeBlurUntilHidden = false
- } else {
- shadeRadius = 0f
- }
+ if (blursDisabledForAppLaunch) {
+ shadeRadius = 0f
}
- // Home controls have black background, this means that we should not have blur when they
- // are fully visible, otherwise we'll enter Client Composition unnecessarily.
- var globalActionsRadius = globalActionsSpring.radius
- if (showingHomeControls) {
- globalActionsRadius = 0
- }
- var blur = max(shadeRadius.toInt(), globalActionsRadius)
+ var blur = shadeRadius.toInt()
// Make blur be 0 if it is necessary to stop blur effect.
if (scrimsVisible) {
blur = 0
}
-
- val opaque = scrimsVisible && !ignoreShadeBlurUntilHidden
- blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur, opaque)
val zoomOut = blurUtils.ratioOfBlurRadius(blur)
+
+ if (!blurUtils.supportsBlursOnWindows()) {
+ blur = 0
+ }
+
+ // Brightness slider removes blur, but doesn't affect zooms
+ blur = (blur * (1f - brightnessMirrorSpring.ratio)).toInt()
+
+ val opaque = scrimsVisible && !blursDisabledForAppLaunch
+ blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur, opaque)
try {
if (root.isAttachedToWindow && root.windowToken != null) {
wallpaperManager.setWallpaperZoomOut(root.windowToken, zoomOut)
@@ -259,7 +265,7 @@
if (isDozing) {
shadeSpring.finishIfRunning()
shadeAnimation.finishIfRunning()
- globalActionsSpring.finishIfRunning()
+ brightnessMirrorSpring.finishIfRunning()
}
}
@@ -414,19 +420,15 @@
!keyguardStateController.isKeyguardFadingAway
}
- fun updateGlobalDialogVisibility(visibility: Float, dialogView: View?) {
- globalActionsSpring.animateTo(blurUtils.blurRadiusOfRatio(visibility), dialogView)
- }
-
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
IndentingPrintWriter(pw, " ").let {
it.println("StatusBarWindowBlurController:")
it.increaseIndent()
it.println("shadeRadius: ${shadeSpring.radius}")
it.println("shadeAnimation: ${shadeAnimation.radius}")
- it.println("globalActionsRadius: ${globalActionsSpring.radius}")
+ it.println("brightnessMirrorRadius: ${brightnessMirrorSpring.radius}")
it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius")
- it.println("ignoreShadeBlurUntilHidden: $ignoreShadeBlurUntilHidden")
+ it.println("blursDisabledForAppLaunch: $blursDisabledForAppLaunch")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 3c549f9..467f27f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -158,14 +158,7 @@
final int N = activeNotifications.size();
for (int i = 0; i < N; i++) {
NotificationEntry ent = activeNotifications.get(i);
- final boolean isBubbleNotificationSuppressedFromShade = mBubblesOptional.isPresent()
- && mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(
- ent.getKey(), ent.getSbn().getGroupKey());
- if (ent.isRowDismissed() || ent.isRowRemoved()
- || isBubbleNotificationSuppressedFromShade
- || mFgsSectionController.hasEntry(ent)) {
- // we don't want to update removed notifications because they could
- // temporarily become children if they were isolated before.
+ if (shouldSuppressActiveNotification(ent)) {
continue;
}
@@ -254,9 +247,11 @@
}
for (ExpandableNotificationRow viewToRemove : viewsToRemove) {
- if (mEntryManager.getPendingOrActiveNotif(viewToRemove.getEntry().getKey()) != null) {
+ NotificationEntry entry = viewToRemove.getEntry();
+ if (mEntryManager.getPendingOrActiveNotif(entry.getKey()) != null
+ && !shouldSuppressActiveNotification(entry)) {
// we are only transferring this notification to its parent, don't generate an
- // animation
+ // animation. If the notification is suppressed, this isn't a transfer.
mListContainer.setChildTransferInProgress(true);
}
if (viewToRemove.isSummaryWithChildren()) {
@@ -325,6 +320,23 @@
endUpdate();
}
+ /**
+ * Should a notification entry from the active list be suppressed and not show?
+ */
+ private boolean shouldSuppressActiveNotification(NotificationEntry ent) {
+ final boolean isBubbleNotificationSuppressedFromShade = mBubblesOptional.isPresent()
+ && mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(
+ ent.getKey(), ent.getSbn().getGroupKey());
+ if (ent.isRowDismissed() || ent.isRowRemoved()
+ || isBubbleNotificationSuppressedFromShade
+ || mFgsSectionController.hasEntry(ent)) {
+ // we want to suppress removed notifications because they could
+ // temporarily become children if they were isolated before.
+ return true;
+ }
+ return false;
+ }
+
private void addNotificationChildrenAndSort() {
// Let's now add all notification children which are missing
boolean orderChanged = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index 924eb26..b6aed23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -132,6 +132,8 @@
public void removeRemoteInput(NotificationEntry entry, Object token) {
Objects.requireNonNull(entry);
if (entry.mRemoteEditImeVisible) return;
+ // If the view is being removed, this may be called even though we're not active
+ if (!isRemoteInputActive(entry)) return;
pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */, token);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 9f59023..f8a1ff8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -145,11 +145,11 @@
}
@Override
- public boolean setState(int state) {
+ public boolean setState(int state, boolean force) {
if (state > MAX_STATE || state < MIN_STATE) {
throw new IllegalArgumentException("Invalid state " + state);
}
- if (state == mState) {
+ if (!force && state == mState) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index b6d6ed5..73f3d90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -59,7 +59,19 @@
* @param state see {@link StatusBarState} for valid options
* @return {@code true} if the state changed, else {@code false}
*/
- boolean setState(int state);
+ default boolean setState(int state) {
+ return setState(state, false /* force */);
+ }
+
+ /**
+ * Update the status bar state
+ * @param state see {@link StatusBarState} for valid options
+ * @param force whether to set the state even if it's the same as the current state. This will
+ * dispatch the state to all StatusBarStateListeners, ensuring that all listening
+ * components are reset to this state.
+ * @return {@code true} if the state was changed or set forcefully
+ */
+ boolean setState(int state, boolean force);
/**
* Update the dozing state from {@link StatusBar}'s perspective
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
index 8479b30..f30010c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -383,10 +383,18 @@
const val SHOWING_PERSISTENT_DOT = 4
private const val TAG = "SystemStatusAnimationScheduler"
-private const val DELAY: Long = 100
-private const val DISPLAY_LENGTH = 5000L
-private const val ENTRANCE_ANIM_LENGTH = 500L
-private const val CHIP_ANIM_LENGTH = 500L
+private const val DELAY = 0L
+
+/**
+ * The total time spent animation should be 1500ms. The entrance animation is how much time
+ * we give to the system to animate system elements out of the way. Total chip animation length
+ * will be equivalent to 2*chip_anim_length + display_length
+ */
+private const val ENTRANCE_ANIM_LENGTH = 250L
+private const val CHIP_ANIM_LENGTH = 250L
+// 1s + entrance time + chip anim_length
+private const val DISPLAY_LENGTH = 1500L
+
private const val MIN_UPTIME: Long = 5 * 1000
private const val DEBUG = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
index f1479a1..f19cf5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
@@ -22,11 +22,27 @@
)
var startTranslationZ = 0f
+
+ /**
+ * The top position of the notification at the start of the animation. This is needed in order
+ * to keep the notification at its place when launching a notification that is clipped rounded.
+ */
+ var startNotificationTop = 0f
var startClipTopAmount = 0
var parentStartClipTopAmount = 0
var progress = 0f
var linearProgress = 0f
+ /**
+ * The rounded top clipping at the beginning.
+ */
+ var startRoundedTopClipping = 0
+
+ /**
+ * The rounded top clipping of the parent notification at the start.
+ */
+ var parentStartRoundedTopClipping = 0
+
override val topChange: Int
get() {
// We need this compensation to ensure that the QS moves in sync.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index aef01e9..0fb1c54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -71,11 +71,11 @@
// Check if the notification is displaying the menu, if so slide notification back
if (isMenuVisible(row)) {
mLogger.logMenuVisible(entry);
- row.animateTranslateNotification(0);
+ row.animateResetTranslation();
return;
} else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) {
mLogger.logParentMenuVisible(entry);
- row.getNotificationParent().animateTranslateNotification(0);
+ row.getNotificationParent().animateResetTranslation();
return;
} else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
// We never want to open the app directly if the user clicks in between
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index c248670..1bbef25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -40,6 +40,11 @@
private val headsUpManager: HeadsUpManagerPhone,
private val notification: ExpandableNotificationRow
) : ActivityLaunchAnimator.Controller {
+
+ companion object {
+ const val ANIMATION_DURATION_TOP_ROUNDING = 100L
+ }
+
private val notificationEntry = notification.entry
private val notificationKey = notificationEntry.sbn.key
@@ -54,18 +59,37 @@
val height = max(0, notification.actualHeight - notification.clipBottomAmount)
val location = notification.locationOnScreen
+ val clipStartLocation = notificationListContainer.getTopClippingStartLocation()
+ val roundedTopClipping = Math.max(clipStartLocation - location[1], 0)
+ val windowTop = location[1] + roundedTopClipping
+ val topCornerRadius = if (roundedTopClipping > 0) {
+ // Because the rounded Rect clipping is complex, we start the top rounding at
+ // 0, which is pretty close to matching the real clipping.
+ // We'd have to clipOut the overlaid drawable too with the outer rounded rect in case
+ // if we'd like to have this perfect, but this is close enough.
+ 0f
+ } else {
+ notification.currentBackgroundRadiusTop
+ }
val params = ExpandAnimationParameters(
- top = location[1],
+ top = windowTop,
bottom = location[1] + height,
left = location[0],
right = location[0] + notification.width,
- topCornerRadius = notification.currentBackgroundRadiusTop,
+ topCornerRadius = topCornerRadius,
bottomCornerRadius = notification.currentBackgroundRadiusBottom
)
params.startTranslationZ = notification.translationZ
+ params.startNotificationTop = notification.translationY
+ params.startRoundedTopClipping = roundedTopClipping
params.startClipTopAmount = notification.clipTopAmount
if (notification.isChildInGroup) {
+ params.startNotificationTop += notification.notificationParent.translationY
+ val parentRoundedClip = Math.max(clipStartLocation
+ - notification.notificationParent.locationOnScreen[1], 0)
+ params.parentStartRoundedTopClipping = parentRoundedClip
+
val parentClip = notification.notificationParent.clipTopAmount
params.parentStartClipTopAmount = parentClip
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 760bee2..b0a7767 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -264,7 +264,7 @@
}
override fun onStateChanged(newState: Int) {
- if (unlockedScreenOffAnimationController.shouldPlayScreenOffAnimation()) {
+ if (dozeParameters.shouldControlUnlockedScreenOff()) {
if (unlockedScreenOffAnimationController.isScreenOffAnimationPlaying() &&
state == StatusBarState.KEYGUARD &&
newState == StatusBarState.SHADE) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 4136624..4f3406c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -203,6 +203,14 @@
mBackgroundNormal.setCustomBackground(R.drawable.notification_material_bg);
}
+ protected boolean hideBackground() {
+ return false;
+ }
+
+ protected void updateBackground() {
+ mBackgroundNormal.setVisibility(hideBackground() ? INVISIBLE : VISIBLE);
+ }
+
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
@@ -330,30 +338,6 @@
}
}
- @Override
- public void setDistanceToTopRoundness(float distanceToTopRoundness) {
- super.setDistanceToTopRoundness(distanceToTopRoundness);
- mBackgroundNormal.setDistanceToTopRoundness(distanceToTopRoundness);
- }
-
- /** Sets whether this view is the last notification in a section. */
- @Override
- public void setLastInSection(boolean lastInSection) {
- if (lastInSection != mLastInSection) {
- super.setLastInSection(lastInSection);
- mBackgroundNormal.setLastInSection(lastInSection);
- }
- }
-
- /** Sets whether this view is the first notification in a section. */
- @Override
- public void setFirstInSection(boolean firstInSection) {
- if (firstInSection != mFirstInSection) {
- super.setFirstInSection(firstInSection);
- mBackgroundNormal.setFirstInSection(firstInSection);
- }
- }
-
/**
* Set an override tint color that is used for the background.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 6fd5567..93166f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -35,6 +35,7 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.AnimationDrawable;
@@ -85,6 +86,7 @@
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
+import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
@@ -133,7 +135,7 @@
private boolean mUpdateBackgroundOnUpdate;
private boolean mNotificationTranslationFinished = false;
- private ArrayList<MenuItem> mSnoozedMenuItems;
+ private boolean mIsSnoozed;
/**
* Listener for when {@link ExpandableNotificationRow} is laid out.
@@ -252,6 +254,7 @@
private OnExpandClickListener mOnExpandClickListener;
private View.OnClickListener mOnAppClickListener;
private View.OnClickListener mOnFeedbackClickListener;
+ private Path mExpandingClipPath;
// Listener will be called when receiving a long click event.
// Use #setLongPressPosition to optionally assign positional data with the long press.
@@ -836,6 +839,7 @@
public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {
if (mExpandAnimationRunning && !isChildInGroup && mNotificationParent != null) {
mNotificationParent.setChildIsExpanding(false);
+ mNotificationParent.setExpandingClipPath(null);
mNotificationParent.setExtraWidthForClipping(0.0f);
mNotificationParent.setMinimumHeightForClipping(0);
}
@@ -846,8 +850,6 @@
updateClickAndFocus();
if (mNotificationParent != null) {
setOverrideTintColor(NO_COLOR, 0.0f);
- // Let's reset the distance to top roundness, as this isn't applied to group children
- setDistanceToTopRoundness(NO_ROUNDNESS);
mNotificationParent.updateBackgroundForGroupState();
}
updateBackgroundClipping();
@@ -876,7 +878,7 @@
@Override
protected boolean handleSlideBack() {
if (mMenuRow != null && mMenuRow.isMenuVisible()) {
- animateTranslateNotification(0 /* targetLeft */);
+ animateResetTranslation();
return true;
}
return false;
@@ -1107,8 +1109,7 @@
false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
false /* resetMenu */);
mNotificationGutsManager.openGuts(this, 0, 0, item);
- mSnoozedMenuItems = mMenuRow.getMenuItems(mMenuRow.getMenuView().getContext());
- mMenuRow.resetMenu();
+ mIsSnoozed = true;
};
}
@@ -1713,21 +1714,17 @@
mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this);
mChildrenContainer.onNotificationUpdated();
- if (mShouldTranslateContents) {
- mTranslateableViews.add(mChildrenContainer);
- }
+ mTranslateableViews.add(mChildrenContainer);
});
- if (mShouldTranslateContents) {
- // Add the views that we translate to reveal the menu
- mTranslateableViews = new ArrayList<>();
- for (int i = 0; i < getChildCount(); i++) {
- mTranslateableViews.add(getChildAt(i));
- }
- // Remove views that don't translate
- mTranslateableViews.remove(mChildrenContainerStub);
- mTranslateableViews.remove(mGutsStub);
+ // Add the views that we translate to reveal the menu
+ mTranslateableViews = new ArrayList<>();
+ for (int i = 0; i < getChildCount(); i++) {
+ mTranslateableViews.add(getChildAt(i));
}
+ // Remove views that don't translate
+ mTranslateableViews.remove(mChildrenContainerStub);
+ mTranslateableViews.remove(mGutsStub);
}
private void doLongClickCallback() {
@@ -1805,7 +1802,7 @@
mTranslateAnim.cancel();
}
- if (!mShouldTranslateContents) {
+ if (mDismissUsingRowTranslationX) {
setTranslationX(0);
} else if (mTranslateableViews != null) {
for (int i = 0; i < mTranslateableViews.size(); i++) {
@@ -1827,10 +1824,7 @@
void onGutsClosed() {
updateContentAccessibilityImportanceForGuts(true /* isEnabled */);
- if (mSnoozedMenuItems != null && mSnoozedMenuItems.size() > 0) {
- mMenuRow.setMenuItems(mSnoozedMenuItems);
- mSnoozedMenuItems = null;
- }
+ mIsSnoozed = false;
}
/**
@@ -1867,23 +1861,47 @@
return mPrivateLayout.getActiveRemoteInputText();
}
- public void animateTranslateNotification(final float leftTarget) {
+ /**
+ * Reset the translation with an animation.
+ */
+ public void animateResetTranslation() {
if (mTranslateAnim != null) {
mTranslateAnim.cancel();
}
- mTranslateAnim = getTranslateViewAnimator(leftTarget, null /* updateListener */);
+ mTranslateAnim = getTranslateViewAnimator(0, null /* updateListener */);
if (mTranslateAnim != null) {
mTranslateAnim.start();
}
}
+ /**
+ * Set the dismiss behavior of the view.
+ * @param usingRowTranslationX {@code true} if the view should translate using regular
+ * translationX, otherwise the contents will be
+ * translated.
+ */
+ @Override
+ public void setDismissUsingRowTranslationX(boolean usingRowTranslationX) {
+ if (usingRowTranslationX != mDismissUsingRowTranslationX) {
+ // In case we were already transitioning, let's switch over!
+ float previousTranslation = getTranslation();
+ if (previousTranslation != 0) {
+ setTranslation(0);
+ }
+ super.setDismissUsingRowTranslationX(usingRowTranslationX);
+ if (previousTranslation != 0) {
+ setTranslation(previousTranslation);
+ }
+ }
+ }
+
@Override
public void setTranslation(float translationX) {
invalidate();
if (isBlockingHelperShowingAndTranslationFinished()) {
mGuts.setTranslationX(translationX);
return;
- } else if (!mShouldTranslateContents) {
+ } else if (mDismissUsingRowTranslationX) {
setTranslationX(translationX);
} else if (mTranslateableViews != null) {
// Translate the group of views
@@ -1907,7 +1925,7 @@
@Override
public float getTranslation() {
- if (!mShouldTranslateContents) {
+ if (mDismissUsingRowTranslationX) {
return getTranslationX();
}
@@ -2018,7 +2036,22 @@
setTranslationZ(translationZ);
float extraWidthForClipping = params.getWidth() - getWidth();
setExtraWidthForClipping(extraWidthForClipping);
- int top = params.getTop();
+ int top;
+ if (params.getStartRoundedTopClipping() > 0) {
+ // If we were clipping initially, let's interpolate from the start position to the
+ // top. Otherwise, we just take the top directly.
+ float expandProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+ params.getProgress(0,
+ NotificationLaunchAnimatorController.ANIMATION_DURATION_TOP_ROUNDING));
+ float startTop = params.getStartNotificationTop();
+ top = (int) Math.min(MathUtils.lerp(startTop,
+ params.getTop(), expandProgress),
+ startTop);
+ } else {
+ top = params.getTop();
+ }
+ int actualHeight = params.getBottom() - top;
+ setActualHeight(actualHeight);
int startClipTopAmount = params.getStartClipTopAmount();
int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, params.getProgress());
if (mNotificationParent != null) {
@@ -2047,13 +2080,12 @@
setClipTopAmount(clipTopAmount);
}
setTranslationY(top);
- setActualHeight(params.getHeight());
mTopRoundnessDuringExpandAnimation = params.getTopCornerRadius() / mOutlineRadius;
mBottomRoundnessDuringExpandAnimation = params.getBottomCornerRadius() / mOutlineRadius;
invalidateOutline();
- mBackgroundNormal.setExpandAnimationParams(params);
+ mBackgroundNormal.setExpandAnimationSize(params.getWidth(), actualHeight);
}
public void setExpandAnimationRunning(boolean expandAnimationRunning) {
@@ -2450,7 +2482,8 @@
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int intrinsicBefore = getIntrinsicHeight();
super.onLayout(changed, left, top, right, bottom);
- if (intrinsicBefore != getIntrinsicHeight() && intrinsicBefore != 0) {
+ if (intrinsicBefore != getIntrinsicHeight()
+ && (intrinsicBefore != 0 || getActualHeight() > 0)) {
notifyHeightChanged(true /* needsAnimation */);
}
if (mMenuRow != null && mMenuRow.getMenuView() != null) {
@@ -2872,6 +2905,12 @@
mShowNoBackground = false;
}
updateOutline();
+ updateBackground();
+ }
+
+ @Override
+ protected boolean hideBackground() {
+ return mShowNoBackground || super.hideBackground();
}
public int getPositionOfChild(ExpandableNotificationRow childRow) {
@@ -2898,7 +2937,11 @@
float y = event.getY();
NotificationViewWrapper wrapper = getVisibleNotificationViewWrapper();
NotificationHeaderView header = wrapper == null ? null : wrapper.getNotificationHeader();
- if (header != null && header.isInTouchRect(x - getTranslation(), y)) {
+ // the extra translation only needs to be added, if we're translating the notification
+ // contents, otherwise the motionEvent is already at the right place due to the
+ // touch event system.
+ float translation = !mDismissUsingRowTranslationX ? getTranslation() : 0;
+ if (header != null && header.isInTouchRect(x - translation, y)) {
return true;
}
if ((!mIsSummaryWithChildren || shouldShowPublic())
@@ -2953,7 +2996,7 @@
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfoInternal(info);
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
- if (canViewBeDismissed()) {
+ if (canViewBeDismissed() && !mIsSnoozed) {
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
}
boolean expandable = shouldShowPublic();
@@ -2969,7 +3012,7 @@
isExpanded = isExpanded();
}
}
- if (expandable) {
+ if (expandable && !mIsSnoozed) {
if (isExpanded) {
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE);
} else {
@@ -3037,24 +3080,6 @@
}
@Override
- public boolean topAmountNeedsClipping() {
- if (isGroupExpanded()) {
- return true;
- }
- if (isGroupExpansionChanging()) {
- return true;
- }
- if (getShowingLayout().shouldClipToRounding(true /* topRounded */,
- false /* bottomRounded */)) {
- return true;
- }
- if (mGuts != null && mGuts.getAlpha() != 0.0f) {
- return true;
- }
- return false;
- }
-
- @Override
protected boolean childNeedsClipping(View child) {
if (child instanceof NotificationContentView) {
NotificationContentView contentView = (NotificationContentView) child;
@@ -3075,6 +3100,26 @@
return super.childNeedsClipping(child);
}
+ /**
+ * Set a clip path to be set while expanding the notification. This is needed to nicely
+ * clip ourselves during the launch if we were clipped rounded in the beginning
+ */
+ public void setExpandingClipPath(Path path) {
+ mExpandingClipPath = path;
+ invalidate();
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ canvas.save();
+ if (mExpandingClipPath != null && (mExpandAnimationRunning || mChildIsExpanding)) {
+ // If we're launching a notification, let's clip if a clip rounded to the clipPath
+ canvas.clipPath(mExpandingClipPath);
+ }
+ super.dispatchDraw(canvas);
+ canvas.restore();
+ }
+
@Override
protected void applyRoundness() {
super.applyRoundness();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 5134c62..d58fe3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -71,21 +71,19 @@
private int mBackgroundTop;
/**
- * {@code true} if the children views of the {@link ExpandableOutlineView} are translated when
+ * {@code false} if the children views of the {@link ExpandableOutlineView} are translated when
* it is moved. Otherwise, the translation is set on the {@code ExpandableOutlineView} itself.
*/
- protected boolean mShouldTranslateContents;
- private boolean mTopAmountRounded;
- private float mDistanceToTopRoundness = -1;
+ protected boolean mDismissUsingRowTranslationX = true;
private float[] mTmpCornerRadii = new float[8];
private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
if (!mCustomOutline && getCurrentTopRoundness() == 0.0f
- && getCurrentBottomRoundness() == 0.0f && !mAlwaysRoundBothCorners
- && !mTopAmountRounded) {
- int translation = mShouldTranslateContents ? (int) getTranslation() : 0;
+ && getCurrentBottomRoundness() == 0.0f && !mAlwaysRoundBothCorners) {
+ // Only when translating just the contents, does the outline need to be shifted.
+ int translation = !mDismissUsingRowTranslationX ? (int) getTranslation() : 0;
int left = Math.max(translation, 0);
int top = mClipTopAmount + mBackgroundTop;
int right = getWidth() + Math.min(translation, 0);
@@ -110,7 +108,9 @@
float topRoundness = mAlwaysRoundBothCorners
? mOutlineRadius : getCurrentBackgroundRadiusTop();
if (!mCustomOutline) {
- int translation = mShouldTranslateContents && !ignoreTranslation
+ // The outline just needs to be shifted if we're translating the contents. Otherwise
+ // it's already in the right place.
+ int translation = !mDismissUsingRowTranslationX && !ignoreTranslation
? (int) getTranslation() : 0;
int halfExtraWidth = (int) (mExtraWidthForClipping / 2.0f);
left = Math.max(translation, 0) - halfExtraWidth;
@@ -168,33 +168,15 @@
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
canvas.save();
- Path intersectPath = null;
- if (mTopAmountRounded && topAmountNeedsClipping()) {
- int left = (int) (- mExtraWidthForClipping / 2.0f);
- int top = (int) (mClipTopAmount - mDistanceToTopRoundness);
- int right = getWidth() + (int) (mExtraWidthForClipping + left);
- int bottom = (int) Math.max(mMinimumHeightForClipping,
- Math.max(getActualHeight() - mClipBottomAmount, top + mOutlineRadius));
- getRoundedRectPath(left, top, right, bottom, mOutlineRadius, 0.0f, mClipPath);
- intersectPath = mClipPath;
- }
- boolean clipped = false;
if (childNeedsClipping(child)) {
Path clipPath = getCustomClipPath(child);
if (clipPath == null) {
clipPath = getClipPath(false /* ignoreTranslation */);
}
if (clipPath != null) {
- if (intersectPath != null) {
- clipPath.op(intersectPath, Path.Op.INTERSECT);
- }
canvas.clipPath(clipPath);
- clipped = true;
}
}
- if (!clipped && intersectPath != null) {
- canvas.clipPath(intersectPath);
- }
boolean result = super.drawChild(canvas, child, drawingTime);
canvas.restore();
return result;
@@ -212,32 +194,19 @@
invalidate();
}
- @Override
- public void setDistanceToTopRoundness(float distanceToTopRoundness) {
- super.setDistanceToTopRoundness(distanceToTopRoundness);
- if (distanceToTopRoundness != mDistanceToTopRoundness) {
- mTopAmountRounded = distanceToTopRoundness >= 0;
- mDistanceToTopRoundness = distanceToTopRoundness;
- applyRoundness();
- }
- }
-
protected boolean childNeedsClipping(View child) {
return false;
}
- public boolean topAmountNeedsClipping() {
- return true;
- }
-
protected boolean isClippingNeeded() {
- return mAlwaysRoundBothCorners || mCustomOutline || getTranslation() != 0 ;
+ // When translating the contents instead of the overall view, we need to make sure we clip
+ // rounded to the contents.
+ boolean forTranslation = getTranslation() != 0 && !mDismissUsingRowTranslationX;
+ return mAlwaysRoundBothCorners || mCustomOutline || forTranslation;
}
private void initDimens() {
Resources res = getResources();
- mShouldTranslateContents =
- res.getBoolean(R.bool.config_translateNotificationContentsOnSwipe);
mOutlineRadius = res.getDimension(R.dimen.notification_shadow_radius);
mAlwaysRoundBothCorners = res.getBoolean(R.bool.config_clipNotificationsToOutline);
if (!mAlwaysRoundBothCorners) {
@@ -272,11 +241,6 @@
}
public float getCurrentBackgroundRadiusTop() {
- // If this view is top amount notification view, it should always has round corners on top.
- // It will be applied with applyRoundness()
- if (mTopAmountRounded) {
- return mOutlineRadius;
- }
return getCurrentTopRoundness() * mOutlineRadius;
}
@@ -382,9 +346,25 @@
}
}
+ /**
+ * Set the dismiss behavior of the view.
+ * @param usingRowTranslationX {@code true} if the view should translate using regular
+ * translationX, otherwise the contents will be
+ * translated.
+ */
+ public void setDismissUsingRowTranslationX(boolean usingRowTranslationX) {
+ mDismissUsingRowTranslationX = usingRowTranslationX;
+ }
+
@Override
public int getOutlineTranslation() {
- return mCustomOutline ? mOutlineRect.left : (int) getTranslation();
+ if (mCustomOutline) {
+ return mOutlineRect.left;
+ }
+ if (mDismissUsingRowTranslationX) {
+ return 0;
+ }
+ return (int) getTranslation();
}
public void updateOutline() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 763d197..8b0764b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -46,7 +46,6 @@
public abstract class ExpandableView extends FrameLayout implements Dumpable {
private static final String TAG = "ExpandableView";
- public static final float NO_ROUNDNESS = -1;
protected OnHeightChangedListener mOnHeightChangedListener;
private int mActualHeight;
protected int mClipTopAmount;
@@ -192,14 +191,6 @@
}
}
- /**
- * Set the distance to the top roundness, from where we should start clipping a value above
- * or equal to 0 is the effective distance, and if a value below 0 is received, there should
- * be no clipping.
- */
- public void setDistanceToTopRoundness(float distanceToTopRoundness) {
- }
-
public void setActualHeight(int actualHeight) {
setActualHeight(actualHeight, true /* notifyListeners */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 4b1f679..0f615aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -42,10 +42,8 @@
private int mActualHeight;
private int mClipBottomAmount;
private int mTintColor;
- private float[] mCornerRadii = new float[8];
+ private final float[] mCornerRadii = new float[8];
private boolean mBottomIsRounded;
- private boolean mLastInSection;
- private boolean mFirstInSection;
private int mBackgroundTop;
private boolean mBottomAmountClips = true;
private boolean mExpandAnimationRunning;
@@ -53,9 +51,6 @@
private int mDrawableAlpha = 255;
private boolean mIsPressedAllowed;
- private boolean mTopAmountRounded;
- private float mDistanceToTopRoundness;
-
public NotificationBackgroundView(Context context, AttributeSet attrs) {
super(context, attrs);
mDontModifyCorners = getResources().getBoolean(
@@ -90,15 +85,6 @@
left = (int) ((getWidth() - mActualWidth) / 2.0f);
right = (int) (left + mActualWidth);
}
- if (mTopAmountRounded) {
- int clipTop = (int) (mClipTopAmount - mDistanceToTopRoundness);
- if (clipTop >= 0 || !mFirstInSection) {
- top += clipTop;
- }
- if (clipTop >= 0 && !mLastInSection) {
- bottom += clipTop;
- }
- }
drawable.setBounds(left, top, right, bottom);
drawable.draw(canvas);
}
@@ -180,14 +166,6 @@
invalidate();
}
- public void setDistanceToTopRoundness(float distanceToTopRoundness) {
- if (distanceToTopRoundness != mDistanceToTopRoundness) {
- mTopAmountRounded = distanceToTopRoundness >= 0;
- mDistanceToTopRoundness = distanceToTopRoundness;
- invalidate();
- }
- }
-
@Override
public boolean hasOverlappingRendering() {
@@ -246,18 +224,6 @@
}
}
- /** Sets whether this background belongs to the last notification in a section. */
- public void setLastInSection(boolean lastInSection) {
- mLastInSection = lastInSection;
- invalidate();
- }
-
- /** Sets whether this background belongs to the first notification in a section. */
- public void setFirstInSection(boolean firstInSection) {
- mFirstInSection = firstInSection;
- invalidate();
- }
-
private void updateBackgroundRadii() {
if (mDontModifyCorners) {
return;
@@ -274,10 +240,10 @@
invalidate();
}
- /** Set the current expand animation parameters. */
- public void setExpandAnimationParams(ExpandAnimationParameters params) {
- mActualHeight = params.getHeight();
- mActualWidth = params.getWidth();
+ /** Set the current expand animation size. */
+ public void setExpandAnimationSize(int actualWidth, int actualHeight) {
+ mActualHeight = actualHeight;
+ mActualWidth = actualWidth;
invalidate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 0c86262..197920f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -72,10 +72,7 @@
private boolean mUnlockHintRunning;
private boolean mQsCustomizerShowing;
private int mIntrinsicPadding;
- private int mExpandAnimationTopChange;
- private ExpandableNotificationRow mExpandingNotification;
private float mHideAmount;
- private float mNotificationScrimTop;
private boolean mAppearing;
private float mPulseHeight = MAX_PULSE_HEIGHT;
private float mDozeAmount = 0.0f;
@@ -225,8 +222,14 @@
return mScrollY;
}
+ /**
+ * Set the new Scroll Y position.
+ */
public void setScrollY(int scrollY) {
- this.mScrollY = scrollY;
+ // Because we're dealing with an overscroller, scrollY could sometimes become smaller than
+ // 0. However this is only for internal purposes and the scroll position when read
+ // should never be smaller than 0, otherwise it can lead to flickers.
+ this.mScrollY = Math.max(scrollY, 0);
}
/**
@@ -256,20 +259,6 @@
return mHideAmount;
}
- /**
- * Set y position of top of notifications background scrim, relative to top of screen.
- */
- public void setNotificationScrimTop(float notificationScrimTop) {
- mNotificationScrimTop = notificationScrimTop;
- }
-
- /**
- * @return Y position of top of notifications background scrim, relative to top of screen.
- */
- public float getNotificationScrimTop() {
- return mNotificationScrimTop;
- }
-
public void setHideSensitive(boolean hideSensitive) {
mHideSensitive = hideSensitive;
}
@@ -527,22 +516,6 @@
return isDozing() && !isPulsing(row.getEntry());
}
- public void setExpandAnimationTopChange(int expandAnimationTopChange) {
- mExpandAnimationTopChange = expandAnimationTopChange;
- }
-
- public void setExpandingNotification(ExpandableNotificationRow row) {
- mExpandingNotification = row;
- }
-
- public ExpandableNotificationRow getExpandingNotification() {
- return mExpandingNotification;
- }
-
- public int getExpandAnimationTopChange() {
- return mExpandAnimationTopChange;
- }
-
/**
* @return {@code true } when shade is completely hidden: in AOD, ambient display or when
* bypassing.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 99fe541..c2716b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -710,10 +710,10 @@
if (mUserLocked) {
expandFraction = getGroupExpandFraction();
}
- final boolean dividersVisible = mUserLocked && !showingAsLowPriority()
- || (mChildrenExpanded && mShowDividersWhenExpanded)
- || (mContainingNotification.isGroupExpansionChanging()
- && !mHideDividersDuringExpand);
+ final boolean isExpanding = !showingAsLowPriority()
+ && (mUserLocked || mContainingNotification.isGroupExpansionChanging());
+ final boolean dividersVisible = (mChildrenExpanded && mShowDividersWhenExpanded)
+ || (isExpanding && !mHideDividersDuringExpand);
for (int i = 0; i < childCount; i++) {
ExpandableNotificationRow child = mAttachedChildren.get(i);
ExpandableViewState viewState = child.getViewState();
@@ -725,7 +725,7 @@
tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
float alpha = mChildrenExpanded && viewState.alpha != 0 ? mDividerAlpha : 0;
if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) {
- alpha = NotificationUtils.interpolate(0, 0.5f,
+ alpha = NotificationUtils.interpolate(0, mDividerAlpha,
Math.min(viewState.alpha, expandFraction));
}
tmpState.hidden = !dividersVisible;
@@ -789,10 +789,10 @@
int childCount = mAttachedChildren.size();
ViewState tmpState = new ViewState();
float expandFraction = getGroupExpandFraction();
- final boolean dividersVisible = mUserLocked && !showingAsLowPriority()
- || (mChildrenExpanded && mShowDividersWhenExpanded)
- || (mContainingNotification.isGroupExpansionChanging()
- && !mHideDividersDuringExpand);
+ final boolean isExpanding = !showingAsLowPriority()
+ && (mUserLocked || mContainingNotification.isGroupExpansionChanging());
+ final boolean dividersVisible = (mChildrenExpanded && mShowDividersWhenExpanded)
+ || (isExpanding && !mHideDividersDuringExpand);
for (int i = childCount - 1; i >= 0; i--) {
ExpandableNotificationRow child = mAttachedChildren.get(i);
ExpandableViewState viewState = child.getViewState();
@@ -802,9 +802,9 @@
View divider = mDividers.get(i);
tmpState.initFrom(divider);
tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
- float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
+ float alpha = mChildrenExpanded && viewState.alpha != 0 ? mDividerAlpha : 0;
if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) {
- alpha = NotificationUtils.interpolate(0, 0.5f,
+ alpha = NotificationUtils.interpolate(0, mDividerAlpha,
Math.min(viewState.alpha, expandFraction));
}
tmpState.hidden = !dividersVisible;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index 2a2e733f7..7a5c188 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -200,4 +200,11 @@
default void setWillExpand(boolean willExpand) {}
void setNotificationActivityStarter(NotificationActivityStarter notificationActivityStarter);
+
+ /**
+ * @return the start location where we start clipping notifications.
+ */
+ default int getTopClippingStartLocation() {
+ return 0;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index f90b4c0..5ccb064 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -40,6 +40,7 @@
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Paint;
+import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Bundle;
@@ -76,6 +77,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.statusbar.CommandQueue;
@@ -89,6 +91,7 @@
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.ShadeViewRefactor;
import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
@@ -110,6 +113,7 @@
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import com.android.systemui.util.Assert;
+import com.android.systemui.util.leak.RotationUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -137,6 +141,9 @@
// adb shell setprop persist.debug.nssl true && adb reboot
private static final boolean DEBUG = SystemProperties.getBoolean("persist.debug.nssl",
false /* default */);
+ // TODO(b/187291379) disable again before release
+ private static final boolean DEBUG_REMOVE_ANIMATION = SystemProperties.getBoolean(
+ "persist.debug.nssl.dismiss", false /* default */);
private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
@@ -420,17 +427,26 @@
animateScroll();
};
private int mCornerRadius;
+ private int mMinimumPaddings;
+ private int mQsTilePadding;
+ private boolean mSkinnyNotifsInLandscape;
private int mSidePaddings;
private final Rect mBackgroundAnimationRect = new Rect();
private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>();
private int mHeadsUpInset;
+
+ /**
+ * The position of the scroll boundary relative to this view. This is where the notifications
+ * stop scrolling and will start to clip instead.
+ */
+ private int mQsScrollBoundaryPosition;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
private final Rect mTmpRect = new Rect();
private DismissListener mDismissListener;
private DismissAllAnimationListener mDismissAllAnimationListener;
private NotificationRemoteInputManager mRemoteInputManager;
private ShadeController mShadeController;
- private Runnable mOnStackYChanged;
+ private Consumer<Boolean> mOnStackYChanged;
protected boolean mClearAllEnabled;
@@ -453,6 +469,64 @@
private NotificationStackScrollLayoutController mController;
private boolean mKeyguardMediaControllorVisible;
+
+ /**
+ * The clip path used to clip the view in a rounded way.
+ */
+ private final Path mRoundedClipPath = new Path();
+
+ /**
+ * The clip Path used to clip the launching notification. This may be different
+ * from the normal path, as the views launch animation could start clipped.
+ */
+ private final Path mLaunchedNotificationClipPath = new Path();
+
+ /**
+ * Should we use rounded rect clipping right now
+ */
+ private boolean mShouldUseRoundedRectClipping = false;
+
+ private int mRoundedRectClippingLeft;
+ private int mRoundedRectClippingTop;
+ private int mRoundedRectClippingBottom;
+ private int mRoundedRectClippingRight;
+ private float[] mBgCornerRadii = new float[8];
+
+ /**
+ * Whether stackY should be animated in case the view is getting shorter than the scroll
+ * position and this scrolling will lead to the top scroll inset getting smaller.
+ */
+ private boolean mAnimateStackYForContentHeightChange = false;
+
+ /**
+ * Are we launching a notification right now
+ */
+ private boolean mLaunchingNotification;
+
+ /**
+ * Does the launching notification need to be clipped
+ */
+ private boolean mLaunchingNotificationNeedsToBeClipped;
+
+ /**
+ * The current launch animation params when launching a notification
+ */
+ private ExpandAnimationParameters mLaunchAnimationParams;
+
+ /**
+ * Corner radii of the launched notification if it's clipped
+ */
+ private float[] mLaunchedNotificationRadii = new float[8];
+
+ /**
+ * The notification that is being launched currently.
+ */
+ private ExpandableNotificationRow mExpandingNotificationRow;
+
+ /**
+ * Do notifications dismiss with normal transitioning
+ */
+ private boolean mDismissUsingRowTranslationX = true;
private NotificationEntry mTopHeadsUpEntry;
private long mNumHeadsUp;
private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
@@ -506,7 +580,7 @@
mSectionsManager = notificationSectionsManager;
mFeatureFlags = featureFlags;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
- mShouldUseSplitNotificationShade = shouldUseSplitNotificationShade(mFeatureFlags, res);
+ updateSplitNotificationShade();
mSectionsManager.initialize(this, LayoutInflater.from(context));
mSections = mSectionsManager.createSectionsForBuckets();
@@ -609,6 +683,7 @@
mController.hasActiveClearableNotifications(ROWS_ALL);
RemoteInputController remoteInputController = mRemoteInputManager.getController();
boolean showFooterView = (showDismissView || mController.hasActiveNotifications())
+ && mEmptyShadeView.getVisibility() == GONE
&& mStatusBarState != StatusBarState.KEYGUARD
&& !mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()
&& (remoteInputController == null || !remoteInputController.isRemoteInputActive());
@@ -856,12 +931,32 @@
R.dimen.min_top_overscroll_to_qs);
mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
- mSidePaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
+ mMinimumPaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
+ mQsTilePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_margin_horizontal);
+ mSkinnyNotifsInLandscape = res.getBoolean(R.bool.config_skinnyNotifsInLandscape);
+ mSidePaddings = mMinimumPaddings; // Updated in onMeasure by updateSidePadding()
mMinInteractionHeight = res.getDimensionPixelSize(
R.dimen.notification_min_interaction_height);
mCornerRadius = res.getDimensionPixelSize(R.dimen.notification_corner_radius);
mHeadsUpInset = mStatusBarHeight + res.getDimensionPixelSize(
R.dimen.heads_up_status_bar_padding);
+ mQsScrollBoundaryPosition = res.getDimensionPixelSize(
+ com.android.internal.R.dimen.quick_qs_offset_height);
+ }
+
+ void updateSidePadding(int viewWidth) {
+ if (viewWidth == 0 || !mSkinnyNotifsInLandscape) {
+ mSidePaddings = mMinimumPaddings;
+ return;
+ }
+ // Portrait is easy, just use the dimen for paddings
+ if (RotationUtils.getRotation(mContext) == RotationUtils.ROTATION_NONE) {
+ mSidePaddings = mMinimumPaddings;
+ return;
+ }
+ final int innerWidth = viewWidth - mMinimumPaddings * 2;
+ final int qsTileWidth = (innerWidth - mQsTilePadding * 3) / 4;
+ mSidePaddings = mMinimumPaddings + qsTileWidth + mQsTilePadding;
}
void updateCornerRadius() {
@@ -924,6 +1019,7 @@
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
+ updateSidePadding(width);
int childWidthSpec = MeasureSpec.makeMeasureSpec(width - mSidePaddings * 2,
MeasureSpec.getMode(widthMeasureSpec));
// Don't constrain the height of the children so we know how big they'd like to be
@@ -961,6 +1057,9 @@
updateFirstAndLastBackgroundViews();
updateAlgorithmLayoutMinHeight();
updateOwnTranslationZ();
+
+ // Once the layout has finished, we don't need to animate any scrolling clampings anymore.
+ mAnimateStackYForContentHeightChange = false;
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -1017,33 +1116,11 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void onPreDrawDuringAnimation() {
mShelf.updateAppearance();
- updateClippingToTopRoundedCorner();
if (!mNeedsAnimation && !mChildrenUpdateRequested) {
updateBackground();
}
}
- @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- private void updateClippingToTopRoundedCorner() {
- Float clipStart = mAmbientState.getNotificationScrimTop();
- Float clipEnd = clipStart + mCornerRadius;
- boolean first = true;
- for (int i = 0; i < getChildCount(); i++) {
- ExpandableView child = (ExpandableView) getChildAt(i);
- if (child.getVisibility() == GONE) {
- continue;
- }
- float start = child.getTranslationY();
- float end = start + child.getActualHeight();
- boolean clip = clipStart > start && clipStart < end
- || clipEnd >= start && clipEnd <= end;
- clip &= !(first && mScrollAdapter.isScrolledToTop());
- child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0)
- : ExpandableView.NO_ROUNDNESS);
- first = false;
- }
- }
-
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void updateScrollStateForAddedChildren() {
if (mChildrenToAddAnimated.isEmpty()) {
@@ -1117,7 +1194,13 @@
private void clampScrollPosition() {
int scrollRange = getScrollRange();
if (scrollRange < mOwnScrollY) {
- setOwnScrollY(scrollRange);
+ boolean animateStackY = false;
+ if (scrollRange < getScrollAmountToScrollBoundary()
+ && mAnimateStackYForContentHeightChange) {
+ // if the scroll boundary updates the position of the stack,
+ animateStackY = true;
+ }
+ setOwnScrollY(scrollRange, animateStackY);
}
}
@@ -1146,14 +1229,23 @@
* Apply expansion fraction to the y position and height of the notifications panel.
*/
private void updateStackPosition() {
+ updateStackPosition(false /* listenerNeedsAnimation */);
+ }
+
+ /**
+ * Apply expansion fraction to the y position and height of the notifications panel.
+ * @param listenerNeedsAnimation does the listener need to animate?
+ */
+ private void updateStackPosition(boolean listenerNeedsAnimation) {
// Consider interpolating from an mExpansionStartY for use on lockscreen and AOD
float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition
- + mAmbientState.getOverExpansion();
+ + mAmbientState.getOverExpansion()
+ - getCurrentOverScrollAmount(false /* top */);
final float fraction = mAmbientState.getExpansionFraction();
final float stackY = MathUtils.lerp(0, endTopPosition, fraction);
mAmbientState.setStackY(stackY);
if (mOnStackYChanged != null) {
- mOnStackYChanged.run();
+ mOnStackYChanged.accept(listenerNeedsAnimation);
}
if (mQsExpansionFraction <= 0) {
final float stackEndHeight = Math.max(0f,
@@ -1165,7 +1257,11 @@
}
}
- void setOnStackYChanged(Runnable onStackYChanged) {
+ /**
+ * Add a listener when the StackY changes. The argument signifies whether an animation is
+ * needed.
+ */
+ void setOnStackYChanged(Consumer<Boolean> onStackYChanged) {
mOnStackYChanged = onStackYChanged;
}
@@ -1600,7 +1696,7 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Resources res = getResources();
- mShouldUseSplitNotificationShade = shouldUseSplitNotificationShade(mFeatureFlags, res);
+ updateSplitNotificationShade();
mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
float densityScale = res.getDisplayMetrics().density;
mSwipeHelper.setDensityScale(densityScale);
@@ -1866,6 +1962,7 @@
if (onTop) {
notifyOverscrollTopListener(amount, isRubberbanded);
}
+ updateStackPosition();
requestChildrenUpdate();
}
}
@@ -2074,7 +2171,7 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void updateContentHeight() {
- final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mSidePaddings;
+ final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mMinimumPaddings;
int height = (int) scrimTopPadding;
float previousPaddingRequest = mPaddingBetweenElements;
int numShownItems = 0;
@@ -2527,8 +2624,7 @@
updateScrollStateForRemovedChild(child);
boolean animationGenerated = generateRemoveAnimation(child);
if (animationGenerated) {
- if (!mSwipedOutViews.contains(child)
- || Math.abs(child.getTranslation()) != child.getWidth()) {
+ if (!mSwipedOutViews.contains(child) || !isFullySwipedOut(child)) {
container.addTransientView(child, 0);
child.setTransientContainer(container);
}
@@ -2540,6 +2636,13 @@
focusNextViewIfFocused(child);
}
+ /**
+ * Has this view been fully swiped out such that it's not visible anymore.
+ */
+ public boolean isFullySwipedOut(ExpandableView child) {
+ return Math.abs(child.getTranslation()) >= Math.abs(getTotalTranslationLength(child));
+ }
+
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void focusNextViewIfFocused(View view) {
if (view instanceof ExpandableNotificationRow) {
@@ -2575,7 +2678,17 @@
*/
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
boolean generateRemoveAnimation(ExpandableView child) {
+ String key = "";
+ if (DEBUG_REMOVE_ANIMATION) {
+ if (child instanceof ExpandableNotificationRow) {
+ key = ((ExpandableNotificationRow) child).getEntry().getKey();
+ }
+ Log.d(TAG, "generateRemoveAnimation " + key);
+ }
if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
+ if (DEBUG_REMOVE_ANIMATION) {
+ Log.d(TAG, "removedBecauseOfHeadsUp " + key);
+ }
mAddedHeadsUpChildren.remove(child);
return false;
}
@@ -2584,8 +2697,17 @@
mClearTransientViewsWhenFinished.add(child);
return true;
}
+ if (DEBUG_REMOVE_ANIMATION) {
+ Log.d(TAG, "generateRemove " + key
+ + "\nmIsExpanded " + mIsExpanded
+ + "\nmAnimationsEnabled " + mAnimationsEnabled
+ + "\n!invisible group " + !isChildInInvisibleGroup(child));
+ }
if (mIsExpanded && mAnimationsEnabled && !isChildInInvisibleGroup(child)) {
if (!mChildrenToAddAnimated.contains(child)) {
+ if (DEBUG_REMOVE_ANIMATION) {
+ Log.d(TAG, "needsAnimation = true " + key);
+ }
// Generate Animations
mChildrenToRemoveAnimated.add(child);
mNeedsAnimation = true;
@@ -2607,7 +2729,7 @@
/**
* Remove a removed child view from the heads up animations if it was just added there
*
- * @return whether any child was removed from the list to animate
+ * @return whether any child was removed from the list to animate and the view was just added
*/
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
@@ -2626,7 +2748,7 @@
((ExpandableNotificationRow) child).setHeadsUpAnimatingAway(false);
}
mTmpList.clear();
- return hasAddEvent;
+ return hasAddEvent && mAddedHeadsUpChildren.contains(child);
}
// TODO (b/162832756): remove since this won't happen in new pipeline (we prune groups in
@@ -2659,17 +2781,30 @@
final int startingPosition = getPositionInLinearLayout(removedChild);
final int childHeight = getIntrinsicHeight(removedChild) + mPaddingBetweenElements;
final int endPosition = startingPosition + childHeight;
- if (endPosition <= mOwnScrollY) {
+ final int scrollBoundaryStart = getScrollAmountToScrollBoundary();
+ mAnimateStackYForContentHeightChange = true;
+ // This is reset onLayout
+ if (endPosition <= mOwnScrollY - scrollBoundaryStart) {
// This child is fully scrolled of the top, so we have to deduct its height from the
// scrollPosition
setOwnScrollY(mOwnScrollY - childHeight);
- } else if (startingPosition < mOwnScrollY) {
+ } else if (startingPosition < mOwnScrollY - scrollBoundaryStart) {
// This child is currently being scrolled into, set the scroll position to the
// start of this child
- setOwnScrollY(startingPosition);
+ setOwnScrollY(startingPosition + scrollBoundaryStart);
}
}
+ /**
+ * @return the amount of scrolling needed to start clipping notifications.
+ */
+ private int getScrollAmountToScrollBoundary() {
+ if (mShouldUseSplitNotificationShade) {
+ return mSidePaddings;
+ }
+ return mTopPadding - mQsScrollBoundaryPosition;
+ }
+
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private int getIntrinsicHeight(View view) {
if (view instanceof ExpandableView) {
@@ -2758,7 +2893,10 @@
updateAnimationState(child);
updateChronometerForChild(child);
if (child instanceof ExpandableNotificationRow) {
- ((ExpandableNotificationRow) child).setDismissRtl(mDismissRtl);
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ row.setDismissRtl(mDismissRtl);
+ row.setDismissUsingRowTranslationX(mDismissUsingRowTranslationX);
+
}
}
@@ -2807,7 +2945,16 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
void setExpandingNotification(ExpandableNotificationRow row) {
- mAmbientState.setExpandingNotification(row);
+ if (mExpandingNotificationRow != null && row == null) {
+ // Let's unset the clip path being set during launch
+ mExpandingNotificationRow.setExpandingClipPath(null);
+ ExpandableNotificationRow parent = mExpandingNotificationRow.getNotificationParent();
+ if (parent != null) {
+ parent.setExpandingClipPath(null);
+ }
+ }
+ mExpandingNotificationRow = row;
+ updateLaunchedNotificationClipPath();
requestChildrenUpdate();
}
@@ -2817,7 +2964,10 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void applyExpandAnimationParams(ExpandAnimationParameters params) {
- mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
+ // Modify the clipping for launching notifications
+ mLaunchAnimationParams = params;
+ setLaunchingNotification(params != null);
+ updateLaunchedNotificationClipPath();
requestChildrenUpdate();
}
@@ -2901,7 +3051,6 @@
mAnimationEvents.clear();
updateBackground();
updateViewShadows();
- updateClippingToTopRoundedCorner();
} else {
applyCurrentState();
}
@@ -3030,7 +3179,7 @@
removedTranslation = row.getTranslationWhenRemoved();
ignoreChildren = false;
}
- childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
+ childWasSwipedOut |= isFullySwipedOut(row);
} else if (child instanceof MediaHeaderView) {
childWasSwipedOut = true;
}
@@ -3038,11 +3187,11 @@
Rect clipBounds = child.getClipBounds();
childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
- if (childWasSwipedOut && child instanceof ExpandableView) {
+ if (childWasSwipedOut) {
// Clean up any potential transient views if the child has already been swiped
// out, as we won't be animating it further (due to its height already being
// clipped to 0.
- ViewGroup transientContainer = ((ExpandableView) child).getTransientContainer();
+ ViewGroup transientContainer = child.getTransientContainer();
if (transientContainer != null) {
transientContainer.removeTransientView(child);
}
@@ -3056,6 +3205,13 @@
ignoreChildren);
mAnimationEvents.add(event);
mSwipedOutViews.remove(child);
+ if (DEBUG_REMOVE_ANIMATION) {
+ String key = "";
+ if (child instanceof ExpandableNotificationRow) {
+ key = ((ExpandableNotificationRow) child).getEntry().getKey();
+ }
+ Log.d(TAG, "created Remove Event - SwipedOut: " + childWasSwipedOut + " " + key);
+ }
}
mChildrenToRemoveAnimated.clear();
}
@@ -3795,6 +3951,7 @@
updateNotificationAnimationStates();
updateChronometers();
requestChildrenUpdate();
+ updateUseRoundedRectClipping();
}
}
@@ -3815,6 +3972,10 @@
}
void onChildHeightChanged(ExpandableView view, boolean needsAnimation) {
+ boolean previouslyNeededAnimation = mAnimateStackYForContentHeightChange;
+ if (needsAnimation) {
+ mAnimateStackYForContentHeightChange = true;
+ }
updateContentHeight();
updateScrollPositionOnExpandInBottom(view);
clampScrollPosition();
@@ -3835,6 +3996,7 @@
requestAnimationOnViewResize(row);
}
requestChildrenUpdate();
+ mAnimateStackYForContentHeightChange = previouslyNeededAnimation;
}
void onChildHeightReset(ExpandableView view) {
@@ -4015,7 +4177,6 @@
setAnimationRunning(false);
updateBackground();
updateViewShadows();
- updateClippingToTopRoundedCorner();
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4048,7 +4209,7 @@
expandableView.setFakeShadowIntensity(
diff / FakeShadowView.SHADOW_SIBLING_TRESHOLD,
previous.getOutlineAlpha(), (int) yLocation,
- previous.getOutlineTranslation());
+ (int) (previous.getOutlineTranslation() + previous.getTranslation()));
}
previous = expandableView;
}
@@ -4550,6 +4711,7 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setQsExpansionFraction(float qsExpansionFraction) {
mQsExpansionFraction = qsExpansionFraction;
+ updateUseRoundedRectClipping();
// If notifications are scrolled,
// clear out scrollY by the time we push notifications offscreen
@@ -4560,13 +4722,18 @@
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void setOwnScrollY(int ownScrollY) {
+ setOwnScrollY(ownScrollY, false /* animateScrollChangeListener */);
+ }
+
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
+ private void setOwnScrollY(int ownScrollY, boolean animateStackYChangeListener) {
if (ownScrollY != mOwnScrollY) {
// We still want to call the normal scrolled changed for accessibility reasons
onScrollChanged(mScrollX, ownScrollY, mScrollX, mOwnScrollY);
mOwnScrollY = ownScrollY;
mAmbientState.setScrollY(mOwnScrollY);
updateOnScrollChange();
- updateStackPosition();
+ updateStackPosition(animateStackYChangeListener);
}
}
@@ -4632,6 +4799,7 @@
mStatusBarState = statusBarState;
mAmbientState.setStatusBarState(statusBarState);
updateSpeedBumpIndex();
+ updateDismissBehavior();
}
void onStatePostChange(boolean fromShadeLocked) {
@@ -5192,6 +5360,198 @@
}
/**
+ * Set rounded rect clipping bounds on this view.
+ */
+ public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
+ int bottomRadius) {
+ if (mRoundedRectClippingLeft == left && mRoundedRectClippingRight == right
+ && mRoundedRectClippingBottom == bottom && mRoundedRectClippingTop == top
+ && mBgCornerRadii[0] == topRadius && mBgCornerRadii[5] == bottomRadius) {
+ return;
+ }
+ mRoundedRectClippingLeft = left;
+ mRoundedRectClippingTop = top;
+ mRoundedRectClippingBottom = bottom;
+ mRoundedRectClippingRight = right;
+ mBgCornerRadii[0] = topRadius;
+ mBgCornerRadii[1] = topRadius;
+ mBgCornerRadii[2] = topRadius;
+ mBgCornerRadii[3] = topRadius;
+ mBgCornerRadii[4] = bottomRadius;
+ mBgCornerRadii[5] = bottomRadius;
+ mBgCornerRadii[6] = bottomRadius;
+ mBgCornerRadii[7] = bottomRadius;
+ mRoundedClipPath.reset();
+ mRoundedClipPath.addRoundRect(left, top, right, bottom, mBgCornerRadii, Path.Direction.CW);
+ if (mShouldUseRoundedRectClipping) {
+ invalidate();
+ }
+ }
+
+ private void updateSplitNotificationShade() {
+ boolean split = shouldUseSplitNotificationShade(mFeatureFlags, getResources());
+ if (split != mShouldUseSplitNotificationShade) {
+ mShouldUseSplitNotificationShade = split;
+ updateDismissBehavior();
+ updateUseRoundedRectClipping();
+ }
+ }
+
+ private void updateDismissBehavior() {
+ // On the split keyguard, dismissing with clipping without a visual boundary looks odd,
+ // so let's use the content dismiss behavior instead.
+ boolean dismissUsingRowTranslationX = !mShouldUseSplitNotificationShade
+ || mStatusBarState != StatusBarState.KEYGUARD;
+ if (mDismissUsingRowTranslationX != dismissUsingRowTranslationX) {
+ mDismissUsingRowTranslationX = dismissUsingRowTranslationX;
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child instanceof ExpandableNotificationRow) {
+ ((ExpandableNotificationRow) child).setDismissUsingRowTranslationX(
+ dismissUsingRowTranslationX);
+ }
+ }
+ }
+ }
+
+ /**
+ * Set if we're launching a notification right now.
+ */
+ private void setLaunchingNotification(boolean launching) {
+ if (launching == mLaunchingNotification) {
+ return;
+ }
+ mLaunchingNotification = launching;
+ mLaunchingNotificationNeedsToBeClipped = mLaunchAnimationParams != null
+ && (mLaunchAnimationParams.getStartRoundedTopClipping() > 0
+ || mLaunchAnimationParams.getParentStartRoundedTopClipping() > 0);
+ if (!mLaunchingNotificationNeedsToBeClipped || !mLaunchingNotification) {
+ mLaunchedNotificationClipPath.reset();
+ }
+ // When launching notifications, we're clipping the children individually instead of in
+ // dispatchDraw
+ invalidate();
+ }
+
+ /**
+ * Should we use rounded rect clipping
+ */
+ private void updateUseRoundedRectClipping() {
+ // We don't want to clip notifications when QS is expanded, because incoming heads up on
+ // the bottom would be clipped otherwise
+ boolean qsAllowsClipping = mQsExpansionFraction < 0.5f || mShouldUseSplitNotificationShade;
+ boolean clip = mIsExpanded && qsAllowsClipping;
+ if (clip != mShouldUseRoundedRectClipping) {
+ mShouldUseRoundedRectClipping = clip;
+ invalidate();
+ }
+ }
+
+ /**
+ * Update the clip path for launched notifications in case they were originally clipped
+ */
+ private void updateLaunchedNotificationClipPath() {
+ if (!mLaunchingNotificationNeedsToBeClipped || !mLaunchingNotification
+ || mExpandingNotificationRow == null) {
+ return;
+ }
+ int left = Math.min(mLaunchAnimationParams.getLeft(), mRoundedRectClippingLeft);
+ int right = Math.max(mLaunchAnimationParams.getRight(), mRoundedRectClippingRight);
+ int bottom = Math.max(mLaunchAnimationParams.getBottom(), mRoundedRectClippingBottom);
+ float expandProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+ mLaunchAnimationParams.getProgress(0,
+ NotificationLaunchAnimatorController.ANIMATION_DURATION_TOP_ROUNDING));
+ int top = (int) Math.min(MathUtils.lerp(mRoundedRectClippingTop,
+ mLaunchAnimationParams.getTop(), expandProgress),
+ mRoundedRectClippingTop);
+ float topRadius = mLaunchAnimationParams.getTopCornerRadius();
+ float bottomRadius = mLaunchAnimationParams.getBottomCornerRadius();
+ mLaunchedNotificationRadii[0] = topRadius;
+ mLaunchedNotificationRadii[1] = topRadius;
+ mLaunchedNotificationRadii[2] = topRadius;
+ mLaunchedNotificationRadii[3] = topRadius;
+ mLaunchedNotificationRadii[4] = bottomRadius;
+ mLaunchedNotificationRadii[5] = bottomRadius;
+ mLaunchedNotificationRadii[6] = bottomRadius;
+ mLaunchedNotificationRadii[7] = bottomRadius;
+ mLaunchedNotificationClipPath.reset();
+ mLaunchedNotificationClipPath.addRoundRect(left, top, right, bottom,
+ mLaunchedNotificationRadii, Path.Direction.CW);
+ // Offset into notification clip coordinates instead of parent ones.
+ // This is needed since the notification changes in translationZ, where clipping via
+ // canvas dispatching won't work.
+ ExpandableNotificationRow expandingRow = mExpandingNotificationRow;
+ if (expandingRow.getNotificationParent() != null) {
+ expandingRow = expandingRow.getNotificationParent();
+ }
+ mLaunchedNotificationClipPath.offset(
+ -expandingRow.getLeft() - expandingRow.getTranslationX(),
+ -expandingRow.getTop() - expandingRow.getTranslationY());
+ expandingRow.setExpandingClipPath(mLaunchedNotificationClipPath);
+ if (mShouldUseRoundedRectClipping) {
+ invalidate();
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ if (mShouldUseRoundedRectClipping && !mLaunchingNotification) {
+ // When launching notifications, we're clipping the children individually instead of in
+ // dispatchDraw
+ // Let's clip rounded.
+ canvas.clipPath(mRoundedClipPath);
+ }
+ super.dispatchDraw(canvas);
+ }
+
+ @Override
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ if (mShouldUseRoundedRectClipping && mLaunchingNotification) {
+ // Let's clip children individually during notification launch
+ canvas.save();
+ ExpandableView expandableView = (ExpandableView) child;
+ Path clipPath;
+ if (expandableView.isExpandAnimationRunning()
+ || ((ExpandableView) child).hasExpandingChild()) {
+ // When launching the notification, it is not clipped by this layout, but by the
+ // view itself. This is because the view is Translating in Z, where this clipPath
+ // wouldn't apply.
+ clipPath = null;
+ } else {
+ clipPath = mRoundedClipPath;
+ }
+ if (clipPath != null) {
+ canvas.clipPath(clipPath);
+ }
+ boolean result = super.drawChild(canvas, child, drawingTime);
+ canvas.restore();
+ return result;
+ } else {
+ return super.drawChild(canvas, child, drawingTime);
+ }
+ }
+
+ /**
+ * Calculate the total translation needed when dismissing.
+ */
+ public float getTotalTranslationLength(View animView) {
+ if (!mDismissUsingRowTranslationX) {
+ return animView.getMeasuredWidth();
+ }
+ float notificationWidth = animView.getMeasuredWidth();
+ int containerWidth = getMeasuredWidth();
+ float padding = (containerWidth - notificationWidth) / 2.0f;
+ return containerWidth - padding;
+ }
+
+ /**
+ * @return the start location where we start clipping notifications.
+ */
+ public int getTopClippingStartLocation() {
+ return mIsExpanded ? mQsScrollBoundaryPosition : 0;
+ }
+
+ /**
* A listener that is notified when the empty space below the notifications is clicked on
*/
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index dec9888..e71f7db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -384,6 +384,11 @@
}
@Override
+ public float getTotalTranslationLength(View animView) {
+ return mView.getTotalTranslationLength(animView);
+ }
+
+ @Override
public void onSnooze(StatusBarNotification sbn,
NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
@@ -822,8 +827,18 @@
return mView.isLayoutRtl();
}
+ /**
+ * @return the left of the view.
+ */
public int getLeft() {
- return mView.getLeft();
+ return mView.getLeft();
+ }
+
+ /**
+ * @return the top of the view.
+ */
+ public int getTop() {
+ return mView.getTop();
}
public float getTranslationX() {
@@ -1008,7 +1023,7 @@
mView.setQsExpansionFraction(expansionFraction);
}
- public void setOnStackYChanged(Runnable onStackYChanged) {
+ public void setOnStackYChanged(Consumer<Boolean> onStackYChanged) {
mView.setOnStackYChanged(onStackYChanged);
}
@@ -1056,10 +1071,6 @@
mView.setAlpha(alpha);
}
- public float getCurrentOverScrollAmount(boolean top) {
- return mView.getCurrentOverScrollAmount(top);
- }
-
public float calculateAppearFraction(float height) {
return mView.calculateAppearFraction(height);
}
@@ -1440,6 +1451,14 @@
}
/**
+ * Set rounded rect clipping bounds on this view.
+ */
+ public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
+ int bottomRadius) {
+ mView.setRoundedClippingBounds(left, top, right, bottom, topRadius, bottomRadius);
+ }
+
+ /**
* Enum for UiEvent logged from this class
*/
enum NotificationPanelEvent implements UiEventLogger.UiEventEnum {
@@ -1514,6 +1533,11 @@
}
@Override
+ public int getTopClippingStartLocation() {
+ return mView.getTopClippingStartLocation();
+ }
+
+ @Override
public View getContainerChildAt(int i) {
return mView.getContainerChildAt(i);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index f4c4d44..6647769 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -325,6 +325,11 @@
}
@Override
+ protected float getTotalTranslationLength(View animView) {
+ return mCallback.getTotalTranslationLength(animView);
+ }
+
+ @Override
public void setTranslation(View v, float translate) {
if (v instanceof SwipeableView) {
((SwipeableView) v).setTranslation(translate);
@@ -466,6 +471,13 @@
void onSnooze(StatusBarNotification sbn, SnoozeOption snoozeOption);
void onDismiss();
+
+ /**
+ * Get the total translation length where we want to swipe to when dismissing the view. By
+ * default this is the size of the view, but can also be larger.
+ * @param animView the view to ask about
+ */
+ float getTotalTranslationLength(View animView);
}
static class Builder {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index e5fd103..8f4a71c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -158,7 +158,7 @@
AmbientState ambientState) {
float drawStart = ambientState.isOnKeyguard() ? 0
: ambientState.getStackY() - ambientState.getScrollY();
- float clipStart = ambientState.getNotificationScrimTop();
+ float clipStart = 0;
int childCount = algorithmState.visibleChildren.size();
boolean firstHeadsUp = true;
for (int i = 0; i < childCount; i++) {
@@ -218,13 +218,7 @@
*/
private void initAlgorithmState(ViewGroup hostView, StackScrollAlgorithmState state,
AmbientState ambientState) {
- float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
- int scrollY = ambientState.getScrollY();
-
- // Due to the overScroller, the stackscroller can have negative scroll state. This is
- // already accounted for by the top padding and doesn't need an additional adaption
- scrollY = Math.max(0, scrollY);
- state.scrollY = (int) (scrollY + bottomOverScroll);
+ state.scrollY = ambientState.getScrollY();
state.mCurrentYPosition = -state.scrollY;
state.mCurrentExpandedYPosition = -state.scrollY;
@@ -261,7 +255,7 @@
// Save the index of first view in shelf from when shade is fully
// expanded. Consider updating these states in updateContentView instead so that we don't
// have to recalculate in every frame.
- float currentY = -scrollY;
+ float currentY = -ambientState.getScrollY();
if (!ambientState.isOnKeyguard()) {
currentY += mNotificationScrimPadding;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 4fd2064..ee12b4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -392,7 +392,7 @@
0, () -> removeTransientView(changingView), null);
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
- if (Math.abs(changingView.getTranslation()) == changingView.getWidth()
+ if (mHostLayout.isFullySwipedOut(changingView)
&& changingView.getTransientContainer() != null) {
changingView.getTransientContainer().removeTransientView(changingView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
index 54ef623..b148eeb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -27,6 +27,7 @@
private val listeners: MutableList<ConfigurationController.ConfigurationListener> = ArrayList()
private val lastConfig = Configuration()
private var density: Int = 0
+ private var smallestScreenWidth: Int = 0
private var fontScale: Float = 0.toFloat()
private val inCarMode: Boolean
private var uiMode: Int = 0
@@ -38,6 +39,7 @@
this.context = context
fontScale = currentConfig.fontScale
density = currentConfig.densityDpi
+ smallestScreenWidth = currentConfig.smallestScreenWidthDp
inCarMode = currentConfig.uiMode and Configuration.UI_MODE_TYPE_MASK ==
Configuration.UI_MODE_TYPE_CAR
uiMode = currentConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
@@ -72,6 +74,14 @@
this.fontScale = fontScale
}
+ val smallestScreenWidth = newConfig.smallestScreenWidthDp
+ if (smallestScreenWidth != this.smallestScreenWidth) {
+ this.smallestScreenWidth = smallestScreenWidth
+ listeners.filterForEach({ this.listeners.contains(it) }) {
+ it.onSmallestScreenWidthChanged()
+ }
+ }
+
val localeList = newConfig.locales
if (localeList != this.localeList) {
this.localeList = localeList
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 01d489f..c4d1abc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -63,6 +63,7 @@
private final Resources mResources;
private final BatteryController mBatteryController;
private final FeatureFlags mFeatureFlags;
+ private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private final Set<Callback> mCallbacks = new HashSet<>();
@@ -78,7 +79,8 @@
BatteryController batteryController,
TunerService tunerService,
DumpManager dumpManager,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ UnlockedScreenOffAnimationController unlockedScreenOffAnimationController) {
mResources = resources;
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mAlwaysOnPolicy = alwaysOnDisplayPolicy;
@@ -89,6 +91,7 @@
mPowerManager = powerManager;
mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation);
mFeatureFlags = featureFlags;
+ mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
tunerService.addTunable(
this,
@@ -220,7 +223,8 @@
* then abruptly showing AOD.
*/
public boolean shouldControlUnlockedScreenOff() {
- return getAlwaysOn() && mFeatureFlags.useNewLockscreenAnimations();
+ return getAlwaysOn() && mFeatureFlags.useNewLockscreenAnimations()
+ && mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation();
}
private boolean getBoolean(String propName, int resId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 3e4177d3..fa5011e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -40,9 +40,9 @@
private static float CLOCK_HEIGHT_WEIGHT = 0.7f;
/**
- * Margin between the bottom of the clock and the notification shade.
+ * Margin between the bottom of the status view and the notification shade.
*/
- private int mClockNotificationsMargin;
+ private int mStatusViewBottomMargin;
/**
* Height of the parent view - display size in px.
@@ -153,8 +153,8 @@
* Refreshes the dimension values.
*/
public void loadDimens(Resources res) {
- mClockNotificationsMargin = res.getDimensionPixelSize(
- R.dimen.keyguard_clock_notifications_margin);
+ mStatusViewBottomMargin = res.getDimensionPixelSize(
+ R.dimen.keyguard_status_view_bottom_margin);
mContainerTopPadding =
res.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) / 2;
@@ -181,7 +181,7 @@
mNotificationStackHeight = notificationStackHeight;
mPanelExpansion = panelExpansion;
mHeight = parentHeight;
- mKeyguardStatusHeight = keyguardStatusHeight;
+ mKeyguardStatusHeight = keyguardStatusHeight + mStatusViewBottomMargin;
mUserSwitchHeight = userSwitchHeight;
mUserSwitchPreferredY = userSwitchPreferredY;
mHasCustomClock = hasCustomClock;
@@ -221,40 +221,15 @@
public float getMinStackScrollerPadding() {
return mBypassEnabled ? mUnlockedStackScrollerPadding
- : mMinTopMargin + mKeyguardStatusHeight + mClockNotificationsMargin;
- }
-
- private int getMaxClockY() {
- return mHeight / 2 - mKeyguardStatusHeight - mClockNotificationsMargin;
+ : mMinTopMargin + mKeyguardStatusHeight;
}
private int getExpandedPreferredClockY() {
return mMinTopMargin + mUserSwitchHeight;
}
- /**
- * Vertically align the clock and the shade in the available space considering only
- * a percentage of the clock height defined by {@code CLOCK_HEIGHT_WEIGHT}.
- * @return Clock Y in pixels.
- */
- public int getExpandedClockPosition() {
- final int availableHeight = mMaxShadeBottom - mMinTopMargin;
- final int containerCenter = mMinTopMargin + availableHeight / 2;
-
- float y = containerCenter
- - (mKeyguardStatusHeight + mUserSwitchHeight) * CLOCK_HEIGHT_WEIGHT
- - mClockNotificationsMargin - mNotificationStackHeight / 2;
- if (y < mMinTopMargin) {
- y = mMinTopMargin;
- }
-
- // Don't allow the clock base to be under half of the screen
- final float maxClockY = getMaxClockY();
- if (y > maxClockY) {
- y = maxClockY;
- }
-
- return (int) y;
+ public int getLockscreenStatusViewHeight() {
+ return mKeyguardStatusHeight;
}
private int getClockY(float panelExpansion, float darkAmount) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index 68e2070..96276f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -38,7 +38,7 @@
* A view to show hints on Keyguard ("Swipe up to unlock", "Tap again to open").
*/
public class KeyguardIndicationTextView extends TextView {
- private static final long MSG_DURATION_MILLIS = 600;
+ private static final long MSG_DURATION_MILLIS = 1500;
private long mNextAnimationTime = 0;
private boolean mAnimationsEnabled = true;
private LinkedList<CharSequence> mMessages = new LinkedList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 6b864c9..41af80e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -602,6 +602,10 @@
updateAodIconColors();
}
+ public int getHeight() {
+ return mAodIcons == null ? 0 : mAodIcons.getHeight();
+ }
+
public void appearAodIcons() {
if (mAodIcons == null) {
return;
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 528827f..5d31786 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -36,9 +36,11 @@
import android.app.ActivityManager;
import android.app.Fragment;
import android.app.StatusBarManager;
+import android.content.ContentResolver;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
@@ -50,10 +52,12 @@
import android.graphics.drawable.Drawable;
import android.hardware.biometrics.BiometricSourceType;
import android.os.Bundle;
+import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserManager;
import android.os.VibrationEffect;
+import android.provider.Settings;
import android.util.Log;
import android.util.MathUtils;
import android.view.LayoutInflater;
@@ -210,6 +214,8 @@
new MyOnHeadsUpChangedListener();
private final HeightListener mHeightListener = new HeightListener();
private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
+ private final SettingsChangeObserver mSettingsChangeObserver;
+
@VisibleForTesting final StatusBarStateListener mStatusBarStateListener =
new StatusBarStateListener();
private final BiometricUnlockController mBiometricUnlockController;
@@ -594,6 +600,8 @@
private int mScreenCornerRadius;
private boolean mQSAnimatingHiddenFromCollapsed;
+ private final ContentResolver mContentResolver;
+
private final Executor mUiExecutor;
private final SecureSettings mSecureSettings;
@@ -635,6 +643,7 @@
@Inject
public NotificationPanelViewController(NotificationPanelView view,
@Main Resources resources,
+ @Main Handler handler,
LayoutInflater layoutInflater,
NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
DynamicPrivacyController dynamicPrivacyController,
@@ -678,6 +687,7 @@
TapAgainViewController tapAgainViewController,
NavigationModeController navigationModeController,
FragmentService fragmentService,
+ ContentResolver contentResolver,
QuickAccessWalletController quickAccessWalletController,
@Main Executor uiExecutor,
SecureSettings secureSettings,
@@ -704,15 +714,12 @@
mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory;
mDepthController = notificationShadeDepthController;
mFeatureFlags = featureFlags;
+ mContentResolver = contentResolver;
mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory;
mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory;
mQSDetailDisplayer = qsDetailDisplayer;
mFragmentService = fragmentService;
- mKeyguardUserSwitcherEnabled = mResources.getBoolean(
- com.android.internal.R.bool.config_keyguardUserSwitcher);
- mKeyguardQsUserSwitchEnabled =
- mKeyguardUserSwitcherEnabled && mResources.getBoolean(
- R.bool.config_keyguard_user_switch_opens_qs_details);
+ mSettingsChangeObserver = new SettingsChangeObserver(handler);
mShouldUseSplitNotificationShade =
Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources);
mView.setWillNotDraw(!DEBUG);
@@ -795,6 +802,7 @@
}
mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count);
+ updateUserSwitcherFlags();
onFinishInflate();
}
@@ -830,6 +838,7 @@
mNotificationStackScrollLayoutController.setOverscrollTopChangedListener(
mOnOverscrollTopChangedListener);
mNotificationStackScrollLayoutController.setOnScrollListener(this::onNotificationScrolled);
+ mNotificationStackScrollLayoutController.setOnStackYChanged(this::onStackYChanged);
mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener(
mOnEmptySpaceClickListener);
addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp);
@@ -1033,6 +1042,10 @@
view = mLayoutInflater.inflate(layoutId, mView, false);
mView.addView(view, index);
} else {
+ // Add the stub back so we can re-inflate it again if necessary
+ ViewStub stub = new ViewStub(mView.getContext(), layoutId);
+ stub.setId(stubId);
+ mView.addView(stub, index);
view = null;
}
} else if (enabled) {
@@ -1060,6 +1073,7 @@
updateResources();
// Re-inflate the keyguard user switcher group.
+ updateUserSwitcherFlags();
boolean isUserSwitcherEnabled = mUserManager.isUserSwitcherEnabled();
boolean showQsUserSwitch = mKeyguardQsUserSwitchEnabled && isUserSwitcherEnabled;
boolean showKeyguardUserSwitcher =
@@ -1259,7 +1273,7 @@
mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
expandedFraction,
totalHeight,
- mKeyguardStatusViewController.getHeight(),
+ mKeyguardStatusViewController.getLockscreenHeight(),
userIconHeight,
userSwitcherPreferredY, hasCustomClock(),
hasVisibleNotifications, darkamount, mOverStretchAmount,
@@ -1442,7 +1456,6 @@
private void setQsExpansionEnabled() {
mQsExpansionEnabled = mQsExpansionEnabledPolicy && mQsExpansionEnabledAmbient;
- Log.d(TAG, "Set qsExpansionEnabled: " + mQsExpansionEnabled);
if (mQs == null) return;
mQs.setHeaderClickable(mQsExpansionEnabled);
}
@@ -2198,15 +2211,17 @@
mDepthController.setQsPanelExpansion(qsExpansionFraction);
}
- private Runnable mOnStackYChanged = () -> {
+ private void onStackYChanged(boolean shouldAnimate) {
if (mQs != null) {
+ if (shouldAnimate) {
+ mAnimateNextNotificationBounds = true;
+ mNotificationBoundsAnimationDelay = 0;
+ }
setQSClippingBounds();
}
};
private void onNotificationScrolled(int newScrollPosition) {
- // Since this is an overscroller, sometimes the scrollY can be temporarily negative
- // (when overscrollng on the top and flinging). Let's
updateQSExpansionEnabledAmbient();
}
@@ -2228,14 +2243,13 @@
* and QS state.
*/
private void setQSClippingBounds() {
- int top = 0;
- int bottom = 0;
- int left = 0;
- int right = 0;
+ int top;
+ int bottom;
+ int left;
+ int right;
final int qsPanelBottomY = calculateQsBottomPosition(computeQsExpansionFraction());
- final boolean visible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0)
- && !mShouldUseSplitNotificationShade;
+ final boolean qsVisible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0);
if (!mShouldUseSplitNotificationShade) {
if (mTransitioningToFullShadeProgress > 0.0f) {
@@ -2244,7 +2258,6 @@
top = mTransitionToFullShadeQSPosition;
} else {
final float notificationTop = getQSEdgePosition();
- mAmbientState.setNotificationScrimTop(notificationTop);
top = (int) (isOnKeyguard() ? Math.min(qsPanelBottomY, notificationTop)
: notificationTop);
}
@@ -2252,8 +2265,7 @@
// notification bounds should take full screen width regardless of insets
left = 0;
right = getView().getRight() + mDisplayRightInset;
- } else if (qsPanelBottomY > 0) { // so bounds are empty on lockscreen
- mAmbientState.setNotificationScrimTop(mSplitShadeNotificationsTopPadding);
+ } else {
top = Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding);
bottom = mNotificationStackScrollLayoutController.getHeight();
left = mNotificationStackScrollLayoutController.getLeft();
@@ -2261,17 +2273,17 @@
}
// top should never be lower than bottom, otherwise it will be invisible.
top = Math.min(top, bottom);
- applyQSClippingBounds(left, top, right, bottom, visible);
+ applyQSClippingBounds(left, top, right, bottom, qsVisible);
}
private void applyQSClippingBounds(int left, int top, int right, int bottom,
- boolean visible) {
+ boolean qsVisible) {
if (!mAnimateNextNotificationBounds || mKeyguardStatusAreaClipBounds.isEmpty()) {
if (mQsClippingAnimation != null) {
// update the end position of the animator
mQsClippingAnimationEndBounds.set(left, top, right, bottom);
} else {
- applyQSClippingImmediately(left, top, right, bottom, visible);
+ applyQSClippingImmediately(left, top, right, bottom, qsVisible);
}
} else {
mQsClippingAnimationEndBounds.set(left, top, right, bottom);
@@ -2295,7 +2307,7 @@
int animBottom = (int) MathUtils.lerp(startBottom,
mQsClippingAnimationEndBounds.bottom, fraction);
applyQSClippingImmediately(animLeft, animTop, animRight, animBottom,
- visible /* visible */);
+ qsVisible /* qsVisible */);
});
mQsClippingAnimation.addListener(new AnimatorListenerAdapter() {
@Override
@@ -2310,7 +2322,7 @@
}
private void applyQSClippingImmediately(int left, int top, int right, int bottom,
- boolean visible) {
+ boolean qsVisible) {
// Fancy clipping for quick settings
int radius = mScrimCornerRadius;
int statusBarClipTop = 0;
@@ -2318,19 +2330,34 @@
if (!mShouldUseSplitNotificationShade) {
// The padding on this area is large enough that we can use a cheaper clipping strategy
mKeyguardStatusAreaClipBounds.set(left, top, right, bottom);
- clipStatusView = visible;
+ clipStatusView = qsVisible;
radius = (int) MathUtils.lerp(mScreenCornerRadius, mScrimCornerRadius,
Math.min(top / (float) mScrimCornerRadius, 1f));
statusBarClipTop = top - mKeyguardStatusBar.getTop();
}
if (mQs != null) {
- mQs.setFancyClipping(top, bottom, radius, visible);
+ mQs.setFancyClipping(top, bottom, radius, qsVisible
+ && !mShouldUseSplitNotificationShade);
}
mKeyguardStatusViewController.setClipBounds(
clipStatusView ? mKeyguardStatusAreaClipBounds : null);
- mScrimController.setNotificationsBounds(left, top, right, bottom);
+ if (!qsVisible && mShouldUseSplitNotificationShade) {
+ // On the lockscreen when qs isn't visible, we don't want the bounds of the shade to
+ // be visible, otherwise you can see the bounds once swiping up to see bouncer
+ mScrimController.setNotificationsBounds(0, 0, 0, 0);
+ } else {
+ mScrimController.setNotificationsBounds(left, top, right, bottom);
+ }
+
mScrimController.setScrimCornerRadius(radius);
mKeyguardStatusBar.setTopClipping(statusBarClipTop);
+ int nsslLeft = left - mNotificationStackScrollLayoutController.getLeft();
+ int nsslRight = right - mNotificationStackScrollLayoutController.getLeft();
+ int nsslTop = top - mNotificationStackScrollLayoutController.getTop();
+ int nsslBottom = bottom - mNotificationStackScrollLayoutController.getTop();
+ int bottomRadius = mShouldUseSplitNotificationShade ? radius : 0;
+ mNotificationStackScrollLayoutController.setRoundedClippingBounds(
+ nsslLeft, nsslTop, nsslRight, nsslBottom, radius, bottomRadius);
}
private float getQSEdgePosition() {
@@ -2654,14 +2681,6 @@
@Override
protected int getMaxPanelHeight() {
- if (mKeyguardBypassController.getBypassEnabled() && mBarState == KEYGUARD) {
- return getMaxPanelHeightBypass();
- } else {
- return getMaxPanelHeightNonBypass();
- }
- }
-
- private int getMaxPanelHeightNonBypass() {
int min = mStatusBarMinHeight;
if (!(mBarState == KEYGUARD)
&& mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0) {
@@ -2686,16 +2705,6 @@
return maxHeight;
}
- private int getMaxPanelHeightBypass() {
- int position =
- mClockPositionAlgorithm.getExpandedClockPosition()
- + mKeyguardStatusViewController.getHeight();
- if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0) {
- position += mShelfHeight / 2.0f + mDarkIconSize / 2.0f;
- }
- return position;
- }
-
public boolean isInSettings() {
return mQsExpanded;
}
@@ -2767,11 +2776,8 @@
maxHeight += mNotificationStackScrollLayoutController.getTopPaddingOverflow();
if (mBarState == KEYGUARD) {
- int
- minKeyguardPanelBottom =
- mClockPositionAlgorithm.getExpandedClockPosition()
- + mKeyguardStatusViewController.getHeight()
- + mNotificationStackScrollLayoutController.getIntrinsicContentHeight();
+ int minKeyguardPanelBottom = mClockPositionAlgorithm.getLockscreenStatusViewHeight()
+ + mNotificationStackScrollLayoutController.getIntrinsicContentHeight();
return Math.max(maxHeight, minKeyguardPanelBottom);
} else {
return maxHeight;
@@ -3321,9 +3327,8 @@
}
if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) {
// The expandedHeight is always the full panel Height when bypassing
- expandedHeight = getMaxPanelHeightNonBypass();
+ expandedHeight = getMaxPanelHeight();
}
- mNotificationStackScrollLayoutController.setOnStackYChanged(mOnStackYChanged);
mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight);
updateKeyguardBottomAreaAlpha();
updateBigClockAlpha();
@@ -3881,6 +3886,26 @@
return false;
}
+ private void updateUserSwitcherFlags() {
+ mKeyguardUserSwitcherEnabled = mResources.getBoolean(
+ com.android.internal.R.bool.config_keyguardUserSwitcher);
+ mKeyguardQsUserSwitchEnabled =
+ mKeyguardUserSwitcherEnabled && mResources.getBoolean(
+ R.bool.config_keyguard_user_switch_opens_qs_details);
+ }
+
+ private void registerSettingsChangeListener() {
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.USER_SWITCHER_ENABLED),
+ /* notifyForDescendants */ false,
+ mSettingsChangeObserver
+ );
+ }
+
+ private void unregisterSettingsChangeListener() {
+ mContentResolver.unregisterContentObserver(mSettingsChangeObserver);
+ }
+
private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener {
@Override
public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
@@ -4202,6 +4227,15 @@
}
@Override
+ public void onSmallestScreenWidthChanged() {
+ if (DEBUG) Log.d(TAG, "onSmallestScreenWidthChanged");
+
+ // Can affect multi-user switcher visibility as it depends on screen size by default:
+ // it is enabled only for devices with large screens (see config_keyguardUserSwitcher)
+ reInflateViews();
+ }
+
+ @Override
public void onOverlayChanged() {
if (DEBUG) Log.d(TAG, "onOverlayChanged");
reInflateViews();
@@ -4214,6 +4248,21 @@
}
}
+ private class SettingsChangeObserver extends ContentObserver {
+
+ SettingsChangeObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ if (DEBUG) Log.d(TAG, "onSettingsChanged");
+
+ // Can affect multi-user switcher visibility
+ reInflateViews();
+ }
+ }
+
private class StatusBarStateListener implements StateListener {
@Override
public void onStateChanged(int statusBarState) {
@@ -4222,7 +4271,7 @@
int oldState = mBarState;
boolean keyguardShowing = statusBarState == KEYGUARD;
- if (mUnlockedScreenOffAnimationController.shouldPlayScreenOffAnimation()
+ if (mDozeParameters.shouldControlUnlockedScreenOff()
&& oldState == StatusBarState.SHADE
&& statusBarState == KEYGUARD) {
// This means we're doing the screen off animation - position the keyguard status
@@ -4329,10 +4378,12 @@
mConfigurationListener.onThemeChanged();
mFalsingManager.addTapListener(mFalsingTapListener);
mKeyguardIndicationController.init();
+ registerSettingsChangeListener();
}
@Override
public void onViewDetachedFromWindow(View v) {
+ unregisterSettingsChangeListener();
mFragmentService.getFragmentHostManager(mView)
.removeTagListener(QS.TAG, mFragmentListener);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 52f9aca..c958796 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -26,11 +26,9 @@
import android.app.IActivityManager;
import android.content.Context;
import android.content.pm.ActivityInfo;
-import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.os.Binder;
import android.os.RemoteException;
-import android.os.SystemProperties;
import android.os.Trace;
import android.util.Log;
import android.view.Display;
@@ -53,6 +51,7 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.google.android.collect.Lists;
@@ -108,12 +107,14 @@
StatusBarStateController statusBarStateController,
ConfigurationController configurationController,
KeyguardViewMediator keyguardViewMediator,
- KeyguardBypassController keyguardBypassController, SysuiColorExtractor colorExtractor,
- DumpManager dumpManager) {
+ KeyguardBypassController keyguardBypassController,
+ SysuiColorExtractor colorExtractor,
+ DumpManager dumpManager,
+ KeyguardStateController keyguardStateController) {
mContext = context;
mWindowManager = windowManager;
mActivityManager = activityManager;
- mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
+ mKeyguardScreenRotation = keyguardStateController.isKeyguardScreenRotationAllowed();
mDozeParameters = dozeParameters;
mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze();
mLpChanged = new LayoutParams();
@@ -173,12 +174,6 @@
}
}
- private boolean shouldEnableKeyguardScreenRotation() {
- Resources res = mContext.getResources();
- return SystemProperties.getBoolean("lockscreen.rot_override", false)
- || res.getBoolean(R.bool.config_enableLockScreenRotation);
- }
-
/**
* Adds the notification shade view to the window manager.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index 1cb0be0..ed8fb31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -47,6 +47,8 @@
private View mKeyguardStatusBar;
private boolean mQsExpanded;
private boolean mCustomizerAnimating;
+ private boolean mCustomizing;
+ private boolean mDetailShowing;
private int mBottomPadding;
private int mStackScrollerMargin;
@@ -140,7 +142,18 @@
}
public void setCustomizerShowing(boolean isShowing) {
- if (isShowing) {
+ mCustomizing = isShowing;
+ updateBottomMargin();
+ mStackScroller.setQsCustomizerShowing(isShowing);
+ }
+
+ public void setDetailShowing(boolean isShowing) {
+ mDetailShowing = isShowing;
+ updateBottomMargin();
+ }
+
+ private void updateBottomMargin() {
+ if (mCustomizing || mDetailShowing) {
// Clear out bottom paddings/margins so the qs customization can be full height.
setPadding(0, 0, 0, 0);
setBottomMargin(mStackScroller, 0);
@@ -148,7 +161,6 @@
setPadding(0, 0, 0, mBottomPadding);
setBottomMargin(mStackScroller, mStackScrollerMargin);
}
- mStackScroller.setQsCustomizerShowing(isShowing);
}
private void setBottomMargin(View v, int bottomMargin) {
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 7a1e5cf..cfcea96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -551,11 +551,11 @@
*/
public void setNotificationsBounds(float left, float top, float right, float bottom) {
if (mClipsQsScrim) {
- // notification scrim's rounded corners are anti-aliased, but clipping of the QS scrim
- // can't be and it's causing jagged corners. That's why notification scrim needs
- // to overlap QS scrim by one pixel - both vertically (top - 1) and
- // horizontally (left - 1 and right + 1), see: b/186644628
- mNotificationsScrim.setDrawableBounds(left - 1, top - 1, right + 1, bottom);
+ // notification scrim's rounded corners are anti-aliased, but clipping of the QS/behind
+ // scrim can't be and it's causing jagged corners. That's why notification scrim needs
+ // to overlap QS scrim by one pixel horizontally (left - 1 and right + 1)
+ // see: b/186644628
+ mNotificationsScrim.setDrawableBounds(left - 1, top, right + 1, bottom);
mScrimBehind.setBottomEdgePosition((int) top);
} else {
mNotificationsScrim.setDrawableBounds(left, top, right, bottom);
@@ -1242,6 +1242,9 @@
pw.println(mDefaultScrimAlpha);
pw.print(" mExpansionFraction=");
pw.println(mPanelExpansion);
+
+ pw.print(" mState.getMaxLightRevealScrimAlpha=");
+ pw.println(mState.getMaxLightRevealScrimAlpha());
}
public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index e52e1fa..0681193 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -197,7 +197,7 @@
}
@Override
- public float getBehindAlpha() {
+ public float getMaxLightRevealScrimAlpha() {
return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f;
}
@@ -220,18 +220,11 @@
mBlankScreen = mDisplayRequiresBlanking;
mAnimationDuration = mWakeLockScreenSensorActive
? ScrimController.ANIMATION_DURATION_LONG : ScrimController.ANIMATION_DURATION;
-
- // Wake sensor will show the wallpaper, let's fade from black. Otherwise it will
- // feel like the screen is flashing if the wallpaper is light.
- if (mWakeLockScreenSensorActive && previousState == AOD) {
- updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK);
- }
}
-
@Override
- public float getBehindAlpha() {
+ public float getMaxLightRevealScrimAlpha() {
return mWakeLockScreenSensorActive ? ScrimController.WAKE_SENSOR_SCRIM_ALPHA
- : AOD.getBehindAlpha();
+ : AOD.getMaxLightRevealScrimAlpha();
}
},
@@ -351,6 +344,10 @@
return mBehindAlpha;
}
+ public float getMaxLightRevealScrimAlpha() {
+ return 1f;
+ }
+
public float getNotifAlpha() {
return mNotifAlpha;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index e358756..53394c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -21,7 +21,6 @@
import static android.app.StatusBarManager.WindowType;
import static android.app.StatusBarManager.WindowVisibleState;
import static android.app.StatusBarManager.windowStateToString;
-import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.InsetsState.containsType;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
@@ -46,8 +45,6 @@
import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
import static com.android.wm.shell.bubbles.BubbleController.TASKBAR_CHANGED_BROADCAST;
-import android.animation.ValueAnimator;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -121,6 +118,7 @@
import android.view.accessibility.AccessibilityManager;
import android.widget.DateTimeView;
+import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
@@ -402,8 +400,6 @@
private LightRevealScrim mLightRevealScrim;
private WiredChargingRippleController mChargingRippleAnimationController;
private PowerButtonReveal mPowerButtonReveal;
- private CircleReveal mCircleReveal;
- private ValueAnimator mCircleRevealAnimator = ValueAnimator.ofFloat(0f, 1f);
private final Object mQueueLock = new Object();
@@ -1477,9 +1473,7 @@
* @param why the reason for the wake up
*/
public void wakeUpIfDozing(long time, View where, String why) {
- if (mDozing && !(mKeyguardViewMediator.isAnimatingScreenOff()
- || mUnlockedScreenOffAnimationController
- .isScreenOffLightRevealAnimationPlaying())) {
+ if (mDozing && !mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()) {
mPowerManager.wakeUp(
time, PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:" + why);
mWakeUpComingFromTouch = true;
@@ -2118,8 +2112,8 @@
}
@Override
- public void disableKeyguardBlurs() {
- mMainThreadHandler.post(mKeyguardViewMediator::disableBlursUntilHidden);
+ public void setBlursDisabledForAppLaunch(boolean disabled) {
+ mKeyguardViewMediator.setBlursDisabledForAppLaunch(disabled);
}
public boolean isDeviceInVrMode() {
@@ -2778,9 +2772,14 @@
+ String.valueOf(CameraIntents.getOverrideCameraPackage(mContext)));
}
- public static void dumpBarTransitions(PrintWriter pw, String var, BarTransitions transitions) {
+ public static void dumpBarTransitions(
+ PrintWriter pw, String var, @Nullable BarTransitions transitions) {
pw.print(" "); pw.print(var); pw.print(".BarTransitions.mMode=");
- pw.println(BarTransitions.modeToString(transitions.getMode()));
+ if (transitions != null) {
+ pw.println(BarTransitions.modeToString(transitions.getMode()));
+ } else {
+ pw.println("Unknown");
+ }
}
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
@@ -2803,11 +2802,11 @@
return mDisplayMetrics.density;
}
- float getDisplayWidth() {
+ public float getDisplayWidth() {
return mDisplayMetrics.widthPixels;
}
- float getDisplayHeight() {
+ public float getDisplayHeight() {
return mDisplayMetrics.heightPixels;
}
@@ -3428,6 +3427,10 @@
}
boolean updateIsKeyguard() {
+ return updateIsKeyguard(false /* force */);
+ }
+
+ boolean updateIsKeyguard(boolean force) {
boolean wakeAndUnlocking = mBiometricUnlockController.getMode()
== BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
@@ -3451,7 +3454,7 @@
showKeyguardImpl();
}
} else {
- return hideKeyguardImpl();
+ return hideKeyguardImpl(force);
}
return false;
}
@@ -3533,9 +3536,6 @@
public void fadeKeyguardWhilePulsing() {
mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING,
()-> {
- if (shouldShowCircleReveal()) {
- startCircleReveal();
- }
hideKeyguard();
mStatusBarKeyguardViewManager.onKeyguardFadedAway();
}).start();
@@ -3580,11 +3580,11 @@
/**
* @return true if we would like to stay in the shade, false if it should go away entirely
*/
- public boolean hideKeyguardImpl() {
+ public boolean hideKeyguardImpl(boolean force) {
mIsKeyguard = false;
Trace.beginSection("StatusBar#hideKeyguard");
boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
- if (!(mStatusBarStateController.setState(StatusBarState.SHADE))) {
+ if (!(mStatusBarStateController.setState(StatusBarState.SHADE, force))) {
//TODO: StatusBarStateController should probably know about hiding the keyguard and
// notify listeners.
@@ -3876,7 +3876,7 @@
@Override
public void onDozeAmountChanged(float linear, float eased) {
if (mFeatureFlags.useNewLockscreenAnimations()
- && !mCircleRevealAnimator.isRunning()) {
+ && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
mLightRevealScrim.setRevealAmount(1f - linear);
}
}
@@ -3899,7 +3899,7 @@
|| (!isDozing && mWakefulnessLifecycle.getLastWakeReason()
== PowerManager.WAKE_REASON_POWER_BUTTON)) {
mLightRevealScrim.setRevealEffect(mPowerButtonReveal);
- } else if (!mCircleRevealAnimator.isRunning()) {
+ } else if (!(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE);
}
@@ -3911,36 +3911,8 @@
Trace.endSection();
}
- /**
- * Update the parameters for the dozing circle reveal that animates when the user authenticates
- * from AOD using the fingerprint sensor.
- */
- public void updateCircleReveal() {
- final PointF fpLocation = mAuthRippleController.getFingerprintSensorLocation();
- if (fpLocation != null) {
- mCircleReveal =
- new CircleReveal(
- fpLocation.x,
- fpLocation.y,
- 0,
- Math.max(Math.max(fpLocation.x, getDisplayWidth() - fpLocation.x),
- Math.max(fpLocation.y, getDisplayHeight() - fpLocation.y)));
- }
- }
-
- private void startCircleReveal() {
- mLightRevealScrim.setRevealEffect(mCircleReveal);
- mCircleRevealAnimator.cancel();
- mCircleRevealAnimator.addUpdateListener(animation ->
- mLightRevealScrim.setRevealAmount(
- (float) mCircleRevealAnimator.getAnimatedValue()));
- mCircleRevealAnimator.setDuration(900);
- mCircleRevealAnimator.start();
- }
-
- private boolean shouldShowCircleReveal() {
- return mCircleReveal != null && !mCircleRevealAnimator.isRunning()
- && mBiometricUnlockController.getBiometricType() == FINGERPRINT;
+ public LightRevealScrim getLightRevealScrim() {
+ return mLightRevealScrim;
}
private void updateKeyguardState() {
@@ -4080,7 +4052,7 @@
// The screen off animation uses our LightRevealScrim - we need to be expanded for it to
// be visible.
- if (mUnlockedScreenOffAnimationController.shouldPlayScreenOffAnimation()) {
+ if (mDozeParameters.shouldControlUnlockedScreenOff()) {
makeExpandedVisible(true);
}
@@ -4470,6 +4442,8 @@
} else {
mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
}
+ updateLightRevealScrimVisibility();
+
Trace.endSection();
}
@@ -4920,6 +4894,7 @@
return;
}
+ mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha());
if (mFeatureFlags.useNewLockscreenAnimations()
&& (mDozeParameters.getAlwaysOn() || mDozeParameters.isQuickPickupEnabled())) {
mLightRevealScrim.setVisibility(View.VISIBLE);
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 2601b8b..e846399 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -899,6 +899,9 @@
};
protected void updateStates() {
+ if (mContainer == null ) {
+ return;
+ }
int vis = mContainer.getSystemUiVisibility();
boolean showing = mShowing;
boolean occluded = mOccluded;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index f6e0f51..98b9cc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -41,6 +41,7 @@
import android.util.EventLog;
import android.view.View;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.widget.LockPatternUtils;
@@ -508,9 +509,12 @@
tsb.addNextIntent(intent);
}
+ ActivityLaunchAnimator.Controller viewController =
+ ActivityLaunchAnimator.Controller.fromView(view,
+ InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON
+ );
ActivityLaunchAnimator.Controller animationController =
- new StatusBarLaunchAnimatorController(
- ActivityLaunchAnimator.Controller.fromView(view), mStatusBar,
+ new StatusBarLaunchAnimatorController(viewController, mStatusBar,
true /* isActivityIntent */);
mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index e135cc5..9a04d39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -3,6 +3,8 @@
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
+import android.content.Context
+import android.content.res.Configuration
import android.os.Handler
import android.view.View
import com.android.systemui.animation.Interpolators
@@ -16,6 +18,7 @@
import com.android.systemui.statusbar.notification.PropertyAnimator
import com.android.systemui.statusbar.notification.stack.AnimationProperties
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
+import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
/**
@@ -38,10 +41,11 @@
*/
@SysUISingleton
class UnlockedScreenOffAnimationController @Inject constructor(
+ private val context: Context,
private val wakefulnessLifecycle: WakefulnessLifecycle,
private val statusBarStateControllerImpl: StatusBarStateControllerImpl,
private val keyguardViewMediatorLazy: dagger.Lazy<KeyguardViewMediator>,
- private val dozeParameters: DozeParameters
+ private val keyguardStateController: KeyguardStateController
) : WakefulnessLifecycle.Observer {
private val handler = Handler()
@@ -131,13 +135,18 @@
lightRevealAnimationPlaying = false
aodUiAnimationPlaying = false
- // Make sure the status bar is in the correct keyguard state, since we might have left it in
- // the KEYGUARD state if this wakeup cancelled the screen off animation.
- statusBar.updateIsKeyguard()
+ // Make sure the status bar is in the correct keyguard state, forcing it if necessary. This
+ // is required if the screen off animation is cancelled, since it might be incorrectly left
+ // in the KEYGUARD or SHADE states depending on when it was cancelled and whether 'lock
+ // instantly' is enabled. We need to force it so that the state is set even if we're going
+ // from SHADE to SHADE or KEYGUARD to KEYGUARD, since we might have changed parts of the UI
+ // (such as showing AOD in the shade) without actually changing the StatusBarState. This
+ // ensures that the UI definitely reflects the desired state.
+ statusBar.updateIsKeyguard(true /* force */)
}
override fun onStartedGoingToSleep() {
- if (shouldPlayScreenOffAnimation()) {
+ if (shouldPlayUnlockedScreenOffAnimation()) {
lightRevealAnimationPlaying = true
lightRevealAnimator.start()
@@ -151,13 +160,32 @@
}
/**
- * Whether we should play the screen off animation when the phone starts going to sleep. We can
- * do that if dozeParameters says we can control the unlocked screen off animation and we are in
- * the SHADE state. If we're in KEYGUARD or SHADE_LOCKED, the regular
+ * Whether we want to play the screen off animation when the phone starts going to sleep, based
+ * on the current state of the device.
*/
- fun shouldPlayScreenOffAnimation(): Boolean {
- return dozeParameters.shouldControlUnlockedScreenOff() &&
- statusBarStateControllerImpl.state == StatusBarState.SHADE
+ fun shouldPlayUnlockedScreenOffAnimation(): Boolean {
+ // We only play the unlocked screen off animation if we are... unlocked.
+ if (statusBarStateControllerImpl.state != StatusBarState.SHADE) {
+ return false
+ }
+
+ // We currently draw both the light reveal scrim, and the AOD UI, in the shade. If it's
+ // already expanded and showing notifications/QS, the animation looks really messy. For now,
+ // disable it if the notification panel is expanded.
+ if (!this::statusBar.isInitialized ||
+ statusBar.notificationPanelViewController.isFullyExpanded) {
+ return false
+ }
+
+ // If we're not allowed to rotate the keyguard, then only do the screen off animation if
+ // we're in portrait. Otherwise, AOD will animate in sideways, which looks weird.
+ if (!keyguardStateController.isKeyguardScreenRotationAllowed &&
+ context.resources.configuration.orientation != Configuration.ORIENTATION_PORTRAIT) {
+ return false
+ }
+
+ // Otherwise, good to go.
+ return true
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index ef7fac3..d5965ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -25,6 +25,7 @@
import android.util.Log
import android.view.View
import android.widget.Chronometer
+import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
@@ -84,7 +85,7 @@
val newOngoingCallInfo = CallNotificationInfo(
entry.sbn.key,
entry.sbn.notification.`when`,
- entry.sbn.notification.contentIntent.intent,
+ entry.sbn.notification.contentIntent?.intent,
entry.sbn.uid,
entry.sbn.notification.extras.getInt(
Notification.EXTRA_CALL_TYPE, -1) == CALL_TYPE_ONGOING
@@ -175,11 +176,17 @@
systemClock.elapsedRealtime()
timeView.start()
- currentChipView.setOnClickListener {
- logger.logChipClicked()
- activityStarter.postStartActivityDismissingKeyguard(
- currentCallNotificationInfo.intent, 0,
- ActivityLaunchAnimator.Controller.fromView(backgroundView))
+ currentCallNotificationInfo.intent?.let { intent ->
+ currentChipView.setOnClickListener {
+ logger.logChipClicked()
+ activityStarter.postStartActivityDismissingKeyguard(
+ intent,
+ 0,
+ ActivityLaunchAnimator.Controller.fromView(
+ backgroundView,
+ InteractionJankMonitor.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP)
+ )
+ }
}
setUpUidObserver(currentCallNotificationInfo)
@@ -250,7 +257,7 @@
private data class CallNotificationInfo(
val key: String,
val callStartTime: Long,
- val intent: Intent,
+ val intent: Intent?,
val uid: Int,
/** True if the call is currently ongoing (as opposed to incoming, screening, etc.). */
val isOngoing: Boolean
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index 399c850..a0edc7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -74,11 +74,13 @@
mBrightnessMirror.setVisibility(View.VISIBLE);
mVisibilityCallback.accept(true);
mNotificationPanel.setPanelAlpha(0, true /* animate */);
+ mDepthController.setBrightnessMirrorVisible(true);
}
public void hideMirror() {
mVisibilityCallback.accept(false);
mNotificationPanel.setPanelAlpha(255, true /* animate */);
+ mDepthController.setBrightnessMirrorVisible(false);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
index 0a6cf7b..c2bd87c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
@@ -33,6 +33,7 @@
interface ConfigurationListener {
default void onConfigChanged(Configuration newConfig) {}
default void onDensityOrFontScaleChanged() {}
+ default void onSmallestScreenWidthChanged() {}
default void onOverlayChanged() {}
default void onUiModeChanged() {}
default void onThemeChanged() {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
index af7bf95..fcfc967 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -57,6 +57,11 @@
boolean canPerformSmartSpaceTransition();
/**
+ * Whether the keyguard is allowed to rotate, or needs to be locked to the default orientation.
+ */
+ boolean isKeyguardScreenRotationAllowed();
+
+ /**
* If the device has PIN/pattern/password or a lock screen at all.
*/
boolean isMethodSecure();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 0945a3f..64750bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -23,6 +23,7 @@
import android.content.IntentFilter;
import android.hardware.biometrics.BiometricSourceType;
import android.os.Build;
+import android.os.SystemProperties;
import android.os.Trace;
import androidx.annotation.VisibleForTesting;
@@ -31,6 +32,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dumpable;
+import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
@@ -50,6 +52,7 @@
private static final String AUTH_BROADCAST_KEY = "debug_trigger_auth";
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+ private final Context mContext;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final LockPatternUtils mLockPatternUtils;
private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
@@ -100,6 +103,7 @@
public KeyguardStateControllerImpl(Context context,
KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils,
SmartspaceTransitionController smartspaceTransitionController) {
+ mContext = context;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
@@ -243,6 +247,12 @@
}
@Override
+ public boolean isKeyguardScreenRotationAllowed() {
+ return SystemProperties.getBoolean("lockscreen.rot_override", false)
+ || mContext.getResources().getBoolean(R.bool.config_enableLockScreenRotation);
+ }
+
+ @Override
public boolean isFaceAuthEnabled() {
return mFaceAuthEnabled;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index ce08075..2ac5c1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -387,15 +387,8 @@
int qsTypeIcon = 0;
IconState qsIcon = null;
CharSequence description = null;
- // Mobile icon will only be shown in the statusbar in 2 scenarios
- // 1. Mobile is the default network, and it is validated
- // 2. Mobile is the default network, it is not validated and there is no other
- // non-Carrier WiFi networks available.
- boolean maybeShowIcons = (mCurrentState.inetCondition == 1)
- || (mCurrentState.inetCondition == 0
- && !mNetworkController.isNonCarrierWifiNetworkAvailable());
// Only send data sim callbacks to QS.
- if (mCurrentState.dataSim && mCurrentState.isDefault && maybeShowIcons) {
+ if (mCurrentState.dataSim && mCurrentState.isDefault) {
qsTypeIcon =
(showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.qsDataType : 0;
qsIcon = new IconState(mCurrentState.enabled
@@ -408,7 +401,7 @@
boolean activityOut = mCurrentState.dataConnected
&& !mCurrentState.carrierNetworkChangeMode
&& mCurrentState.activityOut;
- showDataIcon &= mCurrentState.dataSim && mCurrentState.isDefault && maybeShowIcons;
+ showDataIcon &= mCurrentState.dataSim && mCurrentState.isDefault;
boolean showTriangle = showDataIcon && !mCurrentState.airplaneMode;
int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
showDataIcon |= mCurrentState.roaming;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index efeeac6..e6c4e82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -336,7 +336,7 @@
RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent,
results);
- mEntry.remoteInputText = mEditText.getText();
+ mEntry.remoteInputText = mEditText.getText().toString();
// TODO(b/188646667): store attachment to entry
mEntry.remoteInputUri = null;
mEntry.remoteInputMimeType = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 5c44017..14190d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -21,6 +21,7 @@
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.Dialog;
import android.app.IActivityTaskManager;
@@ -48,6 +49,7 @@
import android.util.SparseBooleanArray;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowManagerGlobal;
import android.widget.BaseAdapter;
import com.android.internal.annotations.VisibleForTesting;
@@ -63,6 +65,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.qs.QSUserSwitcherEvent;
@@ -76,6 +79,8 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
import javax.inject.Provider;
@@ -123,23 +128,35 @@
private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
private final UiEventLogger mUiEventLogger;
public final DetailAdapter mUserDetailAdapter;
+ private final Executor mUiBgExecutor;
+ private final boolean mGuestUserAutoCreated;
+ private final AtomicBoolean mGuestCreationScheduled;
@Inject
- public UserSwitcherController(Context context, KeyguardStateController keyguardStateController,
- @Main Handler handler, ActivityStarter activityStarter,
- BroadcastDispatcher broadcastDispatcher, UiEventLogger uiEventLogger,
+ public UserSwitcherController(Context context,
+ KeyguardStateController keyguardStateController,
+ @Main Handler handler,
+ ActivityStarter activityStarter,
+ BroadcastDispatcher broadcastDispatcher,
+ UiEventLogger uiEventLogger,
TelephonyListenerManager telephonyListenerManager,
- IActivityTaskManager activityTaskManager, UserDetailAdapter userDetailAdapter) {
+ IActivityTaskManager activityTaskManager,
+ UserDetailAdapter userDetailAdapter,
+ @UiBackground Executor uiBgExecutor) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mTelephonyListenerManager = telephonyListenerManager;
mActivityTaskManager = activityTaskManager;
mUiEventLogger = uiEventLogger;
- mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(mUiEventLogger);
+ mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(this, mUiEventLogger);
mUserDetailAdapter = userDetailAdapter;
+ mUiBgExecutor = uiBgExecutor;
if (!UserManager.isGuestUserEphemeral()) {
mGuestResumeSessionReceiver.register(mBroadcastDispatcher);
}
+ mGuestUserAutoCreated = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_guestUserAutoCreated);
+ mGuestCreationScheduled = new AtomicBoolean();
mKeyguardStateController = keyguardStateController;
mHandler = handler;
mActivityStarter = activityStarter;
@@ -379,21 +396,13 @@
int id;
if (record.isGuest && record.info == null) {
// No guest user. Create one.
- UserInfo guest;
- try {
- guest = mUserManager.createGuest(mContext,
- mContext.getString(com.android.settingslib.R.string.guest_nickname));
- } catch (UserManager.UserOperationException e) {
- Log.e(TAG, "Couldn't create guest user", e);
- return;
- }
- if (guest == null) {
- // Couldn't create guest, most likely because there already exists one, we just
- // haven't reloaded the user list yet.
+ int guestId = createGuest();
+ if (guestId == UserHandle.USER_NULL) {
+ // This may happen if we haven't reloaded the user list yet.
return;
}
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_ADD);
- id = guest.id;
+ id = guestId;
} else if (record.isAddUser) {
showAddUserDialog();
return;
@@ -457,11 +466,6 @@
mAddUserDialog.show();
}
- protected void exitGuest(int id, int targetId) {
- switchToUserId(targetId);
- mUserManager.removeUser(id);
- }
-
private void listenForCallState() {
mTelephonyListenerManager.addCallStateListener(mPhoneStateListener);
}
@@ -576,6 +580,7 @@
pw.print(" "); pw.println(u.toString());
}
pw.println("mSimpleUserSwitcher=" + mSimpleUserSwitcher);
+ pw.println("mGuestUserAutoCreated=" + mGuestUserAutoCreated);
}
/** Returns the name of the current user of the phone. */
@@ -602,6 +607,126 @@
return mUsers;
}
+ /**
+ * Removes guest user and switches to target user. The guest must be the current user and its id
+ * must be {@code guestUserId}.
+ *
+ * <p>If {@code targetUserId} is {@link UserHandle.USER_NULL}, then create a new guest user in
+ * the foreground, and immediately switch to it. This is used for wiping the current guest and
+ * replacing it with a new one.
+ *
+ * <p>If {@code targetUserId} is specified, then remove the guest in the background while
+ * switching to {@code targetUserId}.
+ *
+ * <p>If device is configured with {@link
+ * com.android.internal.R.bool.config_guestUserAutoCreated}, then after guest user is removed, a
+ * new one is created in the background. This has no effect if {@code targetUserId} is {@link
+ * UserHandle.USER_NULL}.
+ *
+ * @param guestUserId id of the guest user to remove
+ * @param targetUserId id of the user to switch to after guest is removed. If {@link
+ * UserHandle.USER_NULL}, then switch immediately to the newly created guest user.
+ */
+ public void removeGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId) {
+ UserInfo currentUser;
+ try {
+ currentUser = ActivityManager.getService().getCurrentUser();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't remove guest because ActivityManager is dead");
+ return;
+ }
+ if (currentUser.id != guestUserId) {
+ Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")"
+ + " is not current user (" + currentUser.id + ")");
+ return;
+ }
+ if (!currentUser.isGuest()) {
+ Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")"
+ + " is not a guest");
+ return;
+ }
+
+ boolean marked = mUserManager.markGuestForDeletion(currentUser.id);
+ if (!marked) {
+ Log.w(TAG, "Couldn't mark the guest for deletion for user " + guestUserId);
+ return;
+ }
+
+ try {
+ if (targetUserId == UserHandle.USER_NULL) {
+ // Create a new guest in the foreground, and then immediately switch to it
+ int newGuestId = createGuest();
+ if (newGuestId == UserHandle.USER_NULL) {
+ Log.e(TAG, "Could not create new guest, switching back to system user");
+ switchToUserId(UserHandle.USER_SYSTEM);
+ mUserManager.removeUser(currentUser.id);
+ WindowManagerGlobal.getWindowManagerService().lockNow(/* options= */ null);
+ return;
+ }
+ switchToUserId(newGuestId);
+ mUserManager.removeUser(currentUser.id);
+ } else {
+ if (mGuestUserAutoCreated) {
+ // TODO(b/191067027): Move guest recreation to system_server
+ scheduleGuestCreation();
+ }
+ switchToUserId(targetUserId);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't remove guest because ActivityManager or WindowManager is dead");
+ return;
+ }
+ }
+
+ private void scheduleGuestCreation() {
+ if (!mGuestCreationScheduled.compareAndSet(false, true)) {
+ return;
+ }
+
+ mUiBgExecutor.execute(() -> {
+ int newGuestId = createGuest();
+ if (newGuestId == UserHandle.USER_NULL) {
+ Log.w(TAG, "Could not create new guest while exiting existing guest");
+ }
+ mGuestCreationScheduled.set(false);
+ });
+
+ }
+
+ /**
+ * If there is no guest on the device, schedule creation of a new guest user in the background.
+ */
+ public void guaranteeGuestPresent() {
+ if (mUserManager.findCurrentGuestUser() == null) {
+ scheduleGuestCreation();
+ }
+ }
+
+ /**
+ * Creates a guest user and return its multi-user user ID.
+ *
+ * This method does not check if a guest already exists before it makes a call to
+ * {@link UserManager} to create a new one.
+ *
+ * @return The multi-user user ID of the newly created guest user, or
+ * {@link UserHandle.USER_NULL} if the guest couldn't be created.
+ */
+ public @UserIdInt int createGuest() {
+ UserInfo guest;
+ try {
+ guest = mUserManager.createGuest(mContext,
+ mContext.getString(com.android.settingslib.R.string.guest_nickname));
+ } catch (UserManager.UserOperationException e) {
+ Log.e(TAG, "Couldn't create guest user", e);
+ return UserHandle.USER_NULL;
+ }
+ if (guest == null) {
+ Log.e(TAG, "Couldn't create guest, most likely because there already exists one");
+ return UserHandle.USER_NULL;
+ }
+ return guest.id;
+ }
+
public static abstract class BaseUserAdapter extends BaseAdapter {
final UserSwitcherController mController;
@@ -662,10 +787,15 @@
public String getName(Context context, UserRecord item) {
if (item.isGuest) {
if (item.isCurrent) {
- return context.getString(com.android.settingslib.R.string.guest_exit_guest);
+ return context.getString(mController.mGuestUserAutoCreated
+ ? com.android.settingslib.R.string.guest_reset_guest
+ : com.android.settingslib.R.string.guest_exit_guest);
} else {
+ // If config_guestUserAutoCreated, always show guest nickname instead of "Add
+ // guest" to make it seem as though the device always has a guest ready for use
return context.getString(
- item.info == null ? com.android.settingslib.R.string.guest_new_guest
+ item.info == null && !mController.mGuestUserAutoCreated
+ ? com.android.settingslib.R.string.guest_new_guest
: com.android.settingslib.R.string.guest_nickname);
}
} else if (item.isAddUser) {
@@ -882,12 +1012,16 @@
public ExitGuestDialog(Context context, int guestId, int targetId) {
super(context);
- setTitle(R.string.guest_exit_guest_dialog_title);
+ setTitle(mGuestUserAutoCreated
+ ? com.android.settingslib.R.string.guest_reset_guest_dialog_title
+ : R.string.guest_exit_guest_dialog_title);
setMessage(context.getString(R.string.guest_exit_guest_dialog_message));
setButton(DialogInterface.BUTTON_NEGATIVE,
context.getString(android.R.string.cancel), this);
setButton(DialogInterface.BUTTON_POSITIVE,
- context.getString(R.string.guest_exit_guest_dialog_remove), this);
+ context.getString(mGuestUserAutoCreated
+ ? com.android.settingslib.R.string.guest_reset_guest_confirm_button
+ : R.string.guest_exit_guest_dialog_remove), this);
SystemUIDialog.setWindowOnTop(this);
setCanceledOnTouchOutside(false);
mGuestId = guestId;
@@ -901,7 +1035,7 @@
} else {
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE);
dismiss();
- exitGuest(mGuestId, mTargetId);
+ removeGuestUser(mGuestId, mTargetId);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 753def0..2406db3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -109,17 +109,10 @@
contentDescription += ("," + mContext.getString(R.string.data_connection_no_internet));
}
if (mProviderModel) {
- // WiFi icon will only be shown in the statusbar in 2 scenarios
- // 1. WiFi is the default network, and it is validated
- // 2. WiFi is the default network, it is not validated and there is no other
- // non-Carrier WiFi networks available.
- boolean maybeShowIcons = (mCurrentState.inetCondition == 1)
- || (mCurrentState.inetCondition == 0
- && !mNetworkController.isNonCarrierWifiNetworkAvailable());
IconState statusIcon = new IconState(
- wifiVisible && maybeShowIcons, getCurrentIconId(), contentDescription);
+ wifiVisible, getCurrentIconId(), contentDescription);
IconState qsIcon = null;
- if ((mCurrentState.isDefault && maybeShowIcons) || (!mNetworkController.isRadioOn()
+ if (mCurrentState.isDefault || (!mNetworkController.isRadioOn()
&& !mNetworkController.isEthernetDefault())) {
qsIcon = new IconState(mCurrentState.connected,
mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
@@ -158,15 +151,8 @@
if (mCurrentState.inetCondition == 0) {
dataContentDescription = mContext.getString(R.string.data_connection_no_internet);
}
- // Mobile icon will only be shown in the statusbar in 2 scenarios
- // 1. Mobile is the default network, and it is validated
- // 2. Mobile is the default network, it is not validated and there is no other
- // non-Carrier WiFi networks available.
- boolean maybeShowIcons = (mCurrentState.inetCondition == 1)
- || (mCurrentState.inetCondition == 0
- && !mNetworkController.isNonCarrierWifiNetworkAvailable());
boolean sbVisible = mCurrentState.enabled && mCurrentState.connected
- && maybeShowIcons && mCurrentState.isDefault;
+ && mCurrentState.isDefault;
IconState statusIcon =
new IconState(sbVisible, getCurrentIconIdForCarrierWifi(), contentDescription);
int typeIcon = sbVisible ? icons.dataType : 0;
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index ca1f55e..11ddbd0 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -241,7 +241,7 @@
@Override
public void onReceive(Context context, Intent intent) {
boolean newWorkProfile = Intent.ACTION_MANAGED_PROFILE_ADDED.equals(intent.getAction());
- boolean userStarted = Intent.ACTION_USER_STARTED.equals(intent.getAction());
+ boolean userStarted = Intent.ACTION_USER_SWITCHED.equals(intent.getAction());
boolean isManagedProfile = mUserManager.isManagedProfile(
intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
if (userStarted || newWorkProfile) {
@@ -288,7 +288,7 @@
public void start() {
if (DEBUG) Log.d(TAG, "Start");
final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_STARTED);
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);
mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, mMainExecutor,
diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
index c5e35a4..8b394bf 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
@@ -16,13 +16,18 @@
package com.android.systemui.toast;
+import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
+import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+
import android.animation.Animator;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.Application;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -53,7 +58,7 @@
final ToastPlugin.Toast mPluginToast;
private final String mPackageName;
- private final int mUserId;
+ @UserIdInt private final int mUserId;
private final LayoutInflater mLayoutInflater;
final int mDefaultX = 0;
@@ -74,7 +79,7 @@
}
SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text,
- ToastPlugin.Toast pluginToast, String packageName, int userId,
+ ToastPlugin.Toast pluginToast, String packageName, @UserIdInt int userId,
int orientation) {
mLayoutInflater = layoutInflater;
mContext = context;
@@ -248,6 +253,15 @@
return null;
}
+ final Context userContext;
+ try {
+ userContext = context.createPackageContextAsUser("android",
+ 0, new UserHandle(userId));
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Could not create user package context");
+ return null;
+ }
+
final ApplicationsState appState =
ApplicationsState.getInstance((Application) context.getApplicationContext());
if (!appState.isUserAdded(userId)) {
@@ -255,9 +269,11 @@
+ "packageName=" + packageName);
return null;
}
+
+ final PackageManager packageManager = userContext.getPackageManager();
final AppEntry appEntry = appState.getEntry(packageName, userId);
if (appEntry == null || appEntry.info == null
- || !ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(appEntry)) {
+ || !showApplicationIcon(appEntry.info, packageManager)) {
return null;
}
@@ -265,7 +281,20 @@
UserHandle user = UserHandle.getUserHandleForUid(appInfo.uid);
IconFactory iconFactory = IconFactory.obtain(context);
Bitmap iconBmp = iconFactory.createBadgedIconBitmap(
- appInfo.loadUnbadgedIcon(context.getPackageManager()), user, true).icon;
+ appInfo.loadUnbadgedIcon(packageManager), user, true).icon;
return new BitmapDrawable(context.getResources(), iconBmp);
}
+
+ private static boolean showApplicationIcon(ApplicationInfo appInfo,
+ PackageManager packageManager) {
+ if (hasFlag(appInfo.flags, FLAG_UPDATED_SYSTEM_APP)) {
+ return packageManager.getLaunchIntentForPackage(appInfo.packageName)
+ != null;
+ }
+ return !hasFlag(appInfo.flags, FLAG_SYSTEM);
+ }
+
+ private static boolean hasFlag(int flags, int flag) {
+ return (flags & flag) != 0;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 26f4a2b..d97815f 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -68,7 +68,8 @@
private static final String[] RESET_EXCEPTION_LIST = new String[] {
QSTileHost.TILES_SETTING,
Settings.Secure.DOZE_ALWAYS_ON,
- Settings.Secure.MEDIA_CONTROLS_RESUME
+ Settings.Secure.MEDIA_CONTROLS_RESUME,
+ Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION
};
private final Observer mObserver = new Observer();
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvBottomSheetActivity.java b/packages/SystemUI/src/com/android/systemui/tv/TvBottomSheetActivity.java
new file mode 100644
index 0000000..2b7a332
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvBottomSheetActivity.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.tv;
+
+import android.app.Activity;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+
+import java.util.function.Consumer;
+
+/**
+ * Generic bottom sheet with up to two icons in the beginning and two buttons.
+ */
+public abstract class TvBottomSheetActivity extends Activity {
+
+ private static final String TAG = TvBottomSheetActivity.class.getSimpleName();
+ private Drawable mBackgroundWithBlur;
+ private Drawable mBackgroundWithoutBlur;
+
+ private final Consumer<Boolean> mBlurConsumer = this::onBlurChanged;
+
+ private void onBlurChanged(boolean enabled) {
+ Log.v(TAG, "blur enabled: " + enabled);
+ getWindow().setBackgroundDrawable(enabled ? mBackgroundWithBlur : mBackgroundWithoutBlur);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.tv_bottom_sheet);
+
+ overridePendingTransition(R.anim.tv_bottom_sheet_enter, 0);
+
+ mBackgroundWithBlur = getResources()
+ .getDrawable(R.drawable.bottom_sheet_background_with_blur);
+ mBackgroundWithoutBlur = getResources().getDrawable(R.drawable.bottom_sheet_background);
+
+ DisplayMetrics metrics = getResources().getDisplayMetrics();
+ int screenWidth = metrics.widthPixels;
+ int screenHeight = metrics.heightPixels;
+ int marginPx = getResources().getDimensionPixelSize(R.dimen.bottom_sheet_margin);
+
+ WindowManager.LayoutParams windowParams = getWindow().getAttributes();
+ windowParams.width = screenWidth - marginPx * 2;
+ windowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ windowParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
+ windowParams.horizontalMargin = 0f;
+ windowParams.verticalMargin = (float) marginPx / screenHeight;
+ windowParams.format = PixelFormat.TRANSPARENT;
+ windowParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+ windowParams.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+ windowParams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+ getWindow().setAttributes(windowParams);
+ getWindow().setElevation(getWindow().getElevation() + 5);
+ getWindow().setBackgroundBlurRadius(getResources().getDimensionPixelSize(
+ R.dimen.bottom_sheet_background_blur_radius));
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ getWindowManager().addCrossWindowBlurEnabledListener(mBlurConsumer);
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ getWindowManager().removeCrossWindowBlurEnabledListener(mBlurConsumer);
+ super.onDetachedFromWindow();
+ }
+
+ @Override
+ public void finish() {
+ super.finish();
+ overridePendingTransition(0, R.anim.tv_bottom_sheet_exit);
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
index dc86d58..3b64f9f 100644
--- a/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
@@ -16,6 +16,7 @@
package com.android.systemui.util
+import android.content.pm.ActivityInfo
import android.content.res.Resources
import android.graphics.Rect
import android.graphics.drawable.Drawable
@@ -64,6 +65,10 @@
return RoundedCornerState(super.getConstantState()!!)
}
+ override fun getChangingConfigurations(): Int {
+ return super.getChangingConfigurations() or ActivityInfo.CONFIG_DENSITY
+ }
+
private class RoundedCornerState(private val wrappedState: ConstantState) : ConstantState() {
override fun newDrawable(): Drawable {
return newDrawable(null, null)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index a5ccc47..e570598 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -128,14 +128,14 @@
private final MediaSessions mMediaSessions;
protected C mCallbacks = new C();
private final State mState = new State();
- protected final MediaSessionsCallbacks mMediaSessionsCallbacksW = new MediaSessionsCallbacks();
+ protected final MediaSessionsCallbacks mMediaSessionsCallbacksW;
private final Optional<Vibrator> mVibrator;
private final boolean mHasVibrator;
private boolean mShowA11yStream;
private boolean mShowVolumeDialog;
private boolean mShowSafetyWarning;
private long mLastToggledRingerOn;
- private boolean mDeviceInteractive;
+ private boolean mDeviceInteractive = true;
private boolean mDestroyed;
private VolumePolicy mVolumePolicy;
@@ -179,6 +179,7 @@
mWorkerLooper = theadFactory.buildLooperOnNewThread(
VolumeDialogControllerImpl.class.getSimpleName());
mWorker = new W(mWorkerLooper);
+ mMediaSessionsCallbacksW = new MediaSessionsCallbacks(mContext);
mMediaSessions = createMediaSessions(mContext, mWorkerLooper, mMediaSessionsCallbacksW);
mAudio = audioManager;
mNoMan = notificationManager;
@@ -1148,83 +1149,98 @@
private final HashMap<Token, Integer> mRemoteStreams = new HashMap<>();
private int mNextStream = DYNAMIC_STREAM_START_INDEX;
+ private final boolean mShowRemoteSessions;
+
+ public MediaSessionsCallbacks(Context context) {
+ mShowRemoteSessions = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_volumeShowRemoteSessions);
+ }
@Override
public void onRemoteUpdate(Token token, String name, PlaybackInfo pi) {
- addStream(token, "onRemoteUpdate");
+ if (mShowRemoteSessions) {
+ addStream(token, "onRemoteUpdate");
- int stream = 0;
- synchronized (mRemoteStreams) {
- stream = mRemoteStreams.get(token);
- }
- Slog.d(TAG, "onRemoteUpdate: stream: " + stream + " volume: " + pi.getCurrentVolume());
- boolean changed = mState.states.indexOfKey(stream) < 0;
- final StreamState ss = streamStateW(stream);
- ss.dynamic = true;
- ss.levelMin = 0;
- ss.levelMax = pi.getMaxVolume();
- if (ss.level != pi.getCurrentVolume()) {
- ss.level = pi.getCurrentVolume();
- changed = true;
- }
- if (!Objects.equals(ss.remoteLabel, name)) {
- ss.name = -1;
- ss.remoteLabel = name;
- changed = true;
- }
- if (changed) {
- Log.d(TAG, "onRemoteUpdate: " + name + ": " + ss.level + " of " + ss.levelMax);
- mCallbacks.onStateChanged(mState);
+ int stream = 0;
+ synchronized (mRemoteStreams) {
+ stream = mRemoteStreams.get(token);
+ }
+ Slog.d(TAG,
+ "onRemoteUpdate: stream: " + stream + " volume: " + pi.getCurrentVolume());
+ boolean changed = mState.states.indexOfKey(stream) < 0;
+ final StreamState ss = streamStateW(stream);
+ ss.dynamic = true;
+ ss.levelMin = 0;
+ ss.levelMax = pi.getMaxVolume();
+ if (ss.level != pi.getCurrentVolume()) {
+ ss.level = pi.getCurrentVolume();
+ changed = true;
+ }
+ if (!Objects.equals(ss.remoteLabel, name)) {
+ ss.name = -1;
+ ss.remoteLabel = name;
+ changed = true;
+ }
+ if (changed) {
+ Log.d(TAG, "onRemoteUpdate: " + name + ": " + ss.level + " of " + ss.levelMax);
+ mCallbacks.onStateChanged(mState);
+ }
}
}
@Override
public void onRemoteVolumeChanged(Token token, int flags) {
- addStream(token, "onRemoteVolumeChanged");
- int stream = 0;
- synchronized (mRemoteStreams) {
- stream = mRemoteStreams.get(token);
- }
- final boolean showUI = shouldShowUI(flags);
- Slog.d(TAG, "onRemoteVolumeChanged: stream: " + stream + " showui? " + showUI);
- boolean changed = updateActiveStreamW(stream);
- if (showUI) {
- changed |= checkRoutedToBluetoothW(AudioManager.STREAM_MUSIC);
- }
- if (changed) {
- Slog.d(TAG, "onRemoteChanged: updatingState");
- mCallbacks.onStateChanged(mState);
- }
- if (showUI) {
- mCallbacks.onShowRequested(Events.SHOW_REASON_REMOTE_VOLUME_CHANGED);
+ if (mShowRemoteSessions) {
+ addStream(token, "onRemoteVolumeChanged");
+ int stream = 0;
+ synchronized (mRemoteStreams) {
+ stream = mRemoteStreams.get(token);
+ }
+ final boolean showUI = shouldShowUI(flags);
+ Slog.d(TAG, "onRemoteVolumeChanged: stream: " + stream + " showui? " + showUI);
+ boolean changed = updateActiveStreamW(stream);
+ if (showUI) {
+ changed |= checkRoutedToBluetoothW(AudioManager.STREAM_MUSIC);
+ }
+ if (changed) {
+ Slog.d(TAG, "onRemoteChanged: updatingState");
+ mCallbacks.onStateChanged(mState);
+ }
+ if (showUI) {
+ mCallbacks.onShowRequested(Events.SHOW_REASON_REMOTE_VOLUME_CHANGED);
+ }
}
}
@Override
public void onRemoteRemoved(Token token) {
- int stream = 0;
- synchronized (mRemoteStreams) {
- if (!mRemoteStreams.containsKey(token)) {
- Log.d(TAG, "onRemoteRemoved: stream doesn't exist, "
- + "aborting remote removed for token:" + token.toString());
- return;
+ if (mShowRemoteSessions) {
+ int stream = 0;
+ synchronized (mRemoteStreams) {
+ if (!mRemoteStreams.containsKey(token)) {
+ Log.d(TAG, "onRemoteRemoved: stream doesn't exist, "
+ + "aborting remote removed for token:" + token.toString());
+ return;
+ }
+ stream = mRemoteStreams.get(token);
}
- stream = mRemoteStreams.get(token);
+ mState.states.remove(stream);
+ if (mState.activeStream == stream) {
+ updateActiveStreamW(-1);
+ }
+ mCallbacks.onStateChanged(mState);
}
- mState.states.remove(stream);
- if (mState.activeStream == stream) {
- updateActiveStreamW(-1);
- }
- mCallbacks.onStateChanged(mState);
}
public void setStreamVolume(int stream, int level) {
- final Token t = findToken(stream);
- if (t == null) {
- Log.w(TAG, "setStreamVolume: No token found for stream: " + stream);
- return;
+ if (mShowRemoteSessions) {
+ final Token t = findToken(stream);
+ if (t == null) {
+ Log.w(TAG, "setStreamVolume: No token found for stream: " + stream);
+ return;
+ }
+ mMediaSessions.setVolume(t, level);
}
- mMediaSessions.setVolume(t, level);
}
private Token findToken(int stream) {
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
index 8412a8a..0c53477 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
@@ -104,15 +104,14 @@
float percentDistanceFromCenter) {
CharSequence centerCardText = getLabelText(centerCard);
Drawable centerCardIcon = getHeaderIcon(mContext, centerCard);
- if (!TextUtils.equals(mCenterCardText, centerCardText)) {
- mCenterCardText = centerCardText;
+ renderActionButton(centerCard, mIsDeviceLocked, mIsUdfpsEnabled);
+ if (centerCard.isUiEquivalent(nextCard)) {
+ mCardLabel.setAlpha(1f);
+ mIcon.setAlpha(1f);
+ mActionButton.setAlpha(1f);
+ } else {
mCardLabel.setText(centerCardText);
mIcon.setImageDrawable(centerCardIcon);
- }
- renderActionButton(centerCard, mIsDeviceLocked, mIsUdfpsEnabled);
- if (TextUtils.equals(centerCardText, getLabelText(nextCard))) {
- mCardLabel.setAlpha(1f);
- } else {
mCardLabel.setAlpha(percentDistanceFromCenter);
mIcon.setAlpha(percentDistanceFromCenter);
mActionButton.setAlpha(percentDistanceFromCenter);
@@ -141,6 +140,7 @@
mErrorView.setVisibility(GONE);
mEmptyStateView.setVisibility(GONE);
mIcon.setImageDrawable(getHeaderIcon(mContext, data.get(selectedIndex)));
+ mCardLabel.setText(getLabelText(data.get(selectedIndex)));
renderActionButton(data.get(selectedIndex), isDeviceLocked, mIsUdfpsEnabled);
if (shouldAnimate) {
animateViewsShown(mIcon, mCardLabel, mActionButton);
@@ -248,20 +248,20 @@
private void renderActionButton(
WalletCardViewInfo walletCard, boolean isDeviceLocked, boolean isUdfpsEnabled) {
CharSequence actionButtonText = getActionButtonText(walletCard);
- if (!isUdfpsEnabled && isDeviceLocked) {
+ if (!isUdfpsEnabled && actionButtonText != null) {
mActionButton.setVisibility(VISIBLE);
- mActionButton.setText(R.string.wallet_action_button_label_unlock);
- mActionButton.setOnClickListener(mDeviceLockedActionOnClickListener);
- } else if (!isDeviceLocked && actionButtonText != null) {
mActionButton.setText(actionButtonText);
- mActionButton.setVisibility(VISIBLE);
- mActionButton.setOnClickListener(v -> {
- try {
- walletCard.getPendingIntent().send();
- } catch (PendingIntent.CanceledException e) {
- Log.w(TAG, "Error sending pending intent for wallet card");
- }
- });
+ mActionButton.setOnClickListener(
+ isDeviceLocked
+ ? mDeviceLockedActionOnClickListener
+ : v -> {
+ try {
+ walletCard.getPendingIntent().send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "Error sending pending intent for wallet card.");
+ }
+ }
+ );
} else {
mActionButton.setVisibility(GONE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 5441bd4..a29a638 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -617,7 +617,7 @@
* cancel it (and hence the bubbles associated with it).
*
* @return true if we want to intercept the dismissal of the entry, else false.
- * @see Bubbles#handleDismissalInterception(BubbleEntry, List, IntConsumer)
+ * @see Bubbles#handleDismissalInterception(BubbleEntry, List, IntConsumer, Executor)
*/
public boolean handleDismissalInterception(NotificationEntry entry) {
if (entry == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 92ef850..bc956dc 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -34,6 +34,7 @@
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.view.KeyEvent;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -44,6 +45,7 @@
import com.android.systemui.dagger.WMComponent;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.shared.tracing.ProtoTraceable;
@@ -57,6 +59,7 @@
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.nano.WmShellTraceProto;
import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.onehanded.OneHandedEventCallback;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
import com.android.wm.shell.pip.Pip;
@@ -112,6 +115,7 @@
private final NavigationModeController mNavigationModeController;
private final ScreenLifecycle mScreenLifecycle;
private final SysUiState mSysUiState;
+ private final WakefulnessLifecycle mWakefulnessLifecycle;
private final ProtoTracer mProtoTracer;
private final Executor mSysUiMainExecutor;
@@ -119,6 +123,7 @@
private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
+ private WakefulnessLifecycle.Observer mWakefulnessObserver;
@Inject
public WMShell(Context context,
@@ -134,6 +139,7 @@
ScreenLifecycle screenLifecycle,
SysUiState sysUiState,
ProtoTracer protoTracer,
+ WakefulnessLifecycle wakefulnessLifecycle,
@Main Executor sysUiMainExecutor) {
super(context);
mCommandQueue = commandQueue;
@@ -146,6 +152,7 @@
mSplitScreenOptional = splitScreenOptional;
mOneHandedOptional = oneHandedOptional;
mHideDisplayCutoutOptional = hideDisplayCutoutOptional;
+ mWakefulnessLifecycle = wakefulnessLifecycle;
mProtoTracer = protoTracer;
mShellCommandHandler = shellCommandHandler;
mSysUiMainExecutor = sysUiMainExecutor;
@@ -253,23 +260,19 @@
}
});
+ oneHanded.registerEventCallback(new OneHandedEventCallback() {
+ @Override
+ public void notifyExpandNotification() {
+ mSysUiMainExecutor.execute(
+ () -> mCommandQueue.handleSystemKey(
+ KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
+ }
+ });
+
mOneHandedKeyguardCallback = new KeyguardUpdateMonitorCallback() {
@Override
- public void onKeyguardBouncerChanged(boolean bouncer) {
- if (bouncer) {
- oneHanded.stopOneHanded();
- }
- }
-
- @Override
public void onKeyguardVisibilityChanged(boolean showing) {
- if (showing) {
- // When keyguard shown, temperory lock OHM disabled to avoid mis-trigger.
- oneHanded.setLockedDisabled(true /* locked */, false /* enabled */);
- } else {
- // Reset locked.
- oneHanded.setLockedDisabled(false /* locked */, false /* enabled */);
- }
+ oneHanded.onKeyguardVisibilityChanged(showing);
oneHanded.stopOneHanded();
}
@@ -280,6 +283,24 @@
};
mKeyguardUpdateMonitor.registerCallback(mOneHandedKeyguardCallback);
+ mWakefulnessObserver =
+ new WakefulnessLifecycle.Observer() {
+ @Override
+ public void onFinishedWakingUp() {
+ // Reset locked for the case keyguard not shown.
+ oneHanded.setLockedDisabled(false /* locked */, false /* enabled */);
+ }
+
+ @Override
+ public void onStartedGoingToSleep() {
+ oneHanded.stopOneHanded();
+ // When user press power button going to sleep, temperory lock OHM disabled
+ // to avoid mis-trigger.
+ oneHanded.setLockedDisabled(true /* locked */, false /* enabled */);
+ }
+ };
+ mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
+
mScreenLifecycle.addObserver(new ScreenLifecycle.Observer() {
@Override
public void onScreenTurningOff() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 3d4da27..657553f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -64,6 +64,7 @@
import android.os.IRemoteCallback;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.Vibrator;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -108,6 +109,7 @@
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -168,6 +170,8 @@
private TelephonyListenerManager mTelephonyListenerManager;
@Mock
private FeatureFlags mFeatureFlags;
+ @Mock
+ private Vibrator mVibrator;
@Captor
private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
// Direct executor
@@ -638,6 +642,45 @@
}
@Test
+ public void testFaceAndFingerprintLockout_onlyFace() {
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+ mTestableLooper.processAllMessages();
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+
+ mKeyguardUpdateMonitor.mFaceAuthenticationCallback
+ .onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, "");
+
+ verify(mLockPatternUtils, never()).requireStrongAuth(anyInt(), anyInt());
+ }
+
+ @Test
+ public void testFaceAndFingerprintLockout_onlyFingerprint() {
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+ mTestableLooper.processAllMessages();
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+
+ mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
+ .onAuthenticationError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT, "");
+
+ verify(mLockPatternUtils, never()).requireStrongAuth(anyInt(), anyInt());
+ }
+
+
+ @Test
+ public void testFaceAndFingerprintLockout() {
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+ mTestableLooper.processAllMessages();
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+
+ mKeyguardUpdateMonitor.mFaceAuthenticationCallback
+ .onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, "");
+ mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
+ .onAuthenticationError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT, "");
+
+ verify(mLockPatternUtils).requireStrongAuth(anyInt(), anyInt());
+ }
+
+ @Test
public void testGetUserCanSkipBouncer_whenFace() {
int user = KeyguardUpdateMonitor.getCurrentUser();
mKeyguardUpdateMonitor.onFaceAuthenticated(user, true /* isStrongBiometric */);
@@ -979,7 +1022,8 @@
mBroadcastDispatcher, mDumpManager,
mRingerModeTracker, mBackgroundExecutor,
mStatusBarStateController, mLockPatternUtils,
- mAuthController, mTelephonyListenerManager, mFeatureFlags);
+ mAuthController, mTelephonyListenerManager, mFeatureFlags,
+ mVibrator);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index 5b50e89..6237031 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -25,6 +25,8 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.content.Context;
+import android.content.ContextWrapper;
import android.os.UserHandle;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
@@ -39,6 +41,7 @@
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -58,6 +61,7 @@
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
+ private Context mContextWrapper;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private AccessibilityFloatingMenuController mController;
private AccessibilityButtonTargetsObserver mTargetsObserver;
@@ -66,6 +70,16 @@
private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardCallbackCaptor;
private KeyguardUpdateMonitorCallback mKeyguardCallback;
+ @Before
+ public void setUp() throws Exception {
+ mContextWrapper = new ContextWrapper(mContext) {
+ @Override
+ public Context createContextAsUser(UserHandle user, int flags) {
+ return getBaseContext();
+ }
+ };
+ }
+
@Test
public void initController_registerListeners() {
mController = setUpController();
@@ -105,7 +119,7 @@
public void onKeyguardVisibilityChanged_showing_destroyWidget() {
enableAccessibilityFloatingMenuConfig();
mController = setUpController();
- mController.mFloatingMenu = new AccessibilityFloatingMenu(mContext);
+ mController.mFloatingMenu = new AccessibilityFloatingMenu(mContextWrapper);
captureKeyguardUpdateMonitorCallback();
mKeyguardCallback.onUserUnlocked();
@@ -131,7 +145,7 @@
final int fakeUserId = 1;
enableAccessibilityFloatingMenuConfig();
mController = setUpController();
- mController.mFloatingMenu = new AccessibilityFloatingMenu(mContext);
+ mController.mFloatingMenu = new AccessibilityFloatingMenu(mContextWrapper);
captureKeyguardUpdateMonitorCallback();
mKeyguardCallback.onUserSwitching(fakeUserId);
@@ -144,7 +158,7 @@
final int fakeUserId = 1;
enableAccessibilityFloatingMenuConfig();
mController = setUpController();
- mController.mFloatingMenu = new AccessibilityFloatingMenu(mContext);
+ mController.mFloatingMenu = new AccessibilityFloatingMenu(mContextWrapper);
captureKeyguardUpdateMonitorCallback();
mKeyguardCallback.onUserUnlocked();
mKeyguardCallback.onKeyguardVisibilityChanged(true);
@@ -172,7 +186,7 @@
@Test
public void onAccessibilityButtonModeChanged_floatingModeAndHasButtonTargets_showWidget() {
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.putStringForUser(mContextWrapper.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS,
UserHandle.USER_CURRENT);
mController = setUpController();
@@ -184,7 +198,7 @@
@Test
public void onAccessibilityButtonModeChanged_floatingModeAndNoButtonTargets_destroyWidget() {
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.putStringForUser(mContextWrapper.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, "", UserHandle.USER_CURRENT);
mController = setUpController();
@@ -195,7 +209,7 @@
@Test
public void onAccessibilityButtonModeChanged_navBarModeAndHasButtonTargets_destroyWidget() {
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.putStringForUser(mContextWrapper.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS,
UserHandle.USER_CURRENT);
mController = setUpController();
@@ -207,7 +221,7 @@
@Test
public void onAccessibilityButtonModeChanged_navBarModeAndNoButtonTargets_destroyWidget() {
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.putStringForUser(mContextWrapper.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, "", UserHandle.USER_CURRENT);
mController = setUpController();
@@ -218,7 +232,7 @@
@Test
public void onAccessibilityButtonTargetsChanged_floatingModeAndHasButtonTargets_showWidget() {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU,
UserHandle.USER_CURRENT);
mController = setUpController();
@@ -230,7 +244,7 @@
@Test
public void onAccessibilityButtonTargetsChanged_floatingModeAndNoButtonTargets_destroyWidget() {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU,
UserHandle.USER_CURRENT);
mController = setUpController();
@@ -242,7 +256,7 @@
@Test
public void onAccessibilityButtonTargetsChanged_navBarModeAndHasButtonTargets_destroyWidget() {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
mController = setUpController();
@@ -254,7 +268,7 @@
@Test
public void onAccessibilityButtonTargetsChanged_navBarModeAndNoButtonTargets_destroyWidget() {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR, UserHandle.USER_CURRENT);
mController = setUpController();
@@ -269,15 +283,15 @@
mModeObserver = spy(Dependency.get(AccessibilityButtonModeObserver.class));
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
- return new AccessibilityFloatingMenuController(mContext, mTargetsObserver,
+ return new AccessibilityFloatingMenuController(mContextWrapper, mTargetsObserver,
mModeObserver, mKeyguardUpdateMonitor);
}
private void enableAccessibilityFloatingMenuConfig() {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.putIntForUser(mContextWrapper.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU,
UserHandle.USER_CURRENT);
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.putStringForUser(mContextWrapper.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, TEST_A11Y_BTN_TARGETS,
UserHandle.USER_CURRENT);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index 2c7d291..d01cdd4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -11,7 +11,6 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.IRemoteAnimationFinishedCallback
-import android.view.IRemoteAnimationRunner
import android.view.RemoteAnimationAdapter
import android.view.RemoteAnimationTarget
import android.view.SurfaceControl
@@ -20,19 +19,21 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertNotNull
import junit.framework.Assert.assertNull
import junit.framework.Assert.assertTrue
import junit.framework.AssertionFailedError
+import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Mock
+import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
-import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
@@ -43,13 +44,18 @@
@RunWithLooper
class ActivityLaunchAnimatorTest : SysuiTestCase() {
private val launchContainer = LinearLayout(mContext)
- private val keyguardHandler = TestLaunchAnimatorKeyguardHandler(isOnKeyguard = false)
+ @Mock lateinit var keyguardHandler: ActivityLaunchAnimator.KeyguardHandler
@Spy private val controller = TestLaunchAnimatorController(launchContainer)
@Mock lateinit var iCallback: IRemoteAnimationFinishedCallback
- private val activityLaunchAnimator = ActivityLaunchAnimator(keyguardHandler, mContext)
+ private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
@get:Rule val rule = MockitoJUnit.rule()
+ @Before
+ fun setup() {
+ activityLaunchAnimator = ActivityLaunchAnimator(keyguardHandler, mContext)
+ }
+
private fun startIntentWithAnimation(
animator: ActivityLaunchAnimator = this.activityLaunchAnimator,
controller: ActivityLaunchAnimator.Controller? = this.controller,
@@ -110,7 +116,7 @@
@Test
fun animatesIfActivityIsAlreadyOpenAndIsOnKeyguard() {
- val keyguardHandler = spy(TestLaunchAnimatorKeyguardHandler(isOnKeyguard = true))
+ `when`(keyguardHandler.isOnKeyguard()).thenReturn(true)
val animator = ActivityLaunchAnimator(keyguardHandler, context)
val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
@@ -123,7 +129,6 @@
waitForIdleSync()
verify(controller).onIntentStarted(willAnimateCaptor.capture())
- verify(keyguardHandler).disableKeyguardBlurs()
verify(keyguardHandler).hideKeyguardWithAnimation(any())
assertTrue(willAnimateCaptor.value)
@@ -166,6 +171,7 @@
val runner = activityLaunchAnimator.createRunner(controller)
runner.onAnimationStart(0, arrayOf(fakeWindow()), emptyArray(), emptyArray(), iCallback)
waitForIdleSync()
+ verify(keyguardHandler).setBlursDisabledForAppLaunch(eq(true))
verify(controller).onLaunchAnimationStart(anyBoolean())
}
@@ -185,20 +191,6 @@
}
}
-private class TestLaunchAnimatorKeyguardHandler(
- private val isOnKeyguard: Boolean
-) : ActivityLaunchAnimator.KeyguardHandler {
- override fun isOnKeyguard(): Boolean = isOnKeyguard
-
- override fun disableKeyguardBlurs() {
- // Do nothing
- }
-
- override fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner) {
- // Do nothing.
- }
-}
-
/**
* A simple implementation of [ActivityLaunchAnimator.Controller] which throws if it is called
* outside of the main thread.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 240fdf3..d87a26b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -53,6 +54,7 @@
@Mock private lateinit var authController: AuthController
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
@Mock private lateinit var bypassController: KeyguardBypassController
+ @Mock private lateinit var biometricUnlockController: BiometricUnlockController
@Before
fun setUp() {
@@ -66,6 +68,7 @@
commandRegistry,
notificationShadeWindowController,
bypassController,
+ biometricUnlockController,
rippleView
)
controller.init()
@@ -90,7 +93,7 @@
// THEN update sensor location and show ripple
verify(rippleView).setSensorLocation(fpsLocation)
- verify(rippleView).startRipple(any())
+ verify(rippleView).startRipple(any(), any())
}
@Test
@@ -111,7 +114,7 @@
false /* isStrongBiometric */)
// THEN no ripple
- verify(rippleView, never()).startRipple(any())
+ verify(rippleView, never()).startRipple(any(), any())
}
@Test
@@ -132,7 +135,7 @@
false /* isStrongBiometric */)
// THEN no ripple
- verify(rippleView, never()).startRipple(any())
+ verify(rippleView, never()).startRipple(any(), any())
}
@Test
@@ -156,7 +159,7 @@
// THEN show ripple
verify(rippleView).setSensorLocation(faceLocation)
- verify(rippleView).startRipple(any())
+ verify(rippleView).startRipple(any(), any())
}
@Test
@@ -176,7 +179,7 @@
false /* isStrongBiometric */)
// THEN no ripple
- verify(rippleView, never()).startRipple(any())
+ verify(rippleView, never()).startRipple(any(), any())
}
@Test
@@ -191,7 +194,7 @@
0 /* userId */,
BiometricSourceType.FACE /* type */,
false /* isStrongBiometric */)
- verify(rippleView, never()).startRipple(any())
+ verify(rippleView, never()).startRipple(any(), any())
}
@Test
@@ -206,7 +209,7 @@
0 /* userId */,
BiometricSourceType.FINGERPRINT /* type */,
false /* isStrongBiometric */)
- verify(rippleView, never()).startRipple(any())
+ verify(rippleView, never()).startRipple(any(), any())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 41f9877..8ab32bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -53,8 +53,11 @@
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.util.concurrency.Execution;
+import com.android.systemui.util.concurrency.FakeExecution;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -74,7 +77,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
public class UdfpsControllerTest extends SysuiTestCase {
// Use this for inputs going into SystemUI. Use UdfpsController.mUdfpsSensorId for things
@@ -88,6 +91,7 @@
private UdfpsController mUdfpsController;
// Dependencies
+ private Execution mExecution;
@Mock
private LayoutInflater mLayoutInflater;
@Mock
@@ -117,6 +121,8 @@
@Mock
private AccessibilityManager mAccessibilityManager;
@Mock
+ private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ @Mock
private ScreenLifecycle mScreenLifecycle;
@Mock
private Vibrator mVibrator;
@@ -142,6 +148,8 @@
@Before
public void setUp() {
setUpResources();
+ mExecution = new FakeExecution();
+
when(mLayoutInflater.inflate(R.layout.udfps_view, null, false)).thenReturn(mUdfpsView);
final List<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
@@ -163,6 +171,7 @@
mFgExecutor = new FakeExecutor(new FakeSystemClock());
mUdfpsController = new UdfpsController(
mContext,
+ mExecution,
mLayoutInflater,
mFingerprintManager,
mWindowManager,
@@ -176,6 +185,7 @@
mFalsingManager,
mPowerManager,
mAccessibilityManager,
+ mLockscreenShadeTransitionController,
mScreenLifecycle,
mVibrator,
Optional.of(mHbmProvider));
@@ -334,7 +344,7 @@
moveEvent.recycle();
// THEN click haptic is played
- verify(mVibrator).vibrate(mUdfpsController.mEffectClick,
+ verify(mVibrator).vibrate(mUdfpsController.EFFECT_CLICK,
UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 5923de6..f62587c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -35,6 +35,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -65,6 +66,8 @@
@Mock
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock
+ private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ @Mock
private DumpManager mDumpManager;
@Mock
private DelayableExecutor mExecutor;
@@ -106,6 +109,7 @@
mExecutor,
mDumpManager,
mKeyguardViewMediator,
+ mLockscreenShadeTransitionController,
mUdfpsController);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index c0b45c6..10997fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -16,7 +16,10 @@
package com.android.systemui.doze;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
+
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
@@ -161,7 +164,7 @@
clearInvocations(mSensors);
mTriggers.transitionTo(DozeMachine.State.DOZE_PULSING, DozeMachine.State.DOZE_PULSE_DONE);
- mTriggers.transitionTo(DozeMachine.State.DOZE_PULSE_DONE, DozeMachine.State.DOZE_AOD);
+ mTriggers.transitionTo(DozeMachine.State.DOZE_PULSE_DONE, DOZE_AOD);
waitForSensorManager();
verify(mSensors).requestTriggerSensor(any(), eq(mTapSensor));
}
@@ -206,34 +209,17 @@
// WHEN quick pick up is triggered
mTriggers.onSensor(DozeLog.REASON_SENSOR_QUICK_PICKUP, 100, 100, null);
- // THEN device goes into aod (shows clock with black background)
- verify(mMachine).requestState(DozeMachine.State.DOZE_AOD);
+ // THEN request pulse
+ verify(mMachine).requestPulse(anyInt());
// THEN a log is taken that quick pick up was triggered
verify(mUiEventLogger).log(DozingUpdateUiEvent.DOZING_UPDATE_QUICK_PICKUP);
}
@Test
- public void testQuickPickupTimeOutAfterExecutables() {
- // GIVEN quick pickup is triggered when device is in DOZE
- when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
- mTriggers.onSensor(DozeLog.REASON_SENSOR_QUICK_PICKUP, 100, 100, null);
- verify(mMachine).requestState(DozeMachine.State.DOZE_AOD);
- verify(mMachine, never()).requestState(DozeMachine.State.DOZE);
-
- // WHEN next executable is run
- mExecutor.advanceClockToLast();
- mExecutor.runAllReady();
-
- // THEN device goes back into DOZE
- verify(mMachine).requestState(DozeMachine.State.DOZE);
-
- // THEN a log is taken that wake up timeout expired
- verify(mUiEventLogger).log(DozingUpdateUiEvent.DOZING_UPDATE_WAKE_TIMEOUT);
- }
-
- @Test
public void testOnSensor_Fingerprint() {
+ // GIVEN dozing state
+ when(mMachine.getState()).thenReturn(DOZE_AOD);
final int screenX = 100;
final int screenY = 100;
final float misc = -1;
@@ -241,8 +227,20 @@
final float major = 3f;
final int reason = DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS;
float[] rawValues = new float[]{screenX, screenY, misc, major, minor};
+
+ // WHEN longpress gesture is triggered
mTriggers.onSensor(reason, screenX, screenY, rawValues);
+
+ // THEN
+ // * don't immediately send interrupt
+ // * immediately extend pulse
+ verify(mAuthController, never()).onAodInterrupt(anyInt(), anyInt(), anyFloat(), anyFloat());
verify(mHost).extendPulse(reason);
+
+ // WHEN display state changes to ON
+ mTriggers.onScreenState(Display.STATE_ON);
+
+ // THEN send interrupt
verify(mAuthController).onAodInterrupt(eq(screenX), eq(screenY), eq(major), eq(minor));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
index afe5c0b..1d34aac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -43,6 +43,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.wakelock.WakeLockFake;
@@ -77,6 +78,8 @@
private DozeUi mDozeUi;
@Mock
private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private ConfigurationController mConfigurationController;
@Before
public void setUp() throws Exception {
@@ -89,7 +92,7 @@
mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService,
- () -> mStatusBarStateController);
+ () -> mStatusBarStateController, mConfigurationController);
mDozeUi.setDozeMachine(mMachine);
}
@@ -146,7 +149,7 @@
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService,
- () -> mStatusBarStateController);
+ () -> mStatusBarStateController, mConfigurationController);
mDozeUi.setDozeMachine(mMachine);
// Never animate if display doesn't support it.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 83e7b17..578c2d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -30,6 +30,7 @@
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Color;
import android.media.AudioManager;
@@ -38,8 +39,9 @@
import android.service.dreams.IDreamManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.GestureDetector;
import android.view.IWindowManager;
-import android.view.View;
+import android.view.MotionEvent;
import android.view.WindowManagerPolicyConstants;
import androidx.test.filters.SmallTest;
@@ -55,8 +57,8 @@
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.settings.UserContextProvider;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -96,7 +98,6 @@
@Mock private TrustManager mTrustManager;
@Mock private IActivityManager mActivityManager;
@Mock private MetricsLogger mMetricsLogger;
- @Mock private NotificationShadeDepthController mDepthController;
@Mock private SysuiColorExtractor mColorExtractor;
@Mock private IStatusBarService mStatusBarService;
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@@ -107,8 +108,10 @@
@Mock private RingerModeTracker mRingerModeTracker;
@Mock private RingerModeLiveData mRingerModeLiveData;
@Mock private SysUiState mSysUiState;
+ @Mock private PackageManager mPackageManager;
@Mock private Handler mHandler;
@Mock private UserContextProvider mUserContextProvider;
+ @Mock private StatusBar mStatusBar;
private TestableLooper mTestableLooper;
@@ -120,6 +123,8 @@
when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData);
when(mUserContextProvider.getUserContext()).thenReturn(mContext);
+ when(mResources.getConfiguration()).thenReturn(
+ getContext().getResources().getConfiguration());
mGlobalActionsDialogLite = new GlobalActionsDialogLite(mContext,
mWindowManagerFuncs,
@@ -140,7 +145,6 @@
mActivityManager,
null,
mMetricsLogger,
- mDepthController,
mColorExtractor,
mStatusBarService,
mNotificationShadeWindowController,
@@ -150,7 +154,9 @@
mInfoProvider,
mRingerModeTracker,
mSysUiState,
- mHandler
+ mHandler,
+ mPackageManager,
+ mStatusBar
);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
@@ -194,7 +200,7 @@
}
@Test
- public void testShouldLogOnTapOutside() {
+ public void testSingleTap_logAndDismiss() {
mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
@@ -207,12 +213,61 @@
};
doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
- View container = dialog.findViewById(com.android.systemui.R.id.global_actions_container);
- container.callOnClick();
+
+ GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener);
+ gestureListener.onSingleTapConfirmed(null);
verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
}
@Test
+ public void testSwipeDownLockscreen_logAndOpenQS() {
+ mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+ doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+ doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+ doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+ doReturn(true).when(mStatusBar).isKeyguardShowing();
+ String[] actions = {
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ };
+ doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+ GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
+
+ GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener);
+ MotionEvent start = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ MotionEvent end = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 500, 0);
+ gestureListener.onFling(start, end, 0, 1000);
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ verify(mStatusBar).animateExpandSettingsPanel(null);
+ }
+
+ @Test
+ public void testSwipeDown_logAndOpenNotificationShade() {
+ mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+ doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+ doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+ doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+ doReturn(false).when(mStatusBar).isKeyguardShowing();
+ String[] actions = {
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ };
+ doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+ GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
+
+ GestureDetector.SimpleOnGestureListener gestureListener = spy(dialog.mGestureListener);
+ MotionEvent start = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ MotionEvent end = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 500, 0);
+ gestureListener.onFling(start, end, 0, 1000);
+ verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
+ verify(mStatusBar).animateExpandNotificationsPanel();
+ }
+
+ @Test
public void testShouldLogBugreportPress() throws InterruptedException {
GlobalActionsDialog.BugReportAction bugReportAction =
mGlobalActionsDialogLite.makeBugReportActionForTesting();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index 3130e97..2fa67cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -33,6 +33,7 @@
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Color;
@@ -63,8 +64,8 @@
import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -109,7 +110,6 @@
@Mock private TrustManager mTrustManager;
@Mock private IActivityManager mActivityManager;
@Mock private MetricsLogger mMetricsLogger;
- @Mock private NotificationShadeDepthController mDepthController;
@Mock private SysuiColorExtractor mColorExtractor;
@Mock private IStatusBarService mStatusBarService;
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@@ -123,7 +123,9 @@
@Mock GlobalActionsPanelPlugin.PanelViewController mWalletController;
@Mock private Handler mHandler;
@Mock private UserTracker mUserTracker;
+ @Mock private PackageManager mPackageManager;
@Mock private SecureSettings mSecureSettings;
+ @Mock private StatusBar mStatusBar;
private TestableLooper mTestableLooper;
@@ -134,6 +136,8 @@
allowTestableLooperAsMainThread();
when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData);
+ when(mResources.getConfiguration()).thenReturn(
+ getContext().getResources().getConfiguration());
mGlobalActionsDialog = new GlobalActionsDialog(mContext,
mWindowManagerFuncs,
@@ -155,7 +159,6 @@
mActivityManager,
null,
mMetricsLogger,
- mDepthController,
mColorExtractor,
mStatusBarService,
mNotificationShadeWindowController,
@@ -164,7 +167,9 @@
mUiEventLogger,
mRingerModeTracker,
mSysUiState,
- mHandler
+ mHandler,
+ mPackageManager,
+ mStatusBar
);
mGlobalActionsDialog.setZeroDialogPressDelayForTesting();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
index 2f78532..5157668 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
@@ -34,7 +34,6 @@
import android.graphics.Color;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
import androidx.test.filters.SmallTest;
@@ -258,8 +257,8 @@
// WHEN the device is dozing
mStatusBarStateListener.onDozingChanged(true);
- // THEN the view is GONE
- verify(mView).setVisibility(View.GONE);
+ // THEN switch to INDICATION_TYPE_NONE
+ verify(mView).switchIndication(null);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index d9b56a4..1dacc62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -52,6 +52,7 @@
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -78,6 +79,7 @@
private @Mock DumpManager mDumpManager;
private @Mock PowerManager mPowerManager;
private @Mock TrustManager mTrustManager;
+ private @Mock UserSwitcherController mUserSwitcherController;
private @Mock NavigationModeController mNavigationModeController;
private @Mock KeyguardDisplayManager mKeyguardDisplayManager;
private @Mock DozeParameters mDozeParameters;
@@ -100,13 +102,27 @@
when(mPowerManager.newWakeLock(anyInt(), any())).thenReturn(mock(WakeLock.class));
mViewMediator = new KeyguardViewMediator(
- mContext, mFalsingCollector, mLockPatternUtils, mBroadcastDispatcher,
+ mContext,
+ mFalsingCollector,
+ mLockPatternUtils,
+ mBroadcastDispatcher,
() -> mStatusBarKeyguardViewManager,
- mDismissCallbackRegistry, mUpdateMonitor, mDumpManager, mUiBgExecutor,
- mPowerManager, mTrustManager, mDeviceConfig, mNavigationModeController,
- mKeyguardDisplayManager, mDozeParameters, mStatusBarStateController,
- mKeyguardStateController, () -> mKeyguardUnlockAnimationController,
- mUnlockedScreenOffAnimationController, () -> mNotificationShadeDepthController);
+ mDismissCallbackRegistry,
+ mUpdateMonitor,
+ mDumpManager,
+ mUiBgExecutor,
+ mPowerManager,
+ mTrustManager,
+ mUserSwitcherController,
+ mDeviceConfig,
+ mNavigationModeController,
+ mKeyguardDisplayManager,
+ mDozeParameters,
+ mStatusBarStateController,
+ mKeyguardStateController,
+ () -> mKeyguardUnlockAnimationController,
+ mUnlockedScreenOffAnimationController,
+ () -> mNotificationShadeDepthController);
mViewMediator.start();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
index 63ce98a..910b381 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
@@ -56,7 +56,7 @@
@Test
public void baseState() throws Exception {
- assertEquals(WakefulnessLifecycle.WAKEFULNESS_ASLEEP, mWakefulness.getWakefulness());
+ assertEquals(WakefulnessLifecycle.WAKEFULNESS_AWAKE, mWakefulness.getWakefulness());
verifyNoMoreInteractions(mWakefulnessObserver);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
index e20b426..66b6470 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
@@ -82,9 +82,11 @@
@Test
public void eventNotEmittedWithoutDevice() {
// WHEN data source emits an event without device data
- mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */);
+ mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
+ false /* isSsReactivated */);
// THEN an event isn't emitted
- verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean());
+ verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(),
+ anyBoolean());
}
@Test
@@ -92,7 +94,8 @@
// WHEN device source emits an event without media data
mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
// THEN an event isn't emitted
- verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean());
+ verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(),
+ anyBoolean());
}
@Test
@@ -100,80 +103,95 @@
// GIVEN that a device event has already been received
mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
// WHEN media event is received
- mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */);
+ mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
+ false /* isSsReactivated */);
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
- verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean());
+ verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean(),
+ anyBoolean());
assertThat(captor.getValue().getDevice()).isNotNull();
}
@Test
public void emitEventAfterMediaFirst() {
// GIVEN that media event has already been received
- mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */);
+ mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
+ false /* isSsReactivated */);
// WHEN device event is received
mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
- verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean());
+ verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean(),
+ anyBoolean());
assertThat(captor.getValue().getDevice()).isNotNull();
}
@Test
public void migrateKeyMediaFirst() {
// GIVEN that media and device info has already been received
- mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */);
+ mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */,
+ false /* isSsReactivated */);
mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
reset(mListener);
// WHEN a key migration event is received
- mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */);
+ mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */,
+ false /* isSsReactivated */);
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
- verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean());
+ verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean(),
+ anyBoolean());
assertThat(captor.getValue().getDevice()).isNotNull();
}
@Test
public void migrateKeyDeviceFirst() {
// GIVEN that media and device info has already been received
- mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */);
+ mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */,
+ false /* isSsReactivated */);
mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
reset(mListener);
// WHEN a key migration event is received
mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
// THEN the listener receives a combined event
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
- verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean());
+ verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean(),
+ anyBoolean());
assertThat(captor.getValue().getDevice()).isNotNull();
}
@Test
public void migrateKeyMediaAfter() {
// GIVEN that media and device info has already been received
- mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */);
+ mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */,
+ false /* isSsReactivated */);
mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
reset(mListener);
// WHEN a second key migration event is received for media
- mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */);
+ mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */,
+ false /* isSsReactivated */);
// THEN the key has already been migrated
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
- verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean());
+ verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean(),
+ anyBoolean());
assertThat(captor.getValue().getDevice()).isNotNull();
}
@Test
public void migrateKeyDeviceAfter() {
// GIVEN that media and device info has already been received
- mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */);
+ mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */,
+ false /* isSsReactivated */);
mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData);
- mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */);
+ mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */,
+ false /* isSsReactivated */);
reset(mListener);
// WHEN a second key migration event is received for the device
mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData);
// THEN the key has already be migrated
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
- verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean());
+ verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean(),
+ anyBoolean());
assertThat(captor.getValue().getDevice()).isNotNull();
}
@@ -187,7 +205,8 @@
@Test
public void mediaDataRemovedAfterMediaEvent() {
- mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */);
+ mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
+ false /* isSsReactivated */);
mManager.onMediaDataRemoved(KEY);
verify(mListener).onMediaDataRemoved(eq(KEY));
}
@@ -202,13 +221,15 @@
@Test
public void mediaDataKeyUpdated() {
// GIVEN that device and media events have already been received
- mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */);
+ mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
+ false /* isSsReactivated */);
mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
// WHEN the key is changed
- mManager.onMediaDataLoaded("NEW_KEY", KEY, mMediaData, true /* immediately */);
+ mManager.onMediaDataLoaded("NEW_KEY", KEY, mMediaData, true /* immediately */,
+ false /* isSsReactivated */);
// THEN the listener gets a load event with the correct keys
ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class);
verify(mListener).onMediaDataLoaded(
- eq("NEW_KEY"), any(), captor.capture(), anyBoolean());
+ eq("NEW_KEY"), any(), captor.capture(), anyBoolean(), anyBoolean());
}
}
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 17f2a07..d879186 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt
@@ -120,7 +120,8 @@
mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
// THEN we should tell the listener
- verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true))
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true),
+ eq(false))
}
@Test
@@ -129,7 +130,7 @@
mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
// THEN we should NOT tell the listener
- verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean())
+ verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyBoolean())
}
@Test
@@ -175,10 +176,12 @@
setUser(USER_GUEST)
// THEN we should add back the guest user media
- verify(listener).onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true))
+ verify(listener).onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true),
+ eq(false))
// but not the main user's
- verify(listener, never()).onMediaDataLoaded(eq(KEY), any(), eq(dataMain), anyBoolean())
+ verify(listener, never()).onMediaDataLoaded(eq(KEY), any(), eq(dataMain), anyBoolean(),
+ anyBoolean())
}
@Test
@@ -245,7 +248,7 @@
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean())
+ verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyBoolean())
verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
}
@@ -282,12 +285,15 @@
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
- verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true))
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
+ eq(false))
// AND we get a smartspace signal
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
- // THEN we should tell listeners to treat the media as active instead
+ // THEN we should tell listeners to treat the media as not active instead
+ verify(listener, never()).onMediaDataLoaded(eq(KEY), eq(KEY), any(), anyBoolean(),
+ anyBoolean())
verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
}
@@ -299,14 +305,16 @@
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
- verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true))
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
+ eq(false))
// AND we get a smartspace signal
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
// THEN we should tell listeners to treat the media as active instead
val dataCurrentAndActive = dataCurrent.copy(active = true)
- verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true))
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true),
+ eq(true))
assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
// Smartspace update shouldn't be propagated for the empty rec list.
verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean())
@@ -317,14 +325,16 @@
// WHEN we have media that was recently played, but not currently active
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
- verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true))
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
+ eq(false))
// AND we get a smartspace signal
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
// THEN we should tell listeners to treat the media as active instead
val dataCurrentAndActive = dataCurrent.copy(active = true)
- verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true))
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true),
+ eq(true))
assertThat(mediaDataFilter.hasActiveMedia()).isTrue()
// Smartspace update should also be propagated but not prioritized.
verify(listener)
@@ -344,11 +354,17 @@
fun testOnSmartspaceMediaDataRemoved_usedMediaAndSmartspace_clearsBoth() {
val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime())
mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent)
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true),
+ eq(false))
+
mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData)
+ val dataCurrentAndActive = dataCurrent.copy(active = true)
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true),
+ eq(true))
+
mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
- verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrent), eq(true))
verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY)
assertThat(mediaDataFilter.hasActiveMedia()).isFalse()
}
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 15cfee8..5b4e124 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -9,8 +9,8 @@
import android.media.MediaMetadata
import android.media.session.MediaController
import android.media.session.MediaSession
-import android.provider.Settings
import android.os.Bundle
+import android.provider.Settings
import android.service.notification.StatusBarNotification
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
@@ -20,6 +20,7 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.SbnBuilder
+import com.android.systemui.tuner.TunerService
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
@@ -86,6 +87,8 @@
lateinit var mediaNotification: StatusBarNotification
@Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData>
private val clock = FakeSystemClock()
+ @Mock private lateinit var tunerService: TunerService
+ @Captor lateinit var tunableCaptor: ArgumentCaptor<TunerService.Tunable>
private val originalSmartspaceSetting = Settings.Secure.getInt(context.contentResolver,
Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 1)
@@ -114,8 +117,11 @@
smartspaceMediaDataProvider = smartspaceMediaDataProvider,
useMediaResumption = true,
useQsMediaPlayer = true,
- systemClock = clock
+ systemClock = clock,
+ tunerService = tunerService
)
+ verify(tunerService).addTunable(capture(tunableCaptor),
+ eq(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION))
session = MediaSession(context, "MediaDataManagerTestSession")
mediaNotification = SbnBuilder().run {
setPkg(PACKAGE_NAME)
@@ -179,7 +185,8 @@
fun testOnMetaDataLoaded_callsListener() {
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java))
- verify(listener).onMediaDataLoaded(eq(KEY), eq(null), anyObject(), eq(true))
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), anyObject(), eq(true),
+ eq(false))
}
@Test
@@ -190,7 +197,8 @@
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true))
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+ eq(false))
assertThat(mediaDataCaptor.value!!.active).isTrue()
}
@@ -209,7 +217,8 @@
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true))
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+ eq(false))
val data = mediaDataCaptor.value
assertThat(data.resumption).isFalse()
mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
@@ -217,7 +226,8 @@
mediaDataManager.onNotificationRemoved(KEY)
// THEN the media data indicates that it is for resumption
verify(listener)
- .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true))
+ .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true),
+ eq(false))
assertThat(mediaDataCaptor.value.resumption).isTrue()
}
@@ -230,7 +240,8 @@
assertThat(backgroundExecutor.runAllReady()).isEqualTo(2)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(2)
verify(listener)
- .onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true))
+ .onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+ eq(false))
val data = mediaDataCaptor.value
assertThat(data.resumption).isFalse()
val resumableData = data.copy(resumeAction = Runnable {})
@@ -241,7 +252,8 @@
mediaDataManager.onNotificationRemoved(KEY)
// THEN the data is for resumption and the key is migrated to the package name
verify(listener)
- .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true))
+ .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true),
+ eq(false))
assertThat(mediaDataCaptor.value.resumption).isTrue()
verify(listener, never()).onMediaDataRemoved(eq(KEY))
// WHEN the second is removed
@@ -249,7 +261,8 @@
// THEN the data is for resumption and the second key is removed
verify(listener)
.onMediaDataLoaded(
- eq(PACKAGE_NAME), eq(PACKAGE_NAME), capture(mediaDataCaptor), eq(true))
+ eq(PACKAGE_NAME), eq(PACKAGE_NAME), capture(mediaDataCaptor), eq(true),
+ eq(false))
assertThat(mediaDataCaptor.value.resumption).isTrue()
verify(listener).onMediaDataRemoved(eq(KEY_2))
}
@@ -263,7 +276,8 @@
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true))
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+ eq(false))
val data = mediaDataCaptor.value
val dataRemoteWithResume = data.copy(resumeAction = Runnable {}, isLocalSession = false)
mediaDataManager.onMediaDataLoaded(KEY, null, dataRemoteWithResume)
@@ -289,7 +303,8 @@
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
// THEN the media data indicates that it is for resumption
verify(listener)
- .onMediaDataLoaded(eq(PACKAGE_NAME), eq(null), capture(mediaDataCaptor), eq(true))
+ .onMediaDataLoaded(eq(PACKAGE_NAME), eq(null), capture(mediaDataCaptor), eq(true),
+ eq(false))
val data = mediaDataCaptor.value
assertThat(data.resumption).isTrue()
assertThat(data.song).isEqualTo(SESSION_TITLE)
@@ -329,7 +344,8 @@
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
verify(listener)
- .onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true))
+ .onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+ eq(false))
}
@Test
@@ -364,6 +380,9 @@
fun testOnSmartspaceMediaDataLoaded_hasNoneMediaTarget_callsRemoveListener() {
smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
smartspaceMediaDataProvider.onTargetsAvailable(listOf())
+ foregroundExecutor.advanceClockToLast()
+ foregroundExecutor.runAllReady()
+
verify(listener).onSmartspaceMediaDataRemoved(eq(KEY_MEDIA_SMARTSPACE), eq(false))
}
@@ -372,6 +391,8 @@
// WHEN media recommendation setting is off
Settings.Secure.putInt(context.contentResolver,
Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 0)
+ tunableCaptor.value.onTuningChanged(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, "0")
+
smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
// THEN smartspace signal is ignored
@@ -380,12 +401,31 @@
}
@Test
+ fun testMediaRecommendationDisabled_removesSmartspaceData() {
+ // GIVEN a media recommendation card is present
+ smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
+ verify(listener).onSmartspaceMediaDataLoaded(eq(KEY_MEDIA_SMARTSPACE), anyObject(),
+ anyBoolean())
+
+ // WHEN the media recommendation setting is turned off
+ Settings.Secure.putInt(context.contentResolver,
+ Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 0)
+ tunableCaptor.value.onTuningChanged(Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, "0")
+
+ // THEN listeners are notified
+ foregroundExecutor.advanceClockToLast()
+ foregroundExecutor.runAllReady()
+ verify(listener).onSmartspaceMediaDataRemoved(eq(KEY_MEDIA_SMARTSPACE), eq(true))
+ }
+
+ @Test
fun testOnMediaDataChanged_updatesLastActiveTime() {
val currentTime = clock.elapsedRealtime()
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true))
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+ eq(false))
assertThat(mediaDataCaptor.value!!.lastActive).isAtLeast(currentTime)
}
@@ -402,7 +442,8 @@
mediaDataManager.setTimedOut(KEY, true, true)
// THEN the last active time is not changed
- verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), capture(mediaDataCaptor), eq(true))
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), capture(mediaDataCaptor), eq(true),
+ eq(false))
assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTime)
}
@@ -413,7 +454,8 @@
mediaDataManager.onNotificationAdded(KEY, mediaNotification)
assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
- verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true))
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+ eq(false))
val data = mediaDataCaptor.value
assertThat(data.resumption).isFalse()
mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
@@ -425,7 +467,8 @@
// THEN the last active time is not changed
verify(listener)
- .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true))
+ .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true),
+ eq(false))
assertThat(mediaDataCaptor.value.resumption).isTrue()
assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTime)
}
@@ -451,7 +494,8 @@
assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
// THEN only the first MAX_COMPACT_ACTIONS are actually set
- verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true))
+ verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+ eq(false))
assertThat(mediaDataCaptor.value.actionsToShowInCompact.size).isEqualTo(
MediaDataManager.MAX_COMPACT_ACTIONS)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
index ef8d322..ba4fc0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
@@ -22,14 +22,24 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Ignore
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
import org.mockito.Mockito.mock
+import org.mockito.junit.MockitoJUnit
@SmallTest
@RunWith(AndroidTestingRunner::class)
public class MediaPlayerDataTest : SysuiTestCase() {
+ @Mock
+ private lateinit var playerIsPlaying: MediaControlPanel
+
+ @JvmField
+ @Rule
+ val mockito = MockitoJUnit.rule()
+
companion object {
val LOCAL = true
val RESUMPTION = true
@@ -44,7 +54,6 @@
@Test
fun addPlayingThenRemote() {
- val playerIsPlaying = mock(MediaControlPanel::class.java)
val dataIsPlaying = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION)
val playerIsRemote = mock(MediaControlPanel::class.java)
@@ -83,7 +92,6 @@
@Test
fun fullOrderTest() {
- val playerIsPlaying = mock(MediaControlPanel::class.java)
val dataIsPlaying = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION)
val playerIsPlayingAndRemote = mock(MediaControlPanel::class.java)
@@ -115,6 +123,26 @@
playerUndetermined).inOrder()
}
+ @Test
+ fun testMoveMediaKeysAround() {
+ val keyA = "a"
+ val keyB = "b"
+
+ val data = createMediaData("app1", PLAYING, LOCAL, !RESUMPTION)
+
+ MediaPlayerData.addMediaPlayer(keyA, data, playerIsPlaying)
+ MediaPlayerData.addMediaPlayer(keyB, data, playerIsPlaying)
+
+ assertThat(MediaPlayerData.players()).hasSize(2)
+
+ MediaPlayerData.moveIfExists(keyA, keyB)
+
+ assertThat(MediaPlayerData.players()).hasSize(1)
+
+ assertThat(MediaPlayerData.getMediaPlayer(keyA)).isNull()
+ assertThat(MediaPlayerData.getMediaPlayer(keyB)).isNotNull()
+ }
+
private fun createMediaData(
app: String,
isPlaying: Boolean?,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt
index c6d7e92..b9caab2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt
@@ -185,7 +185,8 @@
filter.onMediaDataLoaded(KEY, null, mediaData1)
bgExecutor.runAllReady()
fgExecutor.runAllReady()
- verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true))
+ verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
+ eq(false))
}
@Test
@@ -207,7 +208,8 @@
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the event is not filtered
- verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true))
+ verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
+ eq(false))
}
@Test
@@ -236,7 +238,8 @@
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the event is not filtered
- verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true))
+ verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
+ eq(false))
}
@Test
@@ -251,14 +254,15 @@
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the event is not filtered
- verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true))
+ verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
+ eq(false))
// WHEN a loaded event is received that matches the local session
filter.onMediaDataLoaded(KEY, null, mediaData2)
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the event is filtered
verify(mediaListener, never()).onMediaDataLoaded(
- eq(KEY), eq(null), eq(mediaData2), anyBoolean())
+ eq(KEY), eq(null), eq(mediaData2), anyBoolean(), anyBoolean())
}
@Test
@@ -274,7 +278,8 @@
fgExecutor.runAllReady()
// THEN the event is not filtered because there isn't a notification for the remote
// session.
- verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true))
+ verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
+ eq(false))
}
@Test
@@ -291,14 +296,15 @@
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the event is not filtered
- verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true))
+ verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true),
+ eq(false))
// WHEN a loaded event is received that matches the local session
filter.onMediaDataLoaded(key2, null, mediaData2)
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the event is filtered
verify(mediaListener, never())
- .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean())
+ .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean(), anyBoolean())
// AND there should be a removed event for key2
verify(mediaListener).onMediaDataRemoved(eq(key2))
}
@@ -317,13 +323,15 @@
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the event is not filtered
- verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true))
+ verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true),
+ eq(false))
// WHEN a loaded event is received that matches the remote session
filter.onMediaDataLoaded(key2, null, mediaData2)
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the event is not filtered
- verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), eq(true))
+ verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), eq(true),
+ eq(false))
}
@Test
@@ -339,13 +347,15 @@
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the event is not filtered
- verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true))
+ verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
+ eq(false))
// WHEN a loaded event is received that matches the local session
filter.onMediaDataLoaded(KEY, null, mediaData2)
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the event is not filtered
- verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2), eq(true))
+ verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2), eq(true),
+ eq(false))
}
@Test
@@ -363,7 +373,8 @@
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the event is not filtered
- verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true))
+ verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true),
+ eq(false))
}
@Test
@@ -385,7 +396,8 @@
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the key migration event is fired
- verify(mediaListener).onMediaDataLoaded(eq(key2), eq(key1), eq(mediaData2), eq(true))
+ verify(mediaListener).onMediaDataLoaded(eq(key2), eq(key1), eq(mediaData2), eq(true),
+ eq(false))
}
@Test
@@ -415,12 +427,13 @@
fgExecutor.runAllReady()
// THEN the key migration event is filtered
verify(mediaListener, never())
- .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean())
+ .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean(), anyBoolean())
// WHEN a loaded event is received that matches the remote session
filter.onMediaDataLoaded(key2, null, mediaData1)
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the key migration event is fired
- verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData1), eq(true))
+ verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData1), eq(true),
+ eq(false))
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 6e21642..2c68661 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -135,8 +135,6 @@
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
@@ -154,8 +152,6 @@
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
@@ -176,9 +172,25 @@
assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
+ }
+
+ @Test
+ public void onBindViewHolder_bindConnectedDevice_withSelectableDevice_showAddIcon() {
+ when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(mMediaDevices);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
+ }
+
+ @Test
+ public void onBindViewHolder_bindConnectedDevice_withoutSelectableDevice_hideAddIcon() {
+ when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(new ArrayList<>());
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.GONE);
}
@Test
@@ -245,8 +257,6 @@
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
}
@@ -263,8 +273,6 @@
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mBottomDivider.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mViewHolder.mDivider.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mViewHolder.mAddIcon.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 589ae2e..9bd07b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -155,6 +155,7 @@
@Test
public void refresh_notInDragging_verifyUpdateAdapter() {
+ when(mMediaOutputBaseAdapter.getCurrentActivePosition()).thenReturn(-1);
when(mMediaOutputBaseAdapter.isDragging()).thenReturn(false);
mMediaOutputBaseDialogImpl.refresh();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index da63b8a..4980f74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -52,6 +52,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -108,7 +109,8 @@
Dependency.get(Dependency.MAIN_HANDLER),
mock(UiEventLogger.class),
mock(NavigationBarOverlayController.class),
- mock(ConfigurationController.class)));
+ mock(ConfigurationController.class),
+ mock(UserTracker.class)));
initializeNavigationBars();
}
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 4ec45b4..b1afeec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -76,6 +76,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -276,7 +277,8 @@
mock(SystemActions.class),
mHandler,
mock(NavigationBarOverlayController.class),
- mUiEventLogger));
+ mUiEventLogger,
+ mock(UserTracker.class)));
}
private void processAllMessages() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index 007a3b9..33c7a57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.people;
import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
+import static com.android.systemui.people.PeopleSpaceUtils.getContactLookupKeysWithBirthdaysToday;
import static com.google.common.truth.Truth.assertThat;
@@ -35,6 +36,7 @@
import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
+import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -64,12 +66,17 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.text.SimpleDateFormat;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -112,6 +119,7 @@
.setNotificationDataUri(URI)
.setMessagesCount(1)
.build();
+ private static final String TEST_DISPLAY_NAME = "Display Name";
private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
SHORTCUT_ID_1).setLongLabel(
@@ -227,6 +235,12 @@
.thenReturn(List.of(mNotificationEntry1, mNotificationEntry2, mNotificationEntry3));
}
+ @After
+ public void tearDown() {
+ cleanupTestContactFromContactProvider();
+ }
+
+
@Test
public void testAugmentTileFromNotification() {
PeopleSpaceTile tile =
@@ -467,4 +481,82 @@
eq(WIDGET_ID_WITH_SHORTCUT),
any());
}
+
+ @Test
+ public void testBirthdayQueriesWithYear() throws Exception {
+ String birthdayToday = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
+ addBirthdayToContactsDatabase(birthdayToday);
+
+ List<String> lookupKeys = getContactLookupKeysWithBirthdaysToday(mContext);
+
+ assertThat(lookupKeys).hasSize(1);
+ }
+
+ @Test
+ public void testBirthdayQueriesWithoutYear() throws Exception {
+ String birthdayToday = new SimpleDateFormat("--MM-dd").format(new Date());
+ addBirthdayToContactsDatabase(birthdayToday);
+
+ List<String> lookupKeys = getContactLookupKeysWithBirthdaysToday(mContext);
+
+ assertThat(lookupKeys).hasSize(1);
+ }
+
+ @Test
+ public void testBirthdayQueriesWithDifferentDates() throws Exception {
+ Date yesterday = new Date(System.currentTimeMillis() - Duration.ofDays(1).toMillis());
+ String birthdayYesterday = new SimpleDateFormat("--MM-dd").format(yesterday);
+ addBirthdayToContactsDatabase(birthdayYesterday);
+
+ List<String> lookupKeys = getContactLookupKeysWithBirthdaysToday(mContext);
+
+ assertThat(lookupKeys).isEmpty();
+ }
+
+ private void addBirthdayToContactsDatabase(String birthdayDate) throws Exception {
+ ContentResolver resolver = mContext.getContentResolver();
+ ArrayList<ContentProviderOperation> ops = new ArrayList<>(3);
+ ops.add(ContentProviderOperation
+ .newInsert(ContactsContract.RawContacts.CONTENT_URI)
+ .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, "com.google")
+ .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, "fakeAccountName")
+ .build());
+ ops.add(ContentProviderOperation
+ .newInsert(ContactsContract.Data.CONTENT_URI)
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+ .withValue(ContactsContract.Data.MIMETYPE,
+ ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
+ .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
+ TEST_DISPLAY_NAME)
+ .build());
+ ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+ .withValueBackReference(
+ ContactsContract.Data.RAW_CONTACT_ID, 0)
+ .withValue(
+ ContactsContract.Data.MIMETYPE,
+ ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE)
+ .withValue(
+ ContactsContract.CommonDataKinds.Event.TYPE,
+ ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY)
+ .withValue(
+ ContactsContract.CommonDataKinds.Event.START_DATE, birthdayDate)
+ .build());
+ resolver.applyBatch(ContactsContract.AUTHORITY, ops);
+ }
+
+ private void cleanupTestContactFromContactProvider() {
+ Cursor cursor = mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
+ null,
+ ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + "=?",
+ new String[]{TEST_DISPLAY_NAME},
+ null);
+ while (cursor.moveToNext()) {
+ String contactId = cursor.getString(cursor.getColumnIndex(
+ ContactsContract.Contacts.NAME_RAW_CONTACT_ID));
+ mContext.getContentResolver().delete(ContactsContract.Data.CONTENT_URI,
+ ContactsContract.Data.RAW_CONTACT_ID + "=?",
+ new String[]{contactId});
+ }
+ cursor.close();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
index 67505c4..e4e8cf0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
@@ -26,8 +26,6 @@
import static android.app.people.PeopleSpaceTile.SHOW_CONTACTS;
import static android.app.people.PeopleSpaceTile.SHOW_IMPORTANT_CONVERSATIONS;
import static android.app.people.PeopleSpaceTile.SHOW_STARRED_CONTACTS;
-import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT;
-import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
import static com.android.systemui.people.PeopleSpaceUtils.STARRED_CONTACT;
import static com.android.systemui.people.PeopleSpaceUtils.VALID_CONTACT;
@@ -35,7 +33,6 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -49,7 +46,6 @@
import android.content.res.Resources;
import android.graphics.drawable.Icon;
import android.net.Uri;
-import android.os.Bundle;
import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
import android.util.DisplayMetrics;
@@ -137,15 +133,14 @@
@Mock
private PackageManager mPackageManager;
- private Bundle mOptions;
+ private int mWidth;
+ private int mHeight;
private PeopleTileViewHelper mPeopleTileViewHelper;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mOptions = new Bundle();
-
when(mMockContext.getString(R.string.birthday_status)).thenReturn(
mContext.getString(R.string.birthday_status));
when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
@@ -160,25 +155,24 @@
TextView textView = mock(TextView.class);
when(textView.getLineHeight()).thenReturn(16);
when(mPackageManager.getApplicationIcon(anyString())).thenReturn(null);
- mPeopleTileViewHelper = getPeopleTileViewHelper(
- PERSON_TILE, mOptions);
+
+ mWidth = getSizeInDp(R.dimen.default_width);
+ mHeight = getSizeInDp(R.dimen.default_height);
+ mPeopleTileViewHelper = getPeopleTileViewHelper(PERSON_TILE);
}
@Test
public void testCreateRemoteViewsWithLastInteractionTimeUnderOneDayHidden() {
- RemoteViews views = getPeopleTileViewHelper(
- PERSON_TILE_WITHOUT_NOTIFICATION, mOptions).getViews();
+ RemoteViews views = getPeopleTileViewHelper(PERSON_TILE_WITHOUT_NOTIFICATION).getViews();
View result = views.apply(mContext, null);
// Not showing last interaction.
assertEquals(View.GONE, result.findViewById(R.id.last_interaction).getVisibility());
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
- getSizeInDp(R.dimen.required_height_for_large));
+ mWidth = getSizeInDp(R.dimen.required_width_for_large);
+ mHeight = getSizeInDp(R.dimen.required_height_for_large);
RemoteViews largeView = getPeopleTileViewHelper(
- PERSON_TILE_WITHOUT_NOTIFICATION, mOptions).getViews();
+ PERSON_TILE_WITHOUT_NOTIFICATION).getViews();
View largeResult = largeView.apply(mContext, null);
// Not showing last interaction.
@@ -214,8 +208,7 @@
PeopleSpaceTile tileWithLastInteraction =
PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setLastInteractionTimestamp(
123445L).build();
- RemoteViews views = getPeopleTileViewHelper(
- tileWithLastInteraction, mOptions).getViews();
+ RemoteViews views = getPeopleTileViewHelper(tileWithLastInteraction).getViews();
View result = views.apply(mContext, null);
TextView name = (TextView) result.findViewById(R.id.name);
@@ -231,10 +224,8 @@
// No status.
assertThat((View) result.findViewById(R.id.text_content)).isNull();
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.required_width_for_medium) - 1);
- RemoteViews smallView = getPeopleTileViewHelper(
- tileWithLastInteraction, mOptions).getViews();
+ mWidth = getSizeInDp(R.dimen.required_width_for_medium) - 1;
+ RemoteViews smallView = getPeopleTileViewHelper(tileWithLastInteraction).getViews();
View smallResult = smallView.apply(mContext, null);
// Show name over predefined icon.
@@ -246,12 +237,10 @@
assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
- getSizeInDp(R.dimen.required_height_for_large));
+ mWidth = getSizeInDp(R.dimen.required_width_for_large);
+ mHeight = getSizeInDp(R.dimen.required_height_for_large);
RemoteViews largeView = getPeopleTileViewHelper(
- tileWithLastInteraction, mOptions).getViews();
+ tileWithLastInteraction).getViews();
View largeResult = largeView.apply(mContext, null);
name = (TextView) largeResult.findViewById(R.id.name);
@@ -276,8 +265,7 @@
new ConversationStatus.Builder(
PERSON_TILE_WITHOUT_NOTIFICATION.getId(),
ACTIVITY_GAME).build())).build();
- RemoteViews views = getPeopleTileViewHelper(
- tileWithAvailabilityAndNewStory, mOptions).getViews();
+ RemoteViews views = getPeopleTileViewHelper(tileWithAvailabilityAndNewStory).getViews();
View result = views.apply(mContext, null);
TextView name = (TextView) result.findViewById(R.id.name);
@@ -291,10 +279,8 @@
// No status.
assertThat((View) result.findViewById(R.id.text_content)).isNull();
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.required_width_for_medium) - 1);
- RemoteViews smallView = getPeopleTileViewHelper(
- tileWithAvailabilityAndNewStory, mOptions).getViews();
+ mWidth = getSizeInDp(R.dimen.required_width_for_medium) - 1;
+ RemoteViews smallView = getPeopleTileViewHelper(tileWithAvailabilityAndNewStory).getViews();
View smallResult = smallView.apply(mContext, null);
// Show name rather than game type.
@@ -306,12 +292,9 @@
// No messages count.
assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
- getSizeInDp(R.dimen.required_height_for_large));
- RemoteViews largeView = getPeopleTileViewHelper(
- tileWithAvailabilityAndNewStory, mOptions).getViews();
+ mWidth = getSizeInDp(R.dimen.required_width_for_large);
+ mHeight = getSizeInDp(R.dimen.required_height_for_large);
+ RemoteViews largeView = getPeopleTileViewHelper(tileWithAvailabilityAndNewStory).getViews();
View largeResult = largeView.apply(mContext, null);
name = (TextView) largeResult.findViewById(R.id.name);
@@ -334,8 +317,7 @@
NEW_STORY_WITH_AVAILABILITY, new ConversationStatus.Builder(
PERSON_TILE_WITHOUT_NOTIFICATION.getId(),
ACTIVITY_BIRTHDAY).build())).build();
- RemoteViews views = getPeopleTileViewHelper(
- tileWithStatusTemplate, mOptions).getViews();
+ RemoteViews views = getPeopleTileViewHelper(tileWithStatusTemplate).getViews();
View result = views.apply(mContext, null);
TextView name = (TextView) result.findViewById(R.id.name);
@@ -352,10 +334,8 @@
assertEquals(statusContent.getText(), mContext.getString(R.string.birthday_status));
assertThat(statusContent.getMaxLines()).isEqualTo(2);
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.required_width_for_medium) - 1);
- RemoteViews smallView = getPeopleTileViewHelper(
- tileWithStatusTemplate, mOptions).getViews();
+ mWidth = getSizeInDp(R.dimen.required_width_for_medium) - 1;
+ RemoteViews smallView = getPeopleTileViewHelper(tileWithStatusTemplate).getViews();
View smallResult = smallView.apply(mContext, null);
// Show icon instead of name.
@@ -368,12 +348,9 @@
// No messages count.
assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
- getSizeInDp(R.dimen.required_height_for_large));
- RemoteViews largeView = getPeopleTileViewHelper(
- tileWithStatusTemplate, mOptions).getViews();
+ mWidth = getSizeInDp(R.dimen.required_width_for_large);
+ mHeight = getSizeInDp(R.dimen.required_height_for_large);
+ RemoteViews largeView = getPeopleTileViewHelper(tileWithStatusTemplate).getViews();
View largeResult = largeView.apply(mContext, null);
name = (TextView) largeResult.findViewById(R.id.name);
@@ -398,8 +375,7 @@
PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
Arrays.asList(GAME_STATUS,
NEW_STORY_WITH_AVAILABILITY)).build();
- RemoteViews views = getPeopleTileViewHelper(
- tileWithStatusTemplate, mOptions).getViews();
+ RemoteViews views = getPeopleTileViewHelper(tileWithStatusTemplate).getViews();
View result = views.apply(mContext, null);
TextView name = (TextView) result.findViewById(R.id.name);
@@ -417,10 +393,8 @@
assertEquals(statusContent.getText(), GAME_DESCRIPTION);
assertThat(statusContent.getMaxLines()).isEqualTo(2);
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.required_width_for_medium) - 1);
- RemoteViews smallView = getPeopleTileViewHelper(
- tileWithStatusTemplate, mOptions).getViews();
+ mWidth = getSizeInDp(R.dimen.required_width_for_medium) - 1;
+ RemoteViews smallView = getPeopleTileViewHelper(tileWithStatusTemplate).getViews();
View smallResult = smallView.apply(mContext, null);
// Show icon instead of name.
@@ -433,12 +407,9 @@
// No messages count.
assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
- getSizeInDp(R.dimen.required_height_for_large));
- RemoteViews largeView = getPeopleTileViewHelper(
- tileWithStatusTemplate, mOptions).getViews();
+ mWidth = getSizeInDp(R.dimen.required_width_for_large);
+ mHeight = getSizeInDp(R.dimen.required_height_for_large);
+ RemoteViews largeView = getPeopleTileViewHelper(tileWithStatusTemplate).getViews();
View largeResult = largeView.apply(mContext, null);
name = (TextView) largeResult.findViewById(R.id.name);
@@ -467,7 +438,7 @@
ACTIVITY_ANNIVERSARY).setDescription("Anniversary").setAvailability(
AVAILABILITY_AVAILABLE).setIcon(mIcon).build())).build();
RemoteViews views = getPeopleTileViewHelper(
- tileWithIconInStatusTemplate, mOptions).getViews();
+ tileWithIconInStatusTemplate).getViews();
View result = views.apply(mContext, null);
assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
@@ -483,12 +454,9 @@
assertEquals(statusContent.getText(), "Anniversary");
assertThat(statusContent.getMaxLines()).isEqualTo(1);
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
- getSizeInDp(R.dimen.required_height_for_large));
- RemoteViews largeView = getPeopleTileViewHelper(
- tileWithIconInStatusTemplate, mOptions).getViews();
+ mWidth = getSizeInDp(R.dimen.required_width_for_large);
+ mHeight = getSizeInDp(R.dimen.required_height_for_large);
+ RemoteViews largeView = getPeopleTileViewHelper(tileWithIconInStatusTemplate).getViews();
View largeResult = largeView.apply(mContext, null);
assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
@@ -513,8 +481,7 @@
PeopleSpaceTile tile = PERSON_TILE.toBuilder()
.setIsPackageSuspended(true)
.build();
- RemoteViews views = getPeopleTileViewHelper(
- tile, mOptions).getViews();
+ RemoteViews views = getPeopleTileViewHelper(tile).getViews();
View result = views.apply(mContext, null);
assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
@@ -525,8 +492,7 @@
PeopleSpaceTile tile = PERSON_TILE.toBuilder()
.setIsUserQuieted(true)
.build();
- RemoteViews views = getPeopleTileViewHelper(
- tile, mOptions).getViews();
+ RemoteViews views = getPeopleTileViewHelper(tile).getViews();
View result = views.apply(mContext, null);
assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_work_profile_quiet_layout);
@@ -537,89 +503,100 @@
PeopleSpaceTile tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(BLOCK_CONVERSATIONS)
.build();
- RemoteViews views = getPeopleTileViewHelper(
- tileWithDndBlocking, mOptions).getViews();
+ RemoteViews views = getPeopleTileViewHelper(tileWithDndBlocking).getViews();
View result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
+ assertResourcesEqual(
+ result.getSourceLayoutResId(),
+ R.layout.people_tile_with_suppression_detail_content_horizontal);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(BLOCK_CONVERSATIONS)
.setCanBypassDnd(true)
.build();
- views = getPeopleTileViewHelper(
- tileWithDndBlocking, mOptions).getViews();
+ views = getPeopleTileViewHelper(tileWithDndBlocking).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
+ assertResourcesNotEqual(
+ result.getSourceLayoutResId(),
+ R.layout.people_tile_with_suppression_detail_content_horizontal);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_IMPORTANT_CONVERSATIONS)
.build();
- views = getPeopleTileViewHelper(
- tileWithDndBlocking, mOptions).getViews();
+ views = getPeopleTileViewHelper(tileWithDndBlocking).getViews();
result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
+ assertResourcesEqual(
+ result.getSourceLayoutResId(),
+ R.layout.people_tile_with_suppression_detail_content_horizontal);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_IMPORTANT_CONVERSATIONS)
.setIsImportantConversation(true)
.build();
- views = getPeopleTileViewHelper(
- tileWithDndBlocking, mOptions).getViews();
+ views = getPeopleTileViewHelper(tileWithDndBlocking).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
+ assertResourcesNotEqual(
+ result.getSourceLayoutResId(),
+ R.layout.people_tile_with_suppression_detail_content_horizontal);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_STARRED_CONTACTS)
.setContactAffinity(VALID_CONTACT)
.build();
- views = getPeopleTileViewHelper(
- tileWithDndBlocking, mOptions).getViews();
+ views = getPeopleTileViewHelper(tileWithDndBlocking).getViews();
result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
+ assertResourcesEqual(
+ result.getSourceLayoutResId(),
+ R.layout.people_tile_with_suppression_detail_content_horizontal);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_STARRED_CONTACTS)
.setContactAffinity(STARRED_CONTACT)
.build();
- views = getPeopleTileViewHelper(
- tileWithDndBlocking, mOptions).getViews();
+ views = getPeopleTileViewHelper(tileWithDndBlocking).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
+ assertResourcesNotEqual(
+ result.getSourceLayoutResId(),
+ R.layout.people_tile_with_suppression_detail_content_horizontal);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_CONTACTS)
.setContactAffinity(STARRED_CONTACT)
.build();
- views = getPeopleTileViewHelper(
- tileWithDndBlocking, mOptions).getViews();
+ views = getPeopleTileViewHelper(tileWithDndBlocking).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
+ assertResourcesNotEqual(
+ result.getSourceLayoutResId(),
+ R.layout.people_tile_with_suppression_detail_content_horizontal);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_CONTACTS)
.setContactAffinity(VALID_CONTACT)
.build();
- views = getPeopleTileViewHelper(
- tileWithDndBlocking, mOptions).getViews();
+ views = getPeopleTileViewHelper(tileWithDndBlocking).getViews();
result = views.apply(mContext, null);
- assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
+ assertResourcesNotEqual(
+ result.getSourceLayoutResId(),
+ R.layout.people_tile_with_suppression_detail_content_horizontal);
tileWithDndBlocking = PERSON_TILE.toBuilder()
.setNotificationPolicyState(SHOW_CONTACTS)
.build();
- views = getPeopleTileViewHelper(
- tileWithDndBlocking, mOptions).getViews();
+ views = getPeopleTileViewHelper(tileWithDndBlocking).getViews();
result = views.apply(mContext, null);
- assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout);
+ assertResourcesEqual(
+ result.getSourceLayoutResId(),
+ R.layout.people_tile_with_suppression_detail_content_horizontal);
+ assertThat(result.<TextView>findViewById(R.id.text_content).getText().toString())
+ .isEqualTo(mContext.getString(R.string.paused_by_dnd));
}
@Test
@@ -629,8 +606,7 @@
.setNotificationCategory(CATEGORY_MISSED_CALL)
.setNotificationContent(MISSED_CALL)
.build();
- RemoteViews views = getPeopleTileViewHelper(
- tileWithMissedCallNotification, mOptions).getViews();
+ RemoteViews views = getPeopleTileViewHelper(tileWithMissedCallNotification).getViews();
View result = views.apply(mContext, null);
TextView name = (TextView) result.findViewById(R.id.name);
@@ -647,10 +623,9 @@
assertEquals(statusContent.getText(), MISSED_CALL);
assertThat(statusContent.getMaxLines()).isEqualTo(2);
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.required_width_for_medium) - 1);
+ mWidth = getSizeInDp(R.dimen.required_width_for_medium) - 1;
RemoteViews smallView = getPeopleTileViewHelper(
- tileWithMissedCallNotification, mOptions).getViews();
+ tileWithMissedCallNotification).getViews();
View smallResult = smallView.apply(mContext, null);
// Show icon instead of name.
@@ -662,12 +637,9 @@
// No messages count.
assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
- getSizeInDp(R.dimen.required_height_for_large));
- RemoteViews largeView = getPeopleTileViewHelper(
- tileWithMissedCallNotification, mOptions).getViews();
+ mWidth = getSizeInDp(R.dimen.required_width_for_large);
+ mHeight = getSizeInDp(R.dimen.required_height_for_large);
+ RemoteViews largeView = getPeopleTileViewHelper(tileWithMissedCallNotification).getViews();
View largeResult = largeView.apply(mContext, null);
name = (TextView) largeResult.findViewById(R.id.name);
@@ -693,7 +665,7 @@
.setStatuses(Arrays.asList(GAME_STATUS,
NEW_STORY_WITH_AVAILABILITY)).build();
RemoteViews views = getPeopleTileViewHelper(
- tileWithStatusAndNotification, mOptions).getViews();
+ tileWithStatusAndNotification).getViews();
View result = views.apply(mContext, null);
TextView name = (TextView) result.findViewById(R.id.name);
@@ -714,10 +686,9 @@
// Has a single message, no count shown.
assertEquals(View.GONE, result.findViewById(R.id.messages_count).getVisibility());
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.required_width_for_medium) - 1);
+ mWidth = getSizeInDp(R.dimen.required_width_for_medium) - 1;
RemoteViews smallView = getPeopleTileViewHelper(
- tileWithStatusAndNotification, mOptions).getViews();
+ tileWithStatusAndNotification).getViews();
View smallResult = smallView.apply(mContext, null);
// Show icon instead of name.
@@ -731,12 +702,10 @@
// Has a single message, no count shown.
assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
- getSizeInDp(R.dimen.required_height_for_large));
+ mWidth = getSizeInDp(R.dimen.required_width_for_large);
+ mHeight = getSizeInDp(R.dimen.required_height_for_large);
RemoteViews largeView = getPeopleTileViewHelper(
- tileWithStatusAndNotification, mOptions).getViews();
+ tileWithStatusAndNotification).getViews();
View largeResult = largeView.apply(mContext, null);
name = (TextView) largeResult.findViewById(R.id.name);
@@ -767,7 +736,7 @@
.setStatuses(Arrays.asList(GAME_STATUS,
NEW_STORY_WITH_AVAILABILITY)).build();
RemoteViews views = getPeopleTileViewHelper(
- tileWithStatusAndNotification, mOptions).getViews();
+ tileWithStatusAndNotification).getViews();
View result = views.apply(mContext, null);
TextView name = (TextView) result.findViewById(R.id.name);
@@ -791,10 +760,9 @@
// Has a single message, no count shown.
assertEquals(View.GONE, result.findViewById(R.id.messages_count).getVisibility());
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.required_width_for_medium) - 1);
+ mWidth = getSizeInDp(R.dimen.required_width_for_medium) - 1;
RemoteViews smallView = getPeopleTileViewHelper(
- tileWithStatusAndNotification, mOptions).getViews();
+ tileWithStatusAndNotification).getViews();
View smallResult = smallView.apply(mContext, null);
// Show icon instead of name.
@@ -808,12 +776,10 @@
// Has a single message, no count shown.
assertEquals(View.GONE, smallResult.findViewById(R.id.messages_count).getVisibility());
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
- getSizeInDp(R.dimen.required_height_for_large));
+ mWidth = getSizeInDp(R.dimen.required_width_for_large);
+ mHeight = getSizeInDp(R.dimen.required_height_for_large);
RemoteViews largeView = getPeopleTileViewHelper(
- tileWithStatusAndNotification, mOptions).getViews();
+ tileWithStatusAndNotification).getViews();
View largeResult = largeView.apply(mContext, null);
name = (TextView) largeResult.findViewById(R.id.name);
@@ -848,7 +814,7 @@
NEW_STORY_WITH_AVAILABILITY))
.setMessagesCount(2).build();
RemoteViews views = getPeopleTileViewHelper(
- tileWithStatusAndNotification, mOptions).getViews();
+ tileWithStatusAndNotification).getViews();
View result = views.apply(mContext, null);
TextView name = (TextView) result.findViewById(R.id.name);
@@ -868,10 +834,9 @@
// Has two messages, show count.
assertEquals(View.VISIBLE, result.findViewById(R.id.messages_count).getVisibility());
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.required_width_for_medium) - 1);
+ mWidth = getSizeInDp(R.dimen.required_width_for_medium) - 1;
RemoteViews smallView = getPeopleTileViewHelper(
- tileWithStatusAndNotification, mOptions).getViews();
+ tileWithStatusAndNotification).getViews();
View smallResult = smallView.apply(mContext, null);
// Show icon instead of name.
@@ -885,12 +850,10 @@
// Has two messages, show count.
assertEquals(View.VISIBLE, smallResult.findViewById(R.id.messages_count).getVisibility());
- mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
- getSizeInDp(R.dimen.required_width_for_large));
- mOptions.putInt(OPTION_APPWIDGET_MAX_HEIGHT,
- getSizeInDp(R.dimen.required_height_for_large));
+ mWidth = getSizeInDp(R.dimen.required_width_for_large);
+ mHeight = getSizeInDp(R.dimen.required_height_for_large);
RemoteViews largeView = getPeopleTileViewHelper(
- tileWithStatusAndNotification, mOptions).getViews();
+ tileWithStatusAndNotification).getViews();
View largeResult = largeView.apply(mContext, null);
name = (TextView) largeResult.findViewById(R.id.name);
@@ -1064,8 +1027,26 @@
/ mContext.getResources().getDisplayMetrics().density);
}
- private PeopleTileViewHelper getPeopleTileViewHelper(PeopleSpaceTile tile, Bundle options) {
- return new PeopleTileViewHelper(mContext, tile, 0, options,
+ private PeopleTileViewHelper getPeopleTileViewHelper(
+ PeopleSpaceTile tile) {
+ return new PeopleTileViewHelper(mContext, tile, 0, mWidth, mHeight,
new PeopleTileKey(tile.getId(), 0, tile.getPackageName()));
}
+
+ private void assertResourcesEqual(int expected, int actual) {
+ assertThat(getResourceName(actual)).isEqualTo(getResourceName(expected));
+ }
+
+ private void assertResourcesNotEqual(int expected, int actual) {
+ assertThat(getResourceName(actual)).isNotEqualTo(getResourceName(expected));
+ }
+
+ private String getResourceName(int resId) {
+ Resources resources = mContext.getResources();
+ try {
+ return resources.getResourceEntryName(resId);
+ } catch (Resources.NotFoundException e) {
+ return String.valueOf(resId);
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
index 5f4d90b..f6264ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
@@ -49,6 +49,7 @@
import com.android.wm.shell.bubbles.Bubble;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -213,6 +214,7 @@
verify(mBubblesManager, never()).expandStackAndSelectBubble(any(NotificationEntry.class));
}
+ @Ignore
@Test
public void testBubbleWithNoNotifOpensBubble() throws Exception {
Bubble bubble = mock(Bubble.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 7af3743..c48f26b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -43,6 +43,7 @@
import static android.app.people.PeopleSpaceTile.SHOW_STARRED_CONTACTS;
import static android.content.Intent.ACTION_BOOT_COMPLETED;
import static android.content.Intent.ACTION_PACKAGES_SUSPENDED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.PermissionChecker.PERMISSION_GRANTED;
import static android.content.PermissionChecker.PERMISSION_HARD_DENIED;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE;
@@ -88,7 +89,6 @@
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.notification.ConversationChannelWrapper;
@@ -1104,7 +1104,7 @@
}
@Test
- public void testGetPeopleTileFromPersistentStorageNoConversation() throws RemoteException {
+ public void testGetPeopleTileFromPersistentStorageNoConversation() throws Exception {
when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(null);
PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A);
PeopleSpaceTile tile = mManager.getTileFromPersistentStorage(key, WIDGET_ID_WITH_SHORTCUT);
@@ -1223,8 +1223,38 @@
}
@Test
- public void testUpdateWidgetsOnStateChange() {
- mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED);
+ public void testUpdateWidgetsFromBroadcastInBackgroundBootCompleteWithPackageUninstalled()
+ throws Exception {
+ when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenThrow(
+ PackageManager.NameNotFoundException.class);
+
+ // We should remove widgets if the package is uninstalled at next reboot if we missed the
+ // package removed broadcast.
+ mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
+
+ PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
+ assertThat(tile).isNull();
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ }
+
+ @Test
+ public void testUpdateWidgetsFromBroadcastInBackgroundPackageRemovedWithPackageUninstalled()
+ throws Exception {
+ when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenThrow(
+ PackageManager.NameNotFoundException.class);
+
+ mManager.updateWidgetsFromBroadcastInBackground(ACTION_PACKAGE_REMOVED);
+
+ PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
+ assertThat(tile).isNull();
+ verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+ any());
+ }
+
+ @Test
+ public void testUpdateWidgetsFromBroadcastInBackground() {
+ mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
assertThat(tile.isPackageSuspended()).isFalse();
@@ -1236,10 +1266,10 @@
}
@Test
- public void testUpdateWidgetsOnStateChangeWithUserQuieted() {
+ public void testUpdateWidgetsFromBroadcastInBackgroundWithUserQuieted() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
- mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED);
+ mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
assertThat(tile.isPackageSuspended()).isFalse();
@@ -1248,10 +1278,10 @@
}
@Test
- public void testUpdateWidgetsOnStateChangeWithPackageSuspended() throws Exception {
+ public void testUpdateWidgetsFromBroadcastInBackgroundWithPackageSuspended() throws Exception {
when(mPackageManager.isPackageSuspended(any())).thenReturn(true);
- mManager.updateWidgetsOnStateChange(ACTION_PACKAGES_SUSPENDED);
+ mManager.updateWidgetsFromBroadcastInBackground(ACTION_PACKAGES_SUSPENDED);
PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
assertThat(tile.isPackageSuspended()).isTrue();
@@ -1260,9 +1290,9 @@
}
@Test
- public void testUpdateWidgetsOnStateChangeNotInDnd() {
+ public void testUpdateWidgetsFromBroadcastInBackgroundNotInDnd() {
int expected = 0;
- mManager.updateWidgetsOnStateChange(NotificationManager
+ mManager.updateWidgetsFromBroadcastInBackground(NotificationManager
.ACTION_INTERRUPTION_FILTER_CHANGED);
PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
@@ -1270,14 +1300,14 @@
}
@Test
- public void testUpdateWidgetsOnStateChangeAllConversations() {
+ public void testUpdateWidgetsFromBroadcastInBackgroundAllConversations() {
int expected = 0;
when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
INTERRUPTION_FILTER_PRIORITY);
when(mNotificationPolicy.allowConversations()).thenReturn(true);
setFinalField("priorityConversationSenders", CONVERSATION_SENDERS_ANYONE);
- mManager.updateWidgetsOnStateChange(NotificationManager
+ mManager.updateWidgetsFromBroadcastInBackground(NotificationManager
.ACTION_INTERRUPTION_FILTER_CHANGED);
PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
@@ -1285,7 +1315,7 @@
}
@Test
- public void testUpdateWidgetsOnStateChangeAllowOnlyImportantConversations() {
+ public void testUpdateWidgetsFromBroadcastInBackgroundAllowOnlyImportantConversations() {
int expected = 0;
// Only allow important conversations.
when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
@@ -1293,7 +1323,7 @@
when(mNotificationPolicy.allowConversations()).thenReturn(true);
setFinalField("priorityConversationSenders", CONVERSATION_SENDERS_IMPORTANT);
- mManager.updateWidgetsOnStateChange(NotificationManager
+ mManager.updateWidgetsFromBroadcastInBackground(NotificationManager
.ACTION_INTERRUPTION_FILTER_CHANGED);
PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
@@ -1302,13 +1332,13 @@
}
@Test
- public void testUpdateWidgetsOnStateChangeAllowNoConversations() {
+ public void testUpdateWidgetsFromBroadcastInBackgroundAllowNoConversations() {
int expected = 0;
when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
INTERRUPTION_FILTER_PRIORITY);
when(mNotificationPolicy.allowConversations()).thenReturn(false);
- mManager.updateWidgetsOnStateChange(NotificationManager
+ mManager.updateWidgetsFromBroadcastInBackground(NotificationManager
.ACTION_INTERRUPTION_FILTER_CHANGED);
PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
@@ -1316,7 +1346,7 @@
}
@Test
- public void testUpdateWidgetsOnStateChangeAllowNoConversationsAllowContactMessages() {
+ public void testUpdateWidgetsFromBroadcastInBackgroundAllowNoConversationsAllowContactMessages() {
int expected = 0;
when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
INTERRUPTION_FILTER_PRIORITY);
@@ -1324,14 +1354,14 @@
when(mNotificationPolicy.allowMessagesFrom()).thenReturn(ZenModeConfig.SOURCE_CONTACT);
when(mNotificationPolicy.allowMessages()).thenReturn(true);
- mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED);
+ mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONTACTS);
}
@Test
- public void testUpdateWidgetsOnStateChangeAllowNoConversationsAllowStarredContactMessages() {
+ public void testUpdateWidgetsFromBroadcastInBackgroundAllowNoConversationsAllowStarredContactMessages() {
int expected = 0;
when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
INTERRUPTION_FILTER_PRIORITY);
@@ -1339,26 +1369,26 @@
when(mNotificationPolicy.allowMessagesFrom()).thenReturn(ZenModeConfig.SOURCE_STAR);
when(mNotificationPolicy.allowMessages()).thenReturn(true);
- mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED);
+ mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_STARRED_CONTACTS);
setFinalField("suppressedVisualEffects", SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
| SUPPRESSED_EFFECT_AMBIENT);
- mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED);
+ mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONVERSATIONS);
}
@Test
- public void testUpdateWidgetsOnStateChangeAllowAlarmsOnly() {
+ public void testUpdateWidgetsFromBroadcastInBackgroundAllowAlarmsOnly() {
int expected = 0;
when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
INTERRUPTION_FILTER_ALARMS);
- mManager.updateWidgetsOnStateChange(NotificationManager
+ mManager.updateWidgetsFromBroadcastInBackground(NotificationManager
.ACTION_INTERRUPTION_FILTER_CHANGED);
PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
@@ -1366,7 +1396,7 @@
}
@Test
- public void testUpdateWidgetsOnStateChangeAllowVisualEffectsAndAllowAlarmsOnly() {
+ public void testUpdateWidgetsFromBroadcastInBackgroundAllowVisualEffectsAndAllowAlarmsOnly() {
int expected = 0;
// If we show visuals, but just only make sounds for alarms, still show content in tiles.
when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn(
@@ -1374,7 +1404,7 @@
setFinalField("suppressedVisualEffects", SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
| SUPPRESSED_EFFECT_AMBIENT);
- mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED);
+ mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED);
PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT);
assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONVERSATIONS);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index b0e3e3e..2ae4cbe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -46,6 +46,7 @@
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.dagger.QSFragmentComponent;
+import com.android.systemui.qs.external.CustomTileStatePersister;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.settings.UserTracker;
@@ -132,7 +133,7 @@
() -> mock(AutoTileManager.class), mock(DumpManager.class),
mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)),
mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class),
- mock(SecureSettings.class));
+ mock(SecureSettings.class), mock(CustomTileStatePersister.class));
qs.setHost(host);
qs.setListening(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 7c73b4c..69bdcbcf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -59,6 +59,8 @@
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.qs.external.CustomTileStatePersister;
+import com.android.systemui.qs.external.TileServiceKey;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.UserTracker;
@@ -125,6 +127,8 @@
private UserTracker mUserTracker;
@Mock
private SecureSettings mSecureSettings;
+ @Mock
+ private CustomTileStatePersister mCustomTileStatePersister;
private Handler mHandler;
private TestableLooper mLooper;
@@ -145,7 +149,7 @@
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker,
- mSecureSettings);
+ mSecureSettings, mCustomTileStatePersister);
setUpTileFactory();
when(mSecureSettings.getStringForUser(eq(QSTileHost.TILES_SETTING), anyInt()))
@@ -371,6 +375,14 @@
verify(mQSLogger, never()).logTileDestroyed(isNull(), anyString());
}
+ @Test
+ public void testCustomTileRemoved_stateDeleted() {
+ mQSTileHost.changeTiles(List.of(CUSTOM_TILE_SPEC), List.of());
+
+ verify(mCustomTileStatePersister)
+ .removeState(new TileServiceKey(CUSTOM_TILE, mQSTileHost.getUserId()));
+ }
+
private class TestQSTileHost extends QSTileHost {
TestQSTileHost(Context context, StatusBarIconController iconController,
QSFactory defaultFactory, Handler mainHandler, Looper bgLooper,
@@ -378,10 +390,11 @@
Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger,
UiEventLogger uiEventLogger, UserTracker userTracker,
- SecureSettings secureSettings) {
+ SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister) {
super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
tunerService, autoTiles, dumpManager, broadcastDispatcher,
- Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings);
+ Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings,
+ customTileStatePersister);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
new file mode 100644
index 0000000..6c96576
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.external
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.SharedPreferences
+import android.service.quicksettings.Tile
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Answers
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class CustomTileStatePersisterTest : SysuiTestCase() {
+
+ companion object {
+ private val TEST_COMPONENT = ComponentName("pkg", "cls")
+ private const val TEST_USER = 0
+ private val KEY = TileServiceKey(TEST_COMPONENT, TEST_USER)
+
+ private const val TEST_STATE = Tile.STATE_INACTIVE
+ private const val TEST_LABEL = "test_label"
+ private const val TEST_SUBTITLE = "test_subtitle"
+ private const val TEST_CONTENT_DESCRIPTION = "test_content_description"
+ private const val TEST_STATE_DESCRIPTION = "test_state_description"
+
+ private fun Tile.isEqualTo(other: Tile): Boolean {
+ return state == other.state &&
+ label == other.label &&
+ subtitle == other.subtitle &&
+ contentDescription == other.contentDescription &&
+ stateDescription == other.stateDescription
+ }
+ }
+
+ @Mock
+ private lateinit var mockContext: Context
+ @Mock
+ private lateinit var sharedPreferences: SharedPreferences
+ @Mock(answer = Answers.RETURNS_SELF)
+ private lateinit var editor: SharedPreferences.Editor
+ private lateinit var tile: Tile
+ private lateinit var customTileStatePersister: CustomTileStatePersister
+
+ @Captor
+ private lateinit var stringCaptor: ArgumentCaptor<String>
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ `when`(mockContext.getSharedPreferences(anyString(), anyInt()))
+ .thenReturn(sharedPreferences)
+ `when`(sharedPreferences.edit()).thenReturn(editor)
+
+ tile = Tile()
+ customTileStatePersister = CustomTileStatePersister(mockContext)
+ }
+
+ @Test
+ fun testWriteState() {
+ tile.apply {
+ state = TEST_STATE
+ label = TEST_LABEL
+ subtitle = TEST_SUBTITLE
+ contentDescription = TEST_CONTENT_DESCRIPTION
+ stateDescription = TEST_STATE_DESCRIPTION
+ }
+
+ customTileStatePersister.persistState(KEY, tile)
+
+ verify(editor).putString(eq(KEY.toString()), capture(stringCaptor))
+
+ assertThat(tile.isEqualTo(readTileFromString(stringCaptor.value))).isTrue()
+ }
+
+ @Test
+ fun testReadState() {
+ tile.apply {
+ state = TEST_STATE
+ label = TEST_LABEL
+ subtitle = TEST_SUBTITLE
+ contentDescription = TEST_CONTENT_DESCRIPTION
+ stateDescription = TEST_STATE_DESCRIPTION
+ }
+
+ `when`(sharedPreferences.getString(eq(KEY.toString()), any()))
+ .thenReturn(writeToString(tile))
+
+ assertThat(tile.isEqualTo(customTileStatePersister.readState(KEY)!!)).isTrue()
+ }
+
+ @Test
+ fun testReadStateDefault() {
+ `when`(sharedPreferences.getString(any(), any())).thenAnswer {
+ it.getArgument(1)
+ }
+
+ assertThat(customTileStatePersister.readState(KEY)).isNull()
+ }
+
+ @Test
+ fun testStoreNulls() {
+ assertThat(tile.label).isNull()
+
+ customTileStatePersister.persistState(KEY, tile)
+
+ verify(editor).putString(eq(KEY.toString()), capture(stringCaptor))
+
+ assertThat(readTileFromString(stringCaptor.value).label).isNull()
+ }
+
+ @Test
+ fun testReadNulls() {
+ assertThat(tile.label).isNull()
+
+ `when`(sharedPreferences.getString(eq(KEY.toString()), any()))
+ .thenReturn(writeToString(tile))
+
+ assertThat(customTileStatePersister.readState(KEY)!!.label).isNull()
+ }
+
+ @Test
+ fun testRemoveState() {
+ customTileStatePersister.removeState(KEY)
+
+ verify(editor).remove(KEY.toString())
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index b1c3d1d..9b5c161 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -38,6 +38,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.util.mockito.any
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
@@ -48,8 +49,9 @@
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito.`when`
-import org.mockito.Mockito.any
import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
@@ -76,6 +78,7 @@
@Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var applicationInfo: ApplicationInfo
@Mock private lateinit var serviceInfo: ServiceInfo
+ @Mock private lateinit var customTileStatePersister: CustomTileStatePersister
private lateinit var customTile: CustomTile
private lateinit var testableLooper: TestableLooper
@@ -108,10 +111,13 @@
metricsLogger,
statusBarStateController,
activityStarter,
- qsLogger
+ qsLogger,
+ customTileStatePersister
)
customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ customTile.initialize()
+ testableLooper.processAllMessages()
}
@Test
@@ -123,6 +129,8 @@
`when`(userContext.userId).thenReturn(10)
val tile = CustomTile.create(customTileBuilder, TILE_SPEC, userContext)
+ tile.initialize()
+ testableLooper.processAllMessages()
assertEquals(10, tile.user)
}
@@ -131,6 +139,8 @@
fun testToggleableTileHasBooleanState() {
`when`(tileServiceManager.isToggleableTile).thenReturn(true)
customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ customTile.initialize()
+ testableLooper.processAllMessages()
assertTrue(customTile.state is QSTile.BooleanState)
assertTrue(customTile.newTileState() is QSTile.BooleanState)
@@ -146,6 +156,9 @@
fun testValueUpdatedInBooleanTile() {
`when`(tileServiceManager.isToggleableTile).thenReturn(true)
customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ customTile.initialize()
+ testableLooper.processAllMessages()
+
customTile.qsTile.icon = mock(Icon::class.java)
`when`(customTile.qsTile.icon.loadDrawable(any(Context::class.java)))
.thenReturn(mock(Drawable::class.java))
@@ -173,4 +186,88 @@
.thenReturn(null)
customTile.handleUpdateState(customTile.newTileState(), null)
}
+
+ @Test
+ fun testNoLoadStateTileNotActive() {
+ // Not active by default
+ testableLooper.processAllMessages()
+
+ verify(customTileStatePersister, never()).readState(any())
+ }
+
+ @Test
+ fun testNoPersistedStateTileNotActive() {
+ // Not active by default
+ val t = Tile().apply {
+ state = Tile.STATE_INACTIVE
+ }
+ customTile.updateTileState(t)
+ testableLooper.processAllMessages()
+
+ verify(customTileStatePersister, never()).persistState(any(), any())
+ }
+
+ @Test
+ fun testPersistedStateRetrieved() {
+ val state = Tile.STATE_INACTIVE
+ val label = "test_label"
+ val subtitle = "test_subtitle"
+ val contentDescription = "test_content_description"
+ val stateDescription = "test_state_description"
+
+ val t = Tile().apply {
+ this.state = state
+ this.label = label
+ this.subtitle = subtitle
+ this.contentDescription = contentDescription
+ this.stateDescription = stateDescription
+ }
+ `when`(tileServiceManager.isActiveTile).thenReturn(true)
+ `when`(customTileStatePersister
+ .readState(TileServiceKey(componentName, customTile.user))).thenReturn(t)
+ val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ tile.initialize()
+ testableLooper.processAllMessages()
+
+ // Make sure we have an icon in the tile because we don't have a default icon
+ // This should not be overridden by the retrieved tile that has null icon.
+ tile.qsTile.icon = mock(Icon::class.java)
+ `when`(tile.qsTile.icon.loadDrawable(any(Context::class.java)))
+ .thenReturn(mock(Drawable::class.java))
+
+ tile.refreshState()
+
+ testableLooper.processAllMessages()
+
+ val tileState = tile.state
+
+ assertEquals(state, tileState.state)
+ assertEquals(label, tileState.label)
+ assertEquals(subtitle, tileState.secondaryLabel)
+ assertEquals(contentDescription, tileState.contentDescription)
+ assertEquals(stateDescription, tileState.stateDescription)
+ }
+
+ @Test
+ fun testStoreStateOnChange() {
+ val t = Tile().apply {
+ state = Tile.STATE_INACTIVE
+ label = "test_label"
+ subtitle = "test_subtitle"
+ contentDescription = "test_content_description"
+ stateDescription = "test_state_description"
+ }
+ `when`(tileServiceManager.isActiveTile).thenReturn(true)
+
+ val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
+ tile.initialize()
+ testableLooper.processAllMessages()
+
+ tile.updateTileState(t)
+
+ testableLooper.processAllMessages()
+
+ verify(customTileStatePersister)
+ .persistState(TileServiceKey(componentName, customTile.user), t)
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 641f917..2b18404 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -118,7 +118,8 @@
mQSLogger,
mUiEventLogger,
mUserTracker,
- mSecureSettings);
+ mSecureSettings,
+ mock(CustomTileStatePersister.class));
mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher,
mUserTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 80231a4..ea4d7cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -121,6 +121,9 @@
mTile = new TileImpl(mHost, mTestableLooper.getLooper(), mainHandler, mFalsingManager,
mMetricsLogger, mStatusBarStateController, mActivityStarter, mQsLogger);
+ mTile.initialize();
+ mTestableLooper.processAllMessages();
+
mTile.setTileSpec(SPEC);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt
index 19ffa49..b8d018f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.qs.tileimpl
import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
+import androidx.test.filters.MediumTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -27,7 +27,7 @@
import org.junit.runner.RunWith
@RunWith(AndroidTestingRunner::class)
-@SmallTest
+@MediumTest
class TilesStatesTextTest : SysuiTestCase() {
@Test
@@ -51,4 +51,29 @@
assertThat(array.size).isEqualTo(3)
}
+
+ @Test
+ fun testStockTilesSubtitlesMap() {
+ val tiles = mContext.getString(R.string.quick_settings_tiles_stock).split(",")
+ tiles.forEach { spec ->
+ val resName = "${QSTileViewImpl.TILE_STATE_RES_PREFIX}$spec"
+ val resId = mContext.resources.getIdentifier(resName, "array", mContext.packageName)
+
+ assertNotEquals("Missing resource for $resName", 0, resId)
+
+ assertThat(SubtitleArrayMapping.getSubtitleId(spec)).isEqualTo(resId)
+ }
+ }
+
+ @Test
+ fun testStockTilesSubtitlesReturnsDefault_unknown() {
+ assertThat(SubtitleArrayMapping.getSubtitleId("unknown"))
+ .isEqualTo(R.array.tile_states_default)
+ }
+
+ @Test
+ fun testStockTilesSubtitlesReturnsDefault_null() {
+ assertThat(SubtitleArrayMapping.getSubtitleId(null))
+ .isEqualTo(R.array.tile_states_default)
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
index 32b1f43..5e2d8fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
@@ -80,6 +80,8 @@
nextAlarmController
)
+ tile.initialize()
+
verify(nextAlarmController).observe(eq(tile), capture(callbackCaptor))
tile.refreshState()
testableLooper.processAllMessages()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
index f17bd56..1bf8351 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
@@ -87,6 +87,9 @@
qsLogger,
batteryController,
secureSettings)
+
+ tile.initialize()
+ testableLooper.processAllMessages()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 7c1a5f5..d44a526 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -112,11 +112,14 @@
mNetworkController,
mHotspotController
);
+ mCastTile.initialize();
// We are not setting the mocks to listening, so we trigger a first refresh state to
// set the initial state
mCastTile.refreshState();
+ mTestableLooper.processAllMessages();
+
mCastTile.handleSetListening(true);
ArgumentCaptor<NetworkController.SignalCallback> signalCallbackArgumentCaptor =
ArgumentCaptor.forClass(NetworkController.SignalCallback.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index 6d1bbd9..94af10a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -378,7 +378,10 @@
qsLogger,
controlsComponent,
keyguardStateController
- )
+ ).also {
+ it.initialize()
+ testableLooper.processAllMessages()
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
index 99d028c..cfd3735 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
@@ -92,6 +92,9 @@
mQSLogger,
mBroadcastDispatcher
);
+
+ mNfcTile.initialize();
+ mTestableLooper.processAllMessages();
}
@Test
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 e4a9aac..a50cbe5 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
@@ -155,6 +155,9 @@
mPackageManager,
mSecureSettings,
mController);
+
+ mTile.initialize();
+ mTestableLooper.processAllMessages();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index df4908d..9eb688d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
@@ -89,6 +89,9 @@
mActivityStarter,
mQSLogger
);
+
+ mTile.initialize();
+ mTestableLooper.processAllMessages();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index 3b4e863..964ce01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -91,6 +91,9 @@
mController,
mKeyguardDismissUtil
);
+
+ mTile.initialize();
+ mTestableLooper.processAllMessages();
}
// Test that the tile is inactive and labeled correctly when the controller is neither starting
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSession.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSession.java
index 9c68f0d..478658e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSession.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSession.java
@@ -57,16 +57,19 @@
private int mScrollDelta;
private int mPageHeight;
+ private int mTargetHeight;
FakeSession(int pageHeight, float maxPages, int tileHeight, int visiblePageTop,
- int visiblePageBottom, int availableTop, int availableBottom) {
+ int visiblePageBottom, int availableTop, int availableBottom,
+ int maxTiles) {
mPageHeight = pageHeight;
mTileHeight = tileHeight;
mAvailable = new Rect(0, availableTop, getPageWidth(), availableBottom);
mAvailableTop = new Rect(mAvailable);
mAvailableTop.inset(0, 0, 0, pageHeight);
mVisiblePage = new Rect(0, visiblePageTop, getPageWidth(), visiblePageBottom);
- mMaxTiles = (int) Math.ceil((pageHeight * maxPages) / mTileHeight);
+ mTargetHeight = (int) (pageHeight * maxPages);
+ mMaxTiles = maxTiles;
}
private static Image mockImage() {
@@ -158,6 +161,11 @@
}
@Override
+ public int getTargetHeight() {
+ return mTargetHeight;
+ }
+
+ @Override
public int getTileHeight() {
return mTileHeight;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java
index 2520af9..4c8a4b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java
@@ -42,21 +42,6 @@
@RunWith(AndroidTestingRunner.class)
public class FakeSessionTest extends SysuiTestCase {
@Test
- public void testMaxTiles() {
- FakeSession session = new FakeSession(
- /* pageHeight */ 100,
- /* maxPages */ 2.25f,
- /* tileHeight */ 10,
- /* visiblePageTop */ 0,
- /* visiblePageBottom */ 100,
- /* availableTop */ -250,
- /* availableBottom */ 250);
-
- // (pageHeight * maxPages) / tileHeight
- assertEquals("getMaxTiles()", 23, session.getMaxTiles());
- }
-
- @Test
public void testNonEmptyResult_hasImage() {
FakeSession session = new FakeSession(
/* pageHeight */ 100,
@@ -65,7 +50,8 @@
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ 0,
- /* availableBottom */ 100);
+ /* availableBottom */ 100,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(0));
assertNotNull("result.image", result.image);
assertNotNull("result.image.getHardwareBuffer()", result.image.getHardwareBuffer());
@@ -80,7 +66,8 @@
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ 0,
- /* availableBottom */ 100);
+ /* availableBottom */ 100,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-100));
assertNull("result.image", result.image);
}
@@ -94,7 +81,8 @@
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ -250,
- /* availableBottom */ 250);
+ /* availableBottom */ 250,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(0));
assertEquals("requested top", 0, result.requested.top);
@@ -113,7 +101,8 @@
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ -250,
- /* availableBottom */ 250);
+ /* availableBottom */ 250,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(90));
assertEquals("requested top", 90, result.requested.top);
@@ -132,7 +121,8 @@
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ -250,
- /* availableBottom */ 250);
+ /* availableBottom */ 250,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-100));
assertEquals("requested top", -100, result.requested.top);
@@ -151,7 +141,8 @@
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ -250,
- /* availableBottom */ 250);
+ /* availableBottom */ 250,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(150));
assertEquals("requested top", 150, result.requested.top);
@@ -170,7 +161,8 @@
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ -100,
- /* availableBottom */ 100);
+ /* availableBottom */ 100,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-125));
assertEquals("requested top", -125, result.requested.top);
@@ -189,7 +181,8 @@
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ -100,
- /* availableBottom */ 100);
+ /* availableBottom */ 100,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(75));
assertEquals("requested top", 75, result.requested.top);
@@ -211,7 +204,8 @@
/* visiblePageTop */ 25, // <<--
/* visiblePageBottom */ 100,
/* availableTop */ -150,
- /* availableBottom */ 150);
+ /* availableBottom */ 150,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-150));
assertEquals("requested top", -150, result.requested.top);
@@ -233,7 +227,8 @@
/* visiblePageTop */ 0,
/* visiblePageBottom */ 75,
/* availableTop */ -150,
- /* availableBottom */ 150);
+ /* availableBottom */ 150,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(50));
assertEquals("requested top", 50, result.requested.top);
@@ -252,7 +247,8 @@
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ -100,
- /* availableBottom */ 200);
+ /* availableBottom */ 200,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-150));
assertTrue("captured rect is empty", result.captured.isEmpty());
}
@@ -266,7 +262,8 @@
/* visiblePageTop */ 0,
/* visiblePageBottom */ 100,
/* availableTop */ -100,
- /* availableBottom */ 200);
+ /* availableBottom */ 200,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(200));
assertTrue("captured rect is empty", result.captured.isEmpty());
}
@@ -280,7 +277,8 @@
/* visiblePageTop */ 60, // <<---
/* visiblePageBottom */ 0,
/* availableTop */ -150,
- /* availableBottom */ 150);
+ /* availableBottom */ 150,
+ /* max Tiles */ 30);
ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(0));
assertEquals("requested top", 0, result.requested.top);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
index 5bab1bc..10c878a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
@@ -92,14 +92,16 @@
// Each tile is cropped to the visible page size, which is inset 5px from the TOP
// requested result
// 0, 50 5, 50
- // -45, 5 -40, 5 <-- clear previous / top
- // 5, 55 5, 55 (not cropped, target is positioned fully within visible range)
- // 55, 105 55, 105
- // 105, 155 105, 155
- // 155, 205 155, 205 <-- bottom
+ // -45, 5 -40, 5
+ // -90, -40 -85, -40 <-- clear previous / top
+ // -40, 10 -40, 10 (not cropped, target is positioned fully within visible range)
+ // 10, 60 10, 60
+ // 60, 110 60, 110
+ // 110, 160 110, 160
+ // 160, 210 160, 210 <-- bottom
- assertEquals("top", -40, screenshot.getTop());
- assertEquals("bottom", 205, screenshot.getBottom());
+ assertEquals("top", -85, screenshot.getTop());
+ assertEquals("bottom", 210, screenshot.getBottom());
}
@Test
@@ -119,13 +121,14 @@
// requested result
// 0, 50 0, 50 // not cropped, positioned within visible range
// -50, 0 -50, 0 <-- clear previous/reverse
- // 0, 50 - 0, 45 // target now positioned at page bottom, bottom cropped
+ // 0, 50 0, 45 // target now positioned at page bottom, bottom cropped
// 45, 95, 45, 90
// 90, 140, 140, 135
- // 135, 185 185, 180 <-- bottom
+ // 135, 185 185, 180
+ // 180, 230 180, 225 <-- bottom
assertEquals("top", -50, screenshot.getTop());
- assertEquals("bottom", 180, screenshot.getBottom());
+ assertEquals("bottom", 225, screenshot.getBottom());
}
@Test
@@ -265,7 +268,8 @@
mLocalVisibleBottom = mPageHeight;
}
Session session = new FakeSession(mPageHeight, mMaxPages, mTileHeight,
- mLocalVisibleTop, mLocalVisibleBottom, mAvailableTop, mAvailableBottom);
+ mLocalVisibleTop, mLocalVisibleBottom, mAvailableTop, mAvailableBottom,
+ /* maxTiles */ 30);
ScrollCaptureClient client = mock(ScrollCaptureClient.class);
when(client.start(/* response */ any(), /* maxPages */ anyFloat()))
.thenReturn(immediateFuture(session));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
index 7c7d2dc..2b44d8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar
import android.content.res.Resources
+import android.view.CrossWindowBlurListeners
import android.view.SurfaceControl
import android.view.ViewRootImpl
import androidx.test.filters.SmallTest
@@ -37,11 +38,13 @@
@Mock lateinit var resources: Resources
@Mock lateinit var dumpManager: DumpManager
@Mock lateinit var transaction: SurfaceControl.Transaction
+ @Mock lateinit var corssWindowBlurListeners: CrossWindowBlurListeners
lateinit var blurUtils: BlurUtils
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
+ `when`(corssWindowBlurListeners.isCrossWindowBlurEnabled).thenReturn(true)
blurUtils = TestableBlurUtils()
}
@@ -71,7 +74,7 @@
verify(transaction).apply()
}
- inner class TestableBlurUtils() : BlurUtils(resources, dumpManager) {
+ inner class TestableBlurUtils() : BlurUtils(resources, corssWindowBlurListeners, dumpManager) {
override fun supportsBlursOnWindows(): Boolean {
return true
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 61c3835..d6c2797 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -47,6 +47,7 @@
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.doThrow
import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@@ -70,7 +71,7 @@
@Mock private lateinit var windowToken: IBinder
@Mock private lateinit var shadeSpring: NotificationShadeDepthController.DepthAnimation
@Mock private lateinit var shadeAnimation: NotificationShadeDepthController.DepthAnimation
- @Mock private lateinit var globalActionsSpring: NotificationShadeDepthController.DepthAnimation
+ @Mock private lateinit var brightnessSpring: NotificationShadeDepthController.DepthAnimation
@Mock private lateinit var listener: NotificationShadeDepthController.DepthListener
@Mock private lateinit var dozeParameters: DozeParameters
@Captor private lateinit var scrimVisibilityCaptor: ArgumentCaptor<Consumer<Int>>
@@ -90,7 +91,11 @@
`when`(blurUtils.blurRadiusOfRatio(anyFloat())).then { answer ->
(answer.arguments[0] as Float * maxBlur).toInt()
}
- `when`(blurUtils.minBlurRadius).thenReturn(0)
+ `when`(blurUtils.ratioOfBlurRadius(anyInt())).then { answer ->
+ answer.arguments[0] as Int / maxBlur.toFloat()
+ }
+ `when`(blurUtils.supportsBlursOnWindows()).thenReturn(true)
+ `when`(blurUtils.maxBlurRadius).thenReturn(maxBlur)
`when`(blurUtils.maxBlurRadius).thenReturn(maxBlur)
notificationShadeDepthController = NotificationShadeDepthController(
@@ -99,7 +104,7 @@
notificationShadeWindowController, dozeParameters, dumpManager)
notificationShadeDepthController.shadeSpring = shadeSpring
notificationShadeDepthController.shadeAnimation = shadeAnimation
- notificationShadeDepthController.globalActionsSpring = globalActionsSpring
+ notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring
notificationShadeDepthController.root = root
val captor = ArgumentCaptor.forClass(StatusBarStateController.StateListener::class.java)
@@ -191,24 +196,21 @@
}
@Test
- fun updateGlobalDialogVisibility_animatesBlur() {
- notificationShadeDepthController.updateGlobalDialogVisibility(0.5f, root)
- verify(globalActionsSpring).animateTo(eq(maxBlur / 2), eq(root))
- }
+ fun setFullShadeTransition_appliesBlur_onlyIfSupported() {
+ reset(blurUtils)
+ `when`(blurUtils.blurRadiusOfRatio(anyFloat())).then { answer ->
+ (answer.arguments[0] as Float * maxBlur).toInt()
+ }
+ `when`(blurUtils.ratioOfBlurRadius(anyInt())).then { answer ->
+ answer.arguments[0] as Int / maxBlur.toFloat()
+ }
+ `when`(blurUtils.maxBlurRadius).thenReturn(maxBlur)
+ `when`(blurUtils.maxBlurRadius).thenReturn(maxBlur)
- @Test
- fun updateGlobalDialogVisibility_appliesBlur_withoutHomeControls() {
- `when`(globalActionsSpring.radius).thenReturn(maxBlur)
- notificationShadeDepthController.updateBlurCallback.doFrame(0)
- verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false))
- }
-
- @Test
- fun updateGlobalDialogVisibility_appliesBlur_unlessHomeControls() {
- notificationShadeDepthController.showingHomeControls = true
- `when`(globalActionsSpring.radius).thenReturn(maxBlur)
+ notificationShadeDepthController.transitionToFullShadeProgress = 1f
notificationShadeDepthController.updateBlurCallback.doFrame(0)
verify(blurUtils).applyBlur(any(), eq(0), eq(false))
+ verify(wallpaperManager).setWallpaperZoomOut(any(), eq(1f))
}
@Test
@@ -239,7 +241,7 @@
fun updateBlurCallback_ignoreShadeBlurUntilHidden_overridesZoom() {
`when`(shadeSpring.radius).thenReturn(maxBlur)
`when`(shadeAnimation.radius).thenReturn(maxBlur)
- notificationShadeDepthController.ignoreShadeBlurUntilHidden = true
+ notificationShadeDepthController.blursDisabledForAppLaunch = true
notificationShadeDepthController.updateBlurCallback.doFrame(0)
verify(blurUtils).applyBlur(any(), eq(0), eq(false))
}
@@ -261,16 +263,42 @@
@Test
fun ignoreShadeBlurUntilHidden_schedulesFrame() {
- notificationShadeDepthController.ignoreShadeBlurUntilHidden = true
+ notificationShadeDepthController.blursDisabledForAppLaunch = true
verify(choreographer).postFrameCallback(
eq(notificationShadeDepthController.updateBlurCallback))
}
@Test
+ fun brightnessMirrorVisible_whenVisible() {
+ notificationShadeDepthController.brightnessMirrorVisible = true
+ verify(brightnessSpring).animateTo(eq(maxBlur), any())
+ }
+
+ @Test
+ fun brightnessMirrorVisible_whenHidden() {
+ notificationShadeDepthController.brightnessMirrorVisible = false
+ verify(brightnessSpring).animateTo(eq(0), any())
+ }
+
+ @Test
+ fun brightnessMirror_hidesShadeBlur() {
+ // Brightness mirror is fully visible
+ `when`(brightnessSpring.ratio).thenReturn(1f)
+ // And shade is blurred
+ `when`(shadeSpring.radius).thenReturn(maxBlur)
+ `when`(shadeAnimation.radius).thenReturn(maxBlur)
+
+ notificationShadeDepthController.updateBlurCallback.doFrame(0)
+ verify(notificationShadeWindowController).setBackgroundBlurRadius(eq(0))
+ verify(wallpaperManager).setWallpaperZoomOut(any(), eq(1f))
+ verify(blurUtils).applyBlur(eq(viewRootImpl), eq(0), eq(false))
+ }
+
+ @Test
fun ignoreShadeBlurUntilHidden_whennNull_ignoresIfShadeHasNoBlur() {
`when`(shadeSpring.radius).thenReturn(0)
`when`(shadeAnimation.radius).thenReturn(0)
- notificationShadeDepthController.ignoreShadeBlurUntilHidden = true
+ notificationShadeDepthController.blursDisabledForAppLaunch = true
verify(shadeSpring, never()).animateTo(anyInt(), any())
verify(shadeAnimation, never()).animateTo(anyInt(), any())
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 2e0827f..fa25c3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -289,6 +289,7 @@
@Test
public void testIconScrollXAfterTranslationAndReset() throws Exception {
+ mGroupRow.setDismissUsingRowTranslationX(false);
mGroupRow.setTranslation(50);
assertEquals(50, -mGroupRow.getEntry().getIcons().getShelfIcon().getScrollX());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 43f7284..04d7b72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.stack;
import static android.provider.Settings.Secure.NOTIFICATION_HISTORY_ENABLED;
+import static android.view.View.GONE;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
@@ -316,6 +317,7 @@
public void testUpdateFooter_oneClearableNotification() {
setBarStateForTest(StatusBarState.SHADE);
+ when(mEmptyShadeView.getVisibility()).thenReturn(GONE);
when(mStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL))
.thenReturn(true);
when(mStackScrollLayoutController.hasActiveNotifications()).thenReturn(true);
@@ -337,6 +339,7 @@
when(mStackScrollLayoutController.hasActiveNotifications()).thenReturn(true);
when(mStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL))
.thenReturn(false);
+ when(mEmptyShadeView.getVisibility()).thenReturn(GONE);
FooterView view = mock(FooterView.class);
mStackScroller.setFooterView(view);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index 9ac600a..5bf1bb3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -63,6 +63,7 @@
@Mock private BatteryController mBatteryController;
@Mock private FeatureFlags mFeatureFlags;
@Mock private DumpManager mDumpManager;
+ @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@Before
public void setup() {
@@ -75,7 +76,8 @@
mBatteryController,
mTunerService,
mDumpManager,
- mFeatureFlags
+ mFeatureFlags,
+ mUnlockedScreenOffAnimationController
);
}
@Test
@@ -125,7 +127,8 @@
when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
when(mFeatureFlags.useNewLockscreenAnimations()).thenReturn(true);
-
+ when(mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation())
+ .thenReturn(true);
assertTrue(mDozeParameters.shouldControlUnlockedScreenOff());
// Trigger the setter for the current value.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 9e939ee..2693b94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -26,9 +26,12 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -37,9 +40,13 @@
import android.annotation.IdRes;
import android.app.ActivityManager;
+import android.content.ContentResolver;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.hardware.biometrics.BiometricSourceType;
+import android.os.Handler;
+import android.os.Looper;
import android.os.PowerManager;
import android.os.UserManager;
import android.testing.AndroidTestingRunner;
@@ -50,6 +57,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
+import android.view.ViewStub;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -60,6 +68,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardClockSwitch;
import com.android.keyguard.KeyguardClockSwitchController;
@@ -84,7 +93,6 @@
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.QSDetailDisplayer;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.FeatureFlags;
@@ -141,6 +149,7 @@
private KeyguardBottomAreaView mKeyguardBottomArea;
@Mock
private KeyguardBottomAreaView mQsFrame;
+ private KeyguardStatusView mKeyguardStatusView;
@Mock
private ViewGroup mBigClockContainer;
@Mock
@@ -154,6 +163,10 @@
@Mock
private KeyguardStatusBarView mKeyguardStatusBar;
@Mock
+ private View mUserSwitcherView;
+ @Mock
+ private ViewStub mUserSwitcherStubView;
+ @Mock
private HeadsUpTouchHelper.Callback mHeadsUpCallback;
@Mock
private PanelBar mPanelBar;
@@ -205,7 +218,6 @@
@Mock
private KeyguardClockSwitch mKeyguardClockSwitch;
private PanelViewController.TouchHandler mTouchHandler;
- @Mock
private ConfigurationController mConfigurationController;
@Mock
private MediaHierarchyManager mMediaHiearchyManager;
@@ -266,6 +278,8 @@
@Mock
private SecureSettings mSecureSettings;
@Mock
+ private ContentResolver mContentResolver;
+ @Mock
private TapAgainViewController mTapAgainViewController;
@Mock
private KeyguardIndicationController mKeyguardIndicationController;
@@ -288,6 +302,9 @@
MockitoAnnotations.initMocks(this);
mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger);
+ mKeyguardStatusView = new KeyguardStatusView(mContext);
+ mKeyguardStatusView.setId(R.id.keyguard_status_view);
+
when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
when(mHeadsUpCallback.getContext()).thenReturn(mContext);
when(mView.getResources()).thenReturn(mResources);
@@ -302,6 +319,9 @@
when(mResources.getDimensionPixelSize(R.dimen.notification_panel_width)).thenReturn(400);
when(mView.getContext()).thenReturn(getContext());
when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
+ when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(mUserSwitcherView);
+ when(mView.findViewById(R.id.keyguard_user_switcher_stub)).thenReturn(
+ mUserSwitcherStubView);
when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch);
when(mView.findViewById(R.id.notification_stack_scroller))
.thenReturn(mNotificationStackScrollLayout);
@@ -321,7 +341,7 @@
mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null);
mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame));
mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller));
- mNotificationContainerParent.addView(newViewWithId(R.id.keyguard_status_view));
+ mNotificationContainerParent.addView(mKeyguardStatusView);
when(mView.findViewById(R.id.notification_container_parent))
.thenReturn(mNotificationContainerParent);
when(mFragmentService.getFragmentHostManager(mView)).thenReturn(mFragmentHostManager);
@@ -349,6 +369,7 @@
mFalsingManager,
mLockscreenShadeTransitionController,
new FalsingCollectorFake());
+ mConfigurationController = new ConfigurationControllerImpl(mContext);
when(mKeyguardStatusViewComponentFactory.build(any()))
.thenReturn(mKeyguardStatusViewComponent);
when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
@@ -359,10 +380,16 @@
.thenReturn(mKeyguardStatusBarViewComponent);
when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController())
.thenReturn(mKeyguardStatusBarViewController);
+ when(mLayoutInflater.inflate(eq(R.layout.keyguard_status_view), any(), anyBoolean()))
+ .thenReturn(mKeyguardStatusView);
+ when(mLayoutInflater.inflate(eq(R.layout.keyguard_bottom_area), any(), anyBoolean()))
+ .thenReturn(mKeyguardBottomArea);
reset(mView);
+
mNotificationPanelViewController = new NotificationPanelViewController(mView,
mResources,
+ new Handler(Looper.getMainLooper()),
mLayoutInflater,
coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
mFalsingManager, new FalsingCollectorFake(),
@@ -396,6 +423,7 @@
mTapAgainViewController,
mNavigationModeController,
mFragmentService,
+ mContentResolver,
mQuickAccessWalletController,
new FakeExecutor(new FakeSystemClock()),
mSecureSettings,
@@ -550,6 +578,38 @@
}
@Test
+ public void testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() {
+ givenViewAttached();
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true);
+ updateMultiUserSetting(true);
+ clearInvocations(mView);
+
+ updateMultiUserSetting(false);
+
+ ArgumentCaptor<View> captor = ArgumentCaptor.forClass(View.class);
+ verify(mView, atLeastOnce()).addView(captor.capture(), anyInt());
+ final View userSwitcherStub = CollectionUtils.find(captor.getAllValues(),
+ view -> view.getId() == R.id.keyguard_user_switcher_stub);
+ assertThat(userSwitcherStub).isNotNull();
+ assertThat(userSwitcherStub).isInstanceOf(ViewStub.class);
+ }
+
+ @Test
+ public void testChangeSmallestScreenWidthAndUserSwitchEnabled_inflatesUserSwitchView() {
+ givenViewAttached();
+ when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(null);
+ updateSmallestScreenWidth(300);
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true);
+ when(mUserManager.isUserSwitcherEnabled()).thenReturn(true);
+
+ updateSmallestScreenWidth(800);
+
+ verify(mUserSwitcherStubView).inflate();
+ }
+
+ @Test
public void testSplitShadeLayout_isAlignedToGuideline() {
enableSplitShade();
@@ -675,21 +735,6 @@
verify(mTapAgainViewController).show();
}
- @Test
- public void testNotificationClipping_isAlignedWithNotificationScrimInSplitShade() {
- mStatusBarStateController.setState(SHADE);
- QS qs = mock(QS.class);
- when(qs.getHeader()).thenReturn(mock(View.class));
- mNotificationPanelViewController.mQs = qs;
- enableSplitShade();
-
- // hacky way to refresh notification scrim top with non-zero qsPanelBottom value
- mNotificationPanelViewController.setTransitionToFullShadeAmount(200, false, 0);
-
- verify(mAmbientState)
- .setNotificationScrimTop(NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE);
- }
-
private FalsingManager.FalsingTapListener getFalsingTapListener() {
for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
listener.onViewAttachedToWindow(mView);
@@ -698,6 +743,12 @@
return mFalsingManager.getTapListeners().get(0);
}
+ private void givenViewAttached() {
+ for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) {
+ listener.onViewAttachedToWindow(mView);
+ }
+ }
+
private View newViewWithId(int id) {
View view = new View(mContext);
view.setId(id);
@@ -720,6 +771,21 @@
mNotificationPanelViewController.updateResources();
}
+ private void updateMultiUserSetting(boolean enabled) {
+ when(mUserManager.isUserSwitcherEnabled()).thenReturn(enabled);
+ final ArgumentCaptor<ContentObserver> observerCaptor =
+ ArgumentCaptor.forClass(ContentObserver.class);
+ verify(mContentResolver)
+ .registerContentObserver(any(), anyBoolean(), observerCaptor.capture());
+ observerCaptor.getValue().onChange(/* selfChange */ false);
+ }
+
+ private void updateSmallestScreenWidth(int smallestScreenWidthDp) {
+ Configuration configuration = new Configuration();
+ configuration.smallestScreenWidthDp = smallestScreenWidthDp;
+ mConfigurationController.onConfigurationChanged(configuration);
+ }
+
private void onTouchEvent(MotionEvent ev) {
mTouchHandler.onTouch(mView, ev);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
index 3238430..9fe47ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
@@ -45,6 +45,7 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
@@ -70,6 +71,7 @@
@Mock private SysuiColorExtractor mColorExtractor;
@Mock ColorExtractor.GradientColors mGradientColors;
@Mock private DumpManager mDumpManager;
+ @Mock private KeyguardStateController mKeyguardStateController;
@Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
@@ -83,7 +85,7 @@
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
- mColorExtractor, mDumpManager);
+ mColorExtractor, mDumpManager, mKeyguardStateController);
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowController.attach();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index e55361e..678b193 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -312,6 +312,8 @@
mScrimBehind, true,
mScrimForBubble, false
));
+
+ assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
}
@Test
@@ -321,8 +323,9 @@
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
- mScrimBehind, OPAQUE,
+ mScrimBehind, TRANSPARENT,
mNotificationsScrim, TRANSPARENT));
+ assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
assertScrimTinted(Map.of(
mScrimInFront, true,
@@ -340,6 +343,7 @@
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
mScrimBehind, TRANSPARENT));
+ assertEquals(0f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
// Pulsing notification should conserve AOD wallpaper.
mScrimController.transitionTo(ScrimState.PULSING);
@@ -348,6 +352,7 @@
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
mScrimBehind, TRANSPARENT));
+ assertEquals(0f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
}
@Test
@@ -359,7 +364,8 @@
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
- mScrimBehind, OPAQUE));
+ mScrimBehind, TRANSPARENT));
+ assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
assertScrimTinted(Map.of(
mScrimInFront, true,
@@ -378,7 +384,8 @@
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
- mScrimBehind, OPAQUE));
+ mScrimBehind, TRANSPARENT));
+ assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
assertScrimTinted(Map.of(
mScrimInFront, true,
@@ -403,13 +410,15 @@
finishAnimationsImmediately();
assertScrimAlpha(Map.of(
mScrimInFront, SEMI_TRANSPARENT,
- mScrimBehind, OPAQUE));
+ mScrimBehind, TRANSPARENT));
+ assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
// ... and that if we set it while we're in AOD, it does take immediate effect.
mScrimController.setAodFrontScrimAlpha(1f);
assertScrimAlpha(Map.of(
mScrimInFront, OPAQUE,
- mScrimBehind, OPAQUE));
+ mScrimBehind, TRANSPARENT));
+ assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
// ... and make sure we recall the previous front scrim alpha even if we transition away
// for a bit.
@@ -418,7 +427,8 @@
finishAnimationsImmediately();
assertScrimAlpha(Map.of(
mScrimInFront, OPAQUE,
- mScrimBehind, OPAQUE));
+ mScrimBehind, TRANSPARENT));
+ assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
// ... and alpha updates should be completely ignored if always_on is off.
// Passing it forward would mess up the wake-up transition.
@@ -448,23 +458,28 @@
finishAnimationsImmediately();
assertScrimAlpha(Map.of(
mScrimInFront, OPAQUE,
- mScrimBehind, OPAQUE));
+ mScrimBehind, TRANSPARENT));
+ assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
// ... but will take effect after docked
when(mDockManager.isDocked()).thenReturn(true);
mScrimController.transitionTo(ScrimState.KEYGUARD);
mScrimController.setAodFrontScrimAlpha(0.5f);
mScrimController.transitionTo(ScrimState.AOD);
+ finishAnimationsImmediately();
assertScrimAlpha(Map.of(
mScrimInFront, SEMI_TRANSPARENT,
- mScrimBehind, OPAQUE));
+ mScrimBehind, TRANSPARENT));
+ assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
// ... and that if we set it while we're in AOD, it does take immediate effect after docked.
mScrimController.setAodFrontScrimAlpha(1f);
+ finishAnimationsImmediately();
assertScrimAlpha(Map.of(
mScrimInFront, OPAQUE,
- mScrimBehind, OPAQUE));
+ mScrimBehind, TRANSPARENT));
+ assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
// Reset value since enums are static.
mScrimController.setAodFrontScrimAlpha(0f);
@@ -480,7 +495,8 @@
finishAnimationsImmediately();
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
- mScrimBehind, OPAQUE));
+ mScrimBehind, TRANSPARENT));
+ assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
mScrimController.transitionTo(ScrimState.PULSING);
finishAnimationsImmediately();
@@ -489,7 +505,8 @@
// Pulse callback should have been invoked
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
- mScrimBehind, OPAQUE));
+ mScrimBehind, TRANSPARENT));
+ assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
assertScrimTinted(Map.of(
mScrimInFront, true,
@@ -503,13 +520,16 @@
// Front scrim should be semi-transparent
assertScrimAlpha(Map.of(
mScrimInFront, SEMI_TRANSPARENT,
- mScrimBehind, OPAQUE));
+ mScrimBehind, TRANSPARENT));
+ assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
mScrimController.setWakeLockScreenSensorActive(true);
finishAnimationsImmediately();
assertScrimAlpha(Map.of(
mScrimInFront, SEMI_TRANSPARENT,
- mScrimBehind, SEMI_TRANSPARENT));
+ mScrimBehind, TRANSPARENT));
+ assertEquals(ScrimController.WAKE_SENSOR_SCRIM_ALPHA,
+ mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
// Reset value since enums are static.
mScrimController.setAodFrontScrimAlpha(0f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index b2efd68..cbc7c6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -777,6 +777,12 @@
}
@Test
+ public void testDumpBarTransitions_DoesNotCrash() {
+ StatusBar.dumpBarTransitions(
+ new PrintWriter(new ByteArrayOutputStream()), "var", /* transitions= */ null);
+ }
+
+ @Test
@RunWithLooper(setAsMainLooper = true)
public void testUpdateKeyguardState_DoesNotCrash() {
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
@@ -843,12 +849,14 @@
// By default, showKeyguardImpl sets state to KEYGUARD.
mStatusBar.showKeyguardImpl();
- verify(mStatusBarStateController).setState(eq(StatusBarState.KEYGUARD));
+ verify(mStatusBarStateController).setState(
+ eq(StatusBarState.KEYGUARD), eq(false) /* force */);
// If useFullscreenUserSwitcher is true, state is set to FULLSCREEN_USER_SWITCHER.
when(mUserSwitcherController.useFullscreenUserSwitcher()).thenReturn(true);
mStatusBar.showKeyguardImpl();
- verify(mStatusBarStateController).setState(eq(StatusBarState.FULLSCREEN_USER_SWITCHER));
+ verify(mStatusBarStateController).setState(
+ eq(StatusBarState.FULLSCREEN_USER_SWITCHER), eq(false) /* force */);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index ca3cff9..94c9de0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -40,23 +40,23 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.*
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.nullable
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.Mockito.reset
-
import org.mockito.MockitoAnnotations
private const val CALL_UID = 900
@@ -140,6 +140,13 @@
.onOngoingCallStateChanged(anyBoolean())
}
+ /** Regression test for b/191472854. */
+ @Test
+ fun onEntryUpdated_notifHasNullContentIntent_noCrash() {
+ notifCollectionListener.onEntryUpdated(
+ createCallNotifEntry(ongoingCallStyle, nullContentIntent = true))
+ }
+
/**
* If a call notification is never added before #onEntryRemoved is called, then the listener
* should never be notified.
@@ -357,14 +364,22 @@
private fun createScreeningCallNotifEntry() = createCallNotifEntry(screeningCallStyle)
- private fun createCallNotifEntry(callStyle: Notification.CallStyle): NotificationEntry {
+ private fun createCallNotifEntry(
+ callStyle: Notification.CallStyle,
+ nullContentIntent: Boolean = false
+ ): NotificationEntry {
val notificationEntryBuilder = NotificationEntryBuilder()
notificationEntryBuilder.modifyNotification(context).style = callStyle
-
- val contentIntent = mock(PendingIntent::class.java)
- `when`(contentIntent.intent).thenReturn(mock(Intent::class.java))
- notificationEntryBuilder.modifyNotification(context).setContentIntent(contentIntent)
notificationEntryBuilder.setUid(CALL_UID)
+
+ if (nullContentIntent) {
+ notificationEntryBuilder.modifyNotification(context).setContentIntent(null)
+ } else {
+ val contentIntent = mock(PendingIntent::class.java)
+ `when`(contentIntent.intent).thenReturn(mock(Intent::class.java))
+ notificationEntryBuilder.modifyNotification(context).setContentIntent(contentIntent)
+ }
+
return notificationEntryBuilder.build()
}
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 521b958..bd9d1a7 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
@@ -238,9 +238,7 @@
mNetworkController.setNoNetworksAvailable(false);
setWifiStateForVcn(true, testSsid);
setWifiLevelForVcn(0);
- // Connected, but still not validated - does not show
- //verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]);
- verifyLastMobileDataIndicatorsForVcn(false, 0, 0, false);
+ verifyLastMobileDataIndicatorsForVcn(true, 0, TelephonyIcons.ICON_CWF, false);
mNetworkController.setNoNetworksAvailable(true);
for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 208790b..1a24c11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -415,13 +415,6 @@
}
@Test
- public void onUserSwitch_setsTheme() {
- mBroadcastReceiver.getValue().onReceive(null,
- new Intent(Intent.ACTION_USER_STARTED));
- verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
- }
-
- @Test
public void onProfileAdded_setsTheme() {
mBroadcastReceiver.getValue().onReceive(null,
new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
index 1aebf1c..e136d00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
@@ -132,4 +132,9 @@
public boolean canPerformSmartSpaceTransition() {
return false;
}
+
+ @Override
+ public boolean isKeyguardScreenRotationAllowed() {
+ return false;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
index e6c740b..3018089 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
@@ -67,7 +67,6 @@
@SmallTest
public class WalletScreenControllerTest extends SysuiTestCase {
- private static final int MAX_CARDS = 10;
private static final int CARD_CAROUSEL_WIDTH = 10;
private static final String CARD_ID_1 = "card_id_1";
private static final String CARD_ID_2 = "card_id_2";
@@ -158,7 +157,7 @@
when(mKeyguardStateController.isUnlocked()).thenReturn(false);
GetWalletCardsResponse response =
new GetWalletCardsResponse(
- Collections.singletonList(createWalletCard(mContext)), 0);
+ Collections.singletonList(createLockedWalletCard(mContext)), 0);
mController.queryWalletCards();
mTestableLooper.processAllMessages();
@@ -406,6 +405,15 @@
.build();
}
+ private WalletCard createLockedWalletCard(Context context) {
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
+ return new WalletCard.Builder(CARD_ID_2, createIcon(), "•••• 5679", pendingIntent)
+ .setCardIcon(createIcon())
+ .setCardLabel("Locked\nUnlock to pay")
+ .build();
+ }
+
private WalletCard createWalletCard(Context context) {
PendingIntent pendingIntent =
PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 6e2e4cb..9fa35f8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -23,6 +23,8 @@
import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
@@ -37,6 +39,7 @@
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -98,6 +101,7 @@
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -227,6 +231,8 @@
private TaskStackListenerImpl mTaskStackListener;
@Mock
private ShellTaskOrganizer mShellTaskOrganizer;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
private TestableBubblePositioner mPositioner;
@@ -249,7 +255,7 @@
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
- mColorExtractor, mDumpManager);
+ mColorExtractor, mDumpManager, mKeyguardStateController);
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowController.attach();
@@ -287,6 +293,7 @@
// TODO: Fix
mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
+ mPositioner.setMaxBubbles(5);
mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
@@ -320,6 +327,7 @@
syncExecutor,
mock(Handler.class));
mBubbleController.setExpandListener(mBubbleExpandListener);
+ spyOn(mBubbleController);
mBubblesManager = new BubblesManager(
mContext,
@@ -466,7 +474,7 @@
@Test
public void testExpandCollapseStack() {
- assertFalse(mBubbleController.isStackExpanded());
+ assertStackCollapsed();
// Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow);
@@ -474,25 +482,23 @@
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
// Expand the stack
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleData.setExpanded(true);
- assertTrue(mBubbleController.isStackExpanded());
+ assertStackExpanded();
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
assertTrue(mSysUiStateBubblesExpanded);
// Make sure the notif is suppressed
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// Collapse
mBubbleController.collapseStack();
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey());
- assertFalse(mBubbleController.isStackExpanded());
+ assertStackCollapsed();
assertFalse(mSysUiStateBubblesExpanded);
}
@@ -508,15 +514,13 @@
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry2);
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleData.setExpanded(true);
- assertTrue(mBubbleController.isStackExpanded());
+ assertStackExpanded();
verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
true, mRow2.getKey());
@@ -524,8 +528,7 @@
// Last added is the one that is expanded
assertEquals(mRow2.getKey(), mBubbleData.getSelectedBubble().getKey());
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry2);
// Switch which bubble is expanded
mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(
@@ -533,8 +536,7 @@
mBubbleData.setExpanded(true);
assertEquals(mRow.getKey(), mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey());
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// collapse for previous bubble
verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
@@ -545,7 +547,7 @@
// Collapse
mBubbleController.collapseStack();
- assertFalse(mBubbleController.isStackExpanded());
+ assertStackCollapsed();
assertFalse(mSysUiStateBubblesExpanded);
}
@@ -558,22 +560,20 @@
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
// Expand
mBubbleData.setExpanded(true);
- assertTrue(mBubbleController.isStackExpanded());
+ assertStackExpanded();
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
assertTrue(mSysUiStateBubblesExpanded);
// Notif is suppressed after expansion
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
}
@@ -586,22 +586,20 @@
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
// Expand
mBubbleData.setExpanded(true);
- assertTrue(mBubbleController.isStackExpanded());
+ assertStackExpanded();
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
assertTrue(mSysUiStateBubblesExpanded);
// Notif is suppressed after expansion
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
@@ -610,8 +608,7 @@
// Nothing should have changed
// Notif is suppressed after expansion
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
}
@@ -630,14 +627,13 @@
assertTrue(mSysUiStateBubblesExpanded);
- assertTrue(mBubbleController.isStackExpanded());
+ assertStackExpanded();
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getKey());
// Last added is the one that is expanded
assertEquals(mRow2.getKey(), mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey());
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry2);
// Dismiss currently expanded
mBubbleController.removeBubble(
@@ -675,7 +671,7 @@
mBubbleData.setExpanded(true);
assertTrue(mSysUiStateBubblesExpanded);
- assertTrue(mBubbleController.isStackExpanded());
+ assertStackExpanded();
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
// Block the bubble so it won't be in the overflow
@@ -694,7 +690,7 @@
@Test
public void testAutoExpand_fails_noFlag() {
- assertFalse(mBubbleController.isStackExpanded());
+ assertStackCollapsed();
setMetadataFlags(mRow,
Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, false /* enableFlag */);
@@ -705,7 +701,7 @@
// Expansion shouldn't change
verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */,
mRow.getKey());
- assertFalse(mBubbleController.isStackExpanded());
+ assertStackCollapsed();
assertFalse(mSysUiStateBubblesExpanded);
}
@@ -722,7 +718,7 @@
// Expansion should change
verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */,
mRow.getKey());
- assertTrue(mBubbleController.isStackExpanded());
+ assertStackExpanded();
assertTrue(mSysUiStateBubblesExpanded);
}
@@ -737,8 +733,7 @@
mBubbleController.updateBubble(mBubbleEntry);
// Notif should be suppressed because we were foreground
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// Dot + flyout is hidden because notif is suppressed
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout());
@@ -751,8 +746,7 @@
mBubbleController.updateBubble(mBubbleEntry);
// Should not be suppressed
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
// Should show dot
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
@@ -762,8 +756,7 @@
mBubbleController.updateBubble(mBubbleEntry);
// Notif should be suppressed
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// Dot + flyout is hidden because notif is suppressed
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout());
@@ -788,8 +781,7 @@
@Test
public void testMarkNewNotificationAsShowInShade() {
mEntryListener.onPendingEntryAdded(mRow);
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
@@ -874,8 +866,7 @@
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
mRow.getKey(), mRow, REASON_CANCEL_ALL);
@@ -883,8 +874,7 @@
// Intercept!
assertTrue(intercepted);
// Should update show in shade state
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
}
@Test
@@ -893,8 +883,7 @@
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
mRow.getKey(), mRow, REASON_CANCEL);
@@ -902,8 +891,7 @@
// Intercept!
assertTrue(intercepted);
// Should update show in shade state
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
}
@Test
@@ -912,8 +900,7 @@
mEntryListener.onPendingEntryAdded(mRow);
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
// Dismiss the bubble into overflow.
mBubbleController.removeBubble(
@@ -934,8 +921,7 @@
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
mBubbleController.removeBubble(
mRow.getKey(), Bubbles.DISMISS_NO_LONGER_BUBBLE);
@@ -981,48 +967,36 @@
@Test
public void testNotifyShadeSuppressionChange_notificationDismiss() {
- Bubbles.SuppressionChangedListener listener =
- mock(Bubbles.SuppressionChangedListener.class);
- mBubbleData.setSuppressionChangedListener(listener);
-
mEntryListener.onPendingEntryAdded(mRow);
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
mRemoveInterceptor.onNotificationRemoveRequested(
mRow.getKey(), mRow, REASON_CANCEL);
// Should update show in shade state
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// Should notify delegate that shade state changed
- verify(listener).onBubbleNotificationSuppressionChange(
+ verify(mBubbleController).onBubbleNotificationSuppressionChanged(
mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
}
@Test
public void testNotifyShadeSuppressionChange_bubbleExpanded() {
- Bubbles.SuppressionChangedListener listener =
- mock(Bubbles.SuppressionChangedListener.class);
- mBubbleData.setSuppressionChangedListener(listener);
-
mEntryListener.onPendingEntryAdded(mRow);
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
mBubbleData.setExpanded(true);
// Once a bubble is expanded the notif is suppressed
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// Should notify delegate that shade state changed
- verify(listener).onBubbleNotificationSuppressionChange(
+ verify(mBubbleController).onBubbleNotificationSuppressionChanged(
mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
}
@@ -1042,7 +1016,11 @@
// THEN the summary and bubbled child are suppressed from the shade
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- groupedBubble.getEntry().getKey(), groupSummary.getEntry().getSbn().getGroupKey()));
+ groupedBubble.getEntry().getKey(),
+ groupSummary.getEntry().getSbn().getGroupKey()));
+ assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
+ groupedBubble.getEntry().getKey(),
+ groupSummary.getEntry().getSbn().getGroupKey()));
assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey()));
}
@@ -1098,6 +1076,9 @@
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
groupedBubble.getEntry().getKey(),
groupedBubble.getEntry().getSbn().getGroupKey()));
+ assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
+ groupedBubble.getEntry().getKey(),
+ groupedBubble.getEntry().getSbn().getGroupKey()));
// THEN the summary is removed from GroupManager
verify(mNotificationGroupManager, times(1)).onEntryRemoved(groupSummary.getEntry());
@@ -1253,4 +1234,42 @@
Icon.createWithResource(mContext, R.drawable.bubble_ic_create_bubble))
.build();
}
+
+ /**
+ * Asserts that the bubble stack is expanded and also validates the cached state is updated.
+ */
+ private void assertStackExpanded() {
+ assertTrue(mBubbleController.isStackExpanded());
+ assertTrue(mBubbleController.getImplCachedState().isStackExpanded());
+ }
+
+ /**
+ * Asserts that the bubble stack is collapsed and also validates the cached state is updated.
+ */
+ private void assertStackCollapsed() {
+ assertFalse(mBubbleController.isStackExpanded());
+ assertFalse(mBubbleController.getImplCachedState().isStackExpanded());
+ }
+
+ /**
+ * Asserts that a bubble notification is suppressed from the shade and also validates the cached
+ * state is updated.
+ */
+ private void assertBubbleNotificationSuppressedFromShade(BubbleEntry entry) {
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ entry.getKey(), entry.getGroupKey()));
+ assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
+ entry.getKey(), entry.getGroupKey()));
+ }
+
+ /**
+ * Asserts that a bubble notification is not suppressed from the shade and also validates the
+ * cached state is updated.
+ */
+ private void assertBubbleNotificationNotSuppressedFromShade(BubbleEntry entry) {
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ entry.getKey(), entry.getGroupKey()));
+ assertFalse(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
+ entry.getKey(), entry.getGroupKey()));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index 9339f81..55cc8bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -19,6 +19,8 @@
import static android.app.Notification.FLAG_BUBBLE;
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
@@ -82,6 +84,7 @@
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
@@ -192,6 +195,8 @@
private TaskStackListenerImpl mTaskStackListener;
@Mock
private ShellTaskOrganizer mShellTaskOrganizer;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
private TestableBubblePositioner mPositioner;
@@ -213,7 +218,7 @@
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController,
- mColorExtractor, mDumpManager);
+ mColorExtractor, mDumpManager, mKeyguardStateController);
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowController.attach();
@@ -232,6 +237,7 @@
when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
mPositioner = new TestableBubblePositioner(mContext, mWindowManager);
+ mPositioner.setMaxBubbles(5);
mBubbleData = new BubbleData(mContext, mBubbleLogger, mPositioner, syncExecutor);
TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
@@ -264,6 +270,7 @@
syncExecutor,
mock(Handler.class));
mBubbleController.setExpandListener(mBubbleExpandListener);
+ spyOn(mBubbleController);
mBubblesManager = new BubblesManager(
mContext,
@@ -324,8 +331,7 @@
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
// Make it look like dismissed notif
mBubbleData.getBubbleInStackWithKey(mRow.getKey()).setSuppressNotification(true);
@@ -348,8 +354,7 @@
.thenReturn(mRow);
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
// Make it look like dismissed notif
mBubbleData.getBubbleInStackWithKey(mRow.getKey()).setSuppressNotification(true);
@@ -384,7 +389,7 @@
@Test
public void testExpandCollapseStack() {
- assertFalse(mBubbleController.isStackExpanded());
+ assertStackCollapsed();
// Mark it as a bubble and add it explicitly
mEntryListener.onEntryAdded(mRow);
@@ -392,23 +397,20 @@
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
// Expand the stack
- BubbleStackView stackView = mBubbleController.getStackView();
mBubbleData.setExpanded(true);
- assertTrue(mBubbleController.isStackExpanded());
+ assertStackExpanded();
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
// Make sure the notif is suppressed
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// Collapse
mBubbleController.collapseStack();
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey());
- assertFalse(mBubbleController.isStackExpanded());
+ assertStackCollapsed();
}
@Test
@@ -422,22 +424,19 @@
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry2);
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleData.setExpanded(true);
- assertTrue(mBubbleController.isStackExpanded());
+ assertStackExpanded();
verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
true, mRow2.getKey());
// Last added is the one that is expanded
assertEquals(mRow2.getKey(), mBubbleData.getSelectedBubble().getKey());
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry2);
// Switch which bubble is expanded
mBubbleData.setSelectedBubble(mBubbleData.getBubbleInStackWithKey(
@@ -445,8 +444,7 @@
mBubbleData.setExpanded(true);
assertEquals(mRow.getKey(), mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey());
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// collapse for previous bubble
verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
@@ -458,7 +456,7 @@
// Collapse
mBubbleController.collapseStack();
- assertFalse(mBubbleController.isStackExpanded());
+ assertStackCollapsed();
}
@Test
@@ -469,20 +467,18 @@
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
// Expand
mBubbleData.setExpanded(true);
- assertTrue(mBubbleController.isStackExpanded());
+ assertStackExpanded();
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
// Notif is suppressed after expansion
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
}
@@ -495,20 +491,18 @@
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
// Expand
mBubbleData.setExpanded(true);
- assertTrue(mBubbleController.isStackExpanded());
+ assertStackExpanded();
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
// Notif is suppressed after expansion
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
@@ -517,8 +511,7 @@
// Nothing should have changed
// Notif is suppressed after expansion
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
}
@@ -535,14 +528,13 @@
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleData.setExpanded(true);
- assertTrue(mBubbleController.isStackExpanded());
+ assertStackExpanded();
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getKey());
// Last added is the one that is expanded
assertEquals(mRow2.getKey(), mBubbleData.getBubbleInStackWithKey(
stackView.getExpandedBubble().getKey()).getKey());
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry2.getKey(), mBubbleEntry2.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry2);
// Dismiss currently expanded
mBubbleController.removeBubble(
@@ -578,7 +570,7 @@
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleData.setExpanded(true);
- assertTrue(mBubbleController.isStackExpanded());
+ assertStackExpanded();
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
// Block the bubble so it won't be in the overflow
@@ -597,7 +589,7 @@
@Test
public void testAutoExpand_fails_noFlag() {
- assertFalse(mBubbleController.isStackExpanded());
+ assertStackCollapsed();
setMetadataFlags(mRow,
Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, false /* enableFlag */);
@@ -608,7 +600,7 @@
// Expansion shouldn't change
verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */,
mRow.getKey());
- assertFalse(mBubbleController.isStackExpanded());
+ assertStackCollapsed();
}
@Test
@@ -623,7 +615,7 @@
// Expansion should change
verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */,
mRow.getKey());
- assertTrue(mBubbleController.isStackExpanded());
+ assertStackExpanded();
}
@Test
@@ -636,8 +628,7 @@
mBubbleController.updateBubble(mBubbleEntry);
// Notif should be suppressed because we were foreground
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// Dot + flyout is hidden because notif is suppressed
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout());
@@ -648,8 +639,7 @@
mBubbleController.updateBubble(mBubbleEntry);
// Should not be suppressed
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
// Should show dot
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
@@ -659,8 +649,7 @@
mBubbleController.updateBubble(mBubbleEntry);
// Notif should be suppressed
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// Dot + flyout is hidden because notif is suppressed
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout());
@@ -669,8 +658,7 @@
@Test
public void testMarkNewNotificationAsShowInShade() {
mEntryListener.onEntryAdded(mRow);
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
@@ -741,16 +729,14 @@
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
boolean intercepted = mBubblesManager.handleDismissalInterception(mRow);
// Intercept!
assertTrue(intercepted);
// Should update show in shade state
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
}
@Test
@@ -759,8 +745,7 @@
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
// Dismiss the bubble
mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
@@ -779,8 +764,7 @@
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
// Dismiss the bubble
mBubbleController.removeBubble(mRow.getKey(), Bubbles.DISMISS_NOTIF_CANCEL);
@@ -795,47 +779,35 @@
@Test
public void testNotifyShadeSuppressionChange_notificationDismiss() {
- Bubbles.SuppressionChangedListener listener =
- mock(Bubbles.SuppressionChangedListener.class);
- mBubbleData.setSuppressionChangedListener(listener);
-
mEntryListener.onEntryAdded(mRow);
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
mBubblesManager.handleDismissalInterception(mRow);
// Should update show in shade state
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// Should notify delegate that shade state changed
- verify(listener).onBubbleNotificationSuppressionChange(
+ verify(mBubbleController).onBubbleNotificationSuppressionChanged(
mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
}
@Test
public void testNotifyShadeSuppressionChange_bubbleExpanded() {
- Bubbles.SuppressionChangedListener listener =
- mock(Bubbles.SuppressionChangedListener.class);
- mBubbleData.setSuppressionChangedListener(listener);
-
mEntryListener.onEntryAdded(mRow);
assertTrue(mBubbleController.hasBubbles());
- assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
mBubbleData.setExpanded(true);
// Once a bubble is expanded the notif is suppressed
- assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
- mBubbleEntry.getKey(), mBubbleEntry.getGroupKey()));
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// Should notify delegate that shade state changed
- verify(listener).onBubbleNotificationSuppressionChange(
+ verify(mBubbleController).onBubbleNotificationSuppressionChanged(
mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
}
@@ -857,6 +829,9 @@
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
groupedBubble.getEntry().getKey(),
groupedBubble.getEntry().getSbn().getGroupKey()));
+ assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
+ groupedBubble.getEntry().getKey(),
+ groupedBubble.getEntry().getSbn().getGroupKey()));
assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey()));
}
@@ -911,11 +886,17 @@
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
groupedBubble.getEntry().getKey(),
groupedBubble.getEntry().getSbn().getGroupKey()));
+ assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
+ groupedBubble.getEntry().getKey(),
+ groupedBubble.getEntry().getSbn().getGroupKey()));
// THEN the summary is also suppressed from the shade
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
groupSummary.getEntry().getKey(),
groupSummary.getEntry().getSbn().getGroupKey()));
+ assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
+ groupSummary.getEntry().getKey(),
+ groupSummary.getEntry().getSbn().getGroupKey()));
}
/**
@@ -934,4 +915,42 @@
}
bubbleMetadata.setFlags(flags);
}
+
+ /**
+ * Asserts that the bubble stack is expanded and also validates the cached state is updated.
+ */
+ private void assertStackExpanded() {
+ assertTrue(mBubbleController.isStackExpanded());
+ assertTrue(mBubbleController.getImplCachedState().isStackExpanded());
+ }
+
+ /**
+ * Asserts that the bubble stack is collapsed and also validates the cached state is updated.
+ */
+ private void assertStackCollapsed() {
+ assertFalse(mBubbleController.isStackExpanded());
+ assertFalse(mBubbleController.getImplCachedState().isStackExpanded());
+ }
+
+ /**
+ * Asserts that a bubble notification is suppressed from the shade and also validates the cached
+ * state is updated.
+ */
+ private void assertBubbleNotificationSuppressedFromShade(BubbleEntry entry) {
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ entry.getKey(), entry.getGroupKey()));
+ assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
+ entry.getKey(), entry.getGroupKey()));
+ }
+
+ /**
+ * Asserts that a bubble notification is not suppressed from the shade and also validates the
+ * cached state is updated.
+ */
+ private void assertBubbleNotificationNotSuppressedFromShade(BubbleEntry entry) {
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ entry.getKey(), entry.getGroupKey()));
+ assertFalse(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade(
+ entry.getKey(), entry.getGroupKey()));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java
index 24a7cd5..6edc373 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java
@@ -22,9 +22,11 @@
import android.graphics.Rect;
import android.view.WindowManager;
+import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.BubblePositioner;
public class TestableBubblePositioner extends BubblePositioner {
+ private int mMaxBubbles;
public TestableBubblePositioner(Context context,
WindowManager windowManager) {
@@ -33,5 +35,15 @@
updateInternal(Configuration.ORIENTATION_PORTRAIT,
Insets.of(0, 0, 0, 0),
new Rect(0, 0, 500, 1000));
+ mMaxBubbles = context.getResources().getInteger(R.integer.bubbles_max_rendered);
+ }
+
+ public void setMaxBubbles(int max) {
+ mMaxBubbles = max;
+ }
+
+ @Override
+ public int getMaxBubbles() {
+ return mMaxBubbles;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 1dd0b21..5691660 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -27,6 +27,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.statusbar.CommandQueue;
@@ -37,6 +38,7 @@
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
+import com.android.wm.shell.onehanded.OneHandedEventCallback;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.Pip;
@@ -69,6 +71,7 @@
@Mock LegacySplitScreen mLegacySplitScreen;
@Mock OneHanded mOneHanded;
@Mock HideDisplayCutout mHideDisplayCutout;
+ @Mock WakefulnessLifecycle mWakefulnessLifecycle;
@Mock ProtoTracer mProtoTracer;
@Mock ShellCommandHandler mShellCommandHandler;
@Mock ShellExecutor mSysUiMainExecutor;
@@ -81,7 +84,8 @@
Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
Optional.of(mShellCommandHandler), mCommandQueue, mConfigurationController,
mKeyguardUpdateMonitor, mNavigationModeController,
- mScreenLifecycle, mSysUiState, mProtoTracer, mSysUiMainExecutor);
+ mScreenLifecycle, mSysUiState, mProtoTracer, mWakefulnessLifecycle,
+ mSysUiMainExecutor);
}
@Test
@@ -106,6 +110,7 @@
verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class));
verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer.class));
verify(mOneHanded).registerTransitionCallback(any(OneHandedTransitionCallback.class));
+ verify(mOneHanded).registerEventCallback(any(OneHandedEventCallback.class));
}
@Test
diff --git a/packages/services/CameraExtensionsProxy/AndroidManifest.xml b/packages/services/CameraExtensionsProxy/AndroidManifest.xml
index e5f460e..d356894 100644
--- a/packages/services/CameraExtensionsProxy/AndroidManifest.xml
+++ b/packages/services/CameraExtensionsProxy/AndroidManifest.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.camera">
+ package="com.android.cameraextensions">
<application
android:label="@string/app_name"
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
similarity index 99%
rename from packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java
rename to packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index 7b9ca2d..69874f8 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/camera/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.camera;
+package com.android.cameraextensions;
import android.app.Service;
import android.content.Context;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7eecc45..f631988 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -872,18 +872,32 @@
"userId=" + userId);
}
+ final int resolvedUserId;
+ final List<AccessibilityServiceInfo> serviceInfos;
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
// performs the current profile parent resolution.
- final int resolvedUserId = mSecurityPolicy
+ resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(userId);
-
- if (Binder.getCallingPid() == OWN_PROCESS_ID) {
- return new ArrayList<>(getUserStateLocked(resolvedUserId).mInstalledServices);
- }
- return getUserStateLocked(resolvedUserId).mInstalledServices;
+ serviceInfos = new ArrayList<>(
+ getUserStateLocked(resolvedUserId).mInstalledServices);
}
+
+ if (Binder.getCallingPid() == OWN_PROCESS_ID) {
+ return serviceInfos;
+ }
+ final PackageManagerInternal pm = LocalServices.getService(
+ PackageManagerInternal.class);
+ final int callingUid = Binder.getCallingUid();
+ for (int i = serviceInfos.size() - 1; i >= 0; i--) {
+ final AccessibilityServiceInfo serviceInfo = serviceInfos.get(i);
+ if (pm.filterAppAccess(serviceInfo.getComponentName().getPackageName(), callingUid,
+ resolvedUserId)) {
+ serviceInfos.remove(i);
+ }
+ }
+ return serviceInfos;
}
@Override
@@ -2892,7 +2906,8 @@
// In case user assigned magnification to the given shortcut.
if (targetName.equals(MAGNIFICATION_CONTROLLER_NAME)) {
final boolean enabled = !getFullScreenMagnificationController().isMagnifying(displayId);
- logAccessibilityShortcutActivated(MAGNIFICATION_COMPONENT_NAME, shortcutType, enabled);
+ logAccessibilityShortcutActivated(mContext, MAGNIFICATION_COMPONENT_NAME, shortcutType,
+ enabled);
sendAccessibilityButtonToInputFilter(displayId);
return;
}
@@ -2907,7 +2922,7 @@
}
// In case user assigned an accessibility shortcut target to the given shortcut.
if (performAccessibilityShortcutTargetActivity(displayId, targetComponentName)) {
- logAccessibilityShortcutActivated(targetComponentName, shortcutType);
+ logAccessibilityShortcutActivated(mContext, targetComponentName, shortcutType);
return;
}
// in case user assigned an accessibility service to the given shortcut.
@@ -2930,12 +2945,12 @@
featureInfo.getSettingKey(), mCurrentUserId);
// Assuming that the default state will be to have the feature off
if (!TextUtils.equals(featureInfo.getSettingOnValue(), setting.read())) {
- logAccessibilityShortcutActivated(assignedTarget, shortcutType, /* serviceEnabled= */
- true);
+ logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType,
+ /* serviceEnabled= */ true);
setting.write(featureInfo.getSettingOnValue());
} else {
- logAccessibilityShortcutActivated(assignedTarget, shortcutType, /* serviceEnabled= */
- false);
+ logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType,
+ /* serviceEnabled= */ false);
setting.write(featureInfo.getSettingOffValue());
}
return true;
@@ -2997,13 +3012,13 @@
if ((targetSdk <= Build.VERSION_CODES.Q && shortcutType == ACCESSIBILITY_SHORTCUT_KEY)
|| (targetSdk > Build.VERSION_CODES.Q && !requestA11yButton)) {
if (serviceConnection == null) {
- logAccessibilityShortcutActivated(assignedTarget,
- shortcutType, /* serviceEnabled= */ true);
+ logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType,
+ /* serviceEnabled= */ true);
enableAccessibilityServiceLocked(assignedTarget, mCurrentUserId);
} else {
- logAccessibilityShortcutActivated(assignedTarget,
- shortcutType, /* serviceEnabled= */ false);
+ logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType,
+ /* serviceEnabled= */ false);
disableAccessibilityServiceLocked(assignedTarget, mCurrentUserId);
}
return true;
@@ -3024,8 +3039,8 @@
return false;
}
// ServiceConnection means service enabled.
- logAccessibilityShortcutActivated(assignedTarget, shortcutType, /* serviceEnabled= */
- true);
+ logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType,
+ /* serviceEnabled= */ true);
serviceConnection.notifyAccessibilityButtonClickedLocked(displayId);
return true;
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index acbf487..5aec6aa 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -409,6 +409,8 @@
// If the set of providers has been modified, notify each active AppWidgetHost
scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
+ // Possibly notify any new components of widget id changes
+ mBackupRestoreController.widgetComponentsChanged(userId);
}
}
}
@@ -2471,8 +2473,8 @@
}
@Override
- public void restoreStarting(int userId) {
- mBackupRestoreController.restoreStarting(userId);
+ public void systemRestoreStarting(int userId) {
+ mBackupRestoreController.systemRestoreStarting(userId);
}
@Override
@@ -2481,8 +2483,8 @@
}
@Override
- public void restoreFinished(int userId) {
- mBackupRestoreController.restoreFinished(userId);
+ public void systemRestoreFinished(int userId) {
+ mBackupRestoreController.systemRestoreFinished(userId);
}
@SuppressWarnings("deprecation")
@@ -4272,6 +4274,9 @@
private final HashMap<Host, ArrayList<RestoreUpdateRecord>> mUpdatesByHost =
new HashMap<>();
+ @GuardedBy("mLock")
+ private boolean mHasSystemRestoreFinished;
+
public List<String> getWidgetParticipants(int userId) {
if (DEBUG) {
Slog.i(TAG, "Getting widget participants for user: " + userId);
@@ -4375,12 +4380,13 @@
return stream.toByteArray();
}
- public void restoreStarting(int userId) {
+ public void systemRestoreStarting(int userId) {
if (DEBUG) {
- Slog.i(TAG, "Restore starting for user: " + userId);
+ Slog.i(TAG, "System restore starting for user: " + userId);
}
synchronized (mLock) {
+ mHasSystemRestoreFinished = false;
// We're starting a new "system" restore operation, so any widget restore
// state that we see from here on is intended to replace the current
// widget configuration of any/all of the affected apps.
@@ -4542,26 +4548,90 @@
}
}
- // Called once following the conclusion of a restore operation. This is when we
+ // Called once following the conclusion of a system restore operation. This is when we
// send out updates to apps involved in widget-state restore telling them about
- // the new widget ID space.
- public void restoreFinished(int userId) {
+ // the new widget ID space. Apps that are not yet installed will be notifed when they are.
+ public void systemRestoreFinished(int userId) {
if (DEBUG) {
- Slog.i(TAG, "restoreFinished for " + userId);
+ Slog.i(TAG, "systemRestoreFinished for " + userId);
+ }
+ synchronized (mLock) {
+ mHasSystemRestoreFinished = true;
+ maybeSendWidgetRestoreBroadcastsLocked(userId);
+ }
+ }
+
+ // Called when widget components (hosts or providers) are added or changed. If system
+ // restore has completed, we use this opportunity to tell the apps to update to the new
+ // widget ID space. If system restore is still in progress, we delay the updates until
+ // the end, to allow all participants to restore their state before updating widget IDs.
+ public void widgetComponentsChanged(int userId) {
+ synchronized (mLock) {
+ if (mHasSystemRestoreFinished) {
+ maybeSendWidgetRestoreBroadcastsLocked(userId);
+ }
+ }
+ }
+
+ // Called following the conclusion of a restore operation and when widget components
+ // are added or changed. This is when we send out updates to apps involved in widget-state
+ // restore telling them about the new widget ID space.
+ @GuardedBy("mLock")
+ private void maybeSendWidgetRestoreBroadcastsLocked(int userId) {
+ if (DEBUG) {
+ Slog.i(TAG, "maybeSendWidgetRestoreBroadcasts for " + userId);
}
final UserHandle userHandle = new UserHandle(userId);
- synchronized (mLock) {
- // Build the providers' broadcasts and send them off
- Set<Map.Entry<Provider, ArrayList<RestoreUpdateRecord>>> providerEntries
- = mUpdatesByProvider.entrySet();
- for (Map.Entry<Provider, ArrayList<RestoreUpdateRecord>> e : providerEntries) {
- // For each provider there's a list of affected IDs
- Provider provider = e.getKey();
+ // Build the providers' broadcasts and send them off
+ Set<Map.Entry<Provider, ArrayList<RestoreUpdateRecord>>> providerEntries
+ = mUpdatesByProvider.entrySet();
+ for (Map.Entry<Provider, ArrayList<RestoreUpdateRecord>> e : providerEntries) {
+ // For each provider there's a list of affected IDs
+ Provider provider = e.getKey();
+ if (provider.zombie) {
+ // Provider not installed, we can't send them broadcasts yet.
+ // We'll be called again when the provider is installed.
+ continue;
+ }
+ ArrayList<RestoreUpdateRecord> updates = e.getValue();
+ final int pending = countPendingUpdates(updates);
+ if (DEBUG) {
+ Slog.i(TAG, "Provider " + provider + " pending: " + pending);
+ }
+ if (pending > 0) {
+ int[] oldIds = new int[pending];
+ int[] newIds = new int[pending];
+ final int N = updates.size();
+ int nextPending = 0;
+ for (int i = 0; i < N; i++) {
+ RestoreUpdateRecord r = updates.get(i);
+ if (!r.notified) {
+ r.notified = true;
+ oldIds[nextPending] = r.oldId;
+ newIds[nextPending] = r.newId;
+ nextPending++;
+ if (DEBUG) {
+ Slog.i(TAG, " " + r.oldId + " => " + r.newId);
+ }
+ }
+ }
+ sendWidgetRestoreBroadcastLocked(
+ AppWidgetManager.ACTION_APPWIDGET_RESTORED,
+ provider, null, oldIds, newIds, userHandle);
+ }
+ }
+
+ // same thing per host
+ Set<Map.Entry<Host, ArrayList<RestoreUpdateRecord>>> hostEntries
+ = mUpdatesByHost.entrySet();
+ for (Map.Entry<Host, ArrayList<RestoreUpdateRecord>> e : hostEntries) {
+ Host host = e.getKey();
+ if (host.id.uid != UNKNOWN_UID) {
ArrayList<RestoreUpdateRecord> updates = e.getValue();
final int pending = countPendingUpdates(updates);
if (DEBUG) {
- Slog.i(TAG, "Provider " + provider + " pending: " + pending);
+ Slog.i(TAG, "Host " + host + " pending: " + pending);
}
if (pending > 0) {
int[] oldIds = new int[pending];
@@ -4581,43 +4651,8 @@
}
}
sendWidgetRestoreBroadcastLocked(
- AppWidgetManager.ACTION_APPWIDGET_RESTORED,
- provider, null, oldIds, newIds, userHandle);
- }
- }
-
- // same thing per host
- Set<Map.Entry<Host, ArrayList<RestoreUpdateRecord>>> hostEntries
- = mUpdatesByHost.entrySet();
- for (Map.Entry<Host, ArrayList<RestoreUpdateRecord>> e : hostEntries) {
- Host host = e.getKey();
- if (host.id.uid != UNKNOWN_UID) {
- ArrayList<RestoreUpdateRecord> updates = e.getValue();
- final int pending = countPendingUpdates(updates);
- if (DEBUG) {
- Slog.i(TAG, "Host " + host + " pending: " + pending);
- }
- if (pending > 0) {
- int[] oldIds = new int[pending];
- int[] newIds = new int[pending];
- final int N = updates.size();
- int nextPending = 0;
- for (int i = 0; i < N; i++) {
- RestoreUpdateRecord r = updates.get(i);
- if (!r.notified) {
- r.notified = true;
- oldIds[nextPending] = r.oldId;
- newIds[nextPending] = r.newId;
- nextPending++;
- if (DEBUG) {
- Slog.i(TAG, " " + r.oldId + " => " + r.newId);
- }
- }
- }
- sendWidgetRestoreBroadcastLocked(
- AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED,
- null, host, oldIds, newIds, userHandle);
- }
+ AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED,
+ null, host, oldIds, newIds, userHandle);
}
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 312cde6..de5f47d 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -662,6 +662,26 @@
return false;
}
+ /**
+ * Requests a count of saved passwords from the current service.
+ *
+ * @return {@code true} if the request succeeded
+ */
+ // Called by Shell command
+ boolean requestSavedPasswordCount(@UserIdInt int userId, @NonNull IResultReceiver receiver) {
+ enforceCallingPermissionForManagement();
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ service.requestSavedPasswordCount(receiver);
+ return true;
+ } else if (sVerbose) {
+ Slog.v(TAG, "requestSavedPasswordCount(): no service for " + userId);
+ }
+ }
+ return false;
+ }
+
private void setLoggingLevelsLocked(boolean debug, boolean verbose) {
com.android.server.autofill.Helper.sDebug = debug;
android.view.autofill.Helper.sDebug = debug;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index a80efc4..5f2d4e8 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -76,6 +76,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.os.IResultReceiver;
import com.android.server.LocalServices;
import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
import com.android.server.autofill.AutofillManagerService.DisabledInfoCache;
@@ -1181,6 +1182,15 @@
}
@GuardedBy("mLock")
+ void requestSavedPasswordCount(IResultReceiver receiver) {
+ RemoteFillService remoteService =
+ new RemoteFillService(
+ getContext(), mInfo.getServiceInfo().getComponentName(), mUserId,
+ /* callbacks= */ null, mMaster.isInstantServiceAllowed());
+ remoteService.onSavedPasswordCountRequest(receiver);
+ }
+
+ @GuardedBy("mLock")
@Nullable RemoteAugmentedAutofillService getRemoteAugmentedAutofillServiceLocked() {
if (mRemoteAugmentedAutofillService == null) {
final String serviceName = mMaster.mAugmentedAutofillResolver.getServiceName(mUserId);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index 68e6290..1eaa59a 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -17,6 +17,7 @@
package com.android.server.autofill;
import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES;
+import static android.service.autofill.AutofillService.EXTRA_RESULT;
import static com.android.server.autofill.AutofillManagerService.RECEIVER_BUNDLE_EXTRA_SESSIONS;
@@ -89,6 +90,9 @@
pw.println(" get bind-instant-service-allowed");
pw.println(" Gets whether binding to services provided by instant apps is allowed");
pw.println("");
+ pw.println(" get saved-password-count");
+ pw.println(" Gets the number of saved passwords in the current service.");
+ pw.println("");
pw.println(" set log_level [off | debug | verbose]");
pw.println(" Sets the Autofill log level.");
pw.println("");
@@ -145,6 +149,8 @@
return getBindInstantService(pw);
case "default-augmented-service-enabled":
return getDefaultAugmentedServiceEnabled(pw);
+ case "saved-password-count":
+ return getSavedPasswordCount(pw);
default:
pw.println("Invalid set: " + what);
return -1;
@@ -342,6 +348,25 @@
return 0;
}
+ private int getSavedPasswordCount(PrintWriter pw) {
+ final int userId = getNextIntArgRequired();
+ CountDownLatch latch = new CountDownLatch(1);
+ IResultReceiver resultReceiver = new IResultReceiver.Stub() {
+ @Override
+ public void send(int resultCode, Bundle resultData) {
+ pw.println("resultCode=" + resultCode);
+ if (resultCode == 0 && resultData != null) {
+ pw.println("value=" + resultData.getInt(EXTRA_RESULT));
+ }
+ latch.countDown();
+ }
+ };
+ if (mService.requestSavedPasswordCount(userId, resultReceiver)) {
+ waitForLatch(pw, latch);
+ }
+ return 0;
+ }
+
private int requestDestroy(PrintWriter pw) {
if (!isNextArgSessions(pw)) {
return -1;
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index b0755ac..94872b0 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -41,6 +41,7 @@
import com.android.internal.infra.AbstractRemoteService;
import com.android.internal.infra.ServiceConnector;
+import com.android.internal.os.IResultReceiver;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
@@ -225,6 +226,10 @@
}));
}
+ void onSavedPasswordCountRequest(IResultReceiver receiver) {
+ run(service -> service.onSavedPasswordCountRequest(receiver));
+ }
+
public void destroy() {
unbind();
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 320047f..078d908 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2648,6 +2648,10 @@
Slog.d(TAG, "Starting partition or augmented request for view id " + id + ": "
+ viewState.getStateAsString());
}
+ // Fix to always let standard autofill start.
+ // Sometimes activity contain IMPORTANT_FOR_AUTOFILL_NO fields which marks session as
+ // augmentedOnly, but other fields are still fillable by standard autofill.
+ mSessionFlags.mAugmentedAutofillOnly = false;
requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_PARTITION, flags);
return true;
}
@@ -2847,12 +2851,18 @@
if (sDebug) Slog.d(TAG, "trigger augmented autofill.");
triggerAugmentedAutofillLocked(flags);
} else {
- if (sDebug) Slog.d(TAG, "skip augmented autofill for same view.");
+ if (sDebug) {
+ Slog.d(TAG, "skip augmented autofill for same view: "
+ + "same view entered");
+ }
}
return;
} else if (mSessionFlags.mAugmentedAutofillOnly && isSameViewEntered) {
// Regular autofill is disabled.
- if (sDebug) Slog.d(TAG, "skip augmented autofill for same view.");
+ if (sDebug) {
+ Slog.d(TAG, "skip augmented autofill for same view: "
+ + "standard autofill disabled.");
+ }
return;
}
}
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 261ebe6..f07bac8 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -381,7 +381,7 @@
// If we're starting a full-system restore, set up to begin widget ID remapping
if (mIsSystemRestore) {
- AppWidgetBackupBridge.restoreStarting(mUserId);
+ AppWidgetBackupBridge.systemRestoreStarting(mUserId);
}
try {
@@ -1133,8 +1133,10 @@
restoreAgentTimeoutMillis);
}
- // Kick off any work that may be needed regarding app widget restores
- AppWidgetBackupBridge.restoreFinished(mUserId);
+ if (mIsSystemRestore) {
+ // Kick off any work that may be needed regarding app widget restores
+ AppWidgetBackupBridge.systemRestoreFinished(mUserId);
+ }
// If this was a full-system restore, record the ancestral
// dataset information
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 83dfe8e..906edb3 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -454,19 +454,13 @@
}).cancelTimeout();
}, FgThread.getExecutor()).whenComplete(uncheckExceptions((association, err) -> {
-
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- if (err == null) {
- addAssociation(association);
- } else {
- Slog.e(LOG_TAG, "Failed to discover device(s)", err);
- callback.onFailure("No devices found: " + err.getMessage());
- }
- cleanup();
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
+ if (err == null) {
+ addAssociation(association);
+ } else {
+ Slog.e(LOG_TAG, "Failed to discover device(s)", err);
+ callback.onFailure("No devices found: " + err.getMessage());
}
+ cleanup();
}));
}
@@ -759,9 +753,19 @@
}
return notMatch;
}));
+ restartBleScan();
}
void onAssociationPreRemove(Association association) {
+ if (association.isNotifyOnDeviceNearby()) {
+ ServiceConnector<ICompanionDeviceService> serviceConnector =
+ mDeviceListenerServiceConnectors.forUser(association.getUserId())
+ .get(association.getPackageName());
+ if (serviceConnector != null) {
+ serviceConnector.unbind();
+ }
+ }
+
String deviceProfile = association.getDeviceProfile();
if (deviceProfile != null) {
Association otherAssociationWithDeviceProfile = find(
@@ -793,16 +797,6 @@
}
}
}
-
- if (association.isNotifyOnDeviceNearby()) {
- ServiceConnector<ICompanionDeviceService> serviceConnector =
- mDeviceListenerServiceConnectors.forUser(association.getUserId())
- .get(association.getPackageName());
- if (serviceConnector != null) {
- serviceConnector.unbind();
- restartBleScan();
- }
- }
}
private void updateSpecialAccessPermissionForAssociatedPackage(Association association) {
diff --git a/services/companion/java/com/android/server/companion/OWNERS b/services/companion/java/com/android/server/companion/OWNERS
index da723b3..734d8b6 100644
--- a/services/companion/java/com/android/server/companion/OWNERS
+++ b/services/companion/java/com/android/server/companion/OWNERS
@@ -1 +1 @@
-eugenesusla@google.com
\ No newline at end of file
+include /core/java/android/companion/OWNERS
\ No newline at end of file
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 9299624..f7ddd29 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1151,6 +1151,8 @@
/**
* Deletes the OAT artifacts of a package.
+ * @param packageName a specific package
+ * @return the number of freed bytes or -1 if there was an error in the process.
*/
- public abstract void deleteOatArtifactsOfPackage(String packageName);
+ public abstract long deleteOatArtifactsOfPackage(String packageName);
}
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index 26ecee8..70176a0 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -38,6 +38,7 @@
import android.net.VpnService;
import android.net.util.NetdService;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.INetworkManagementService;
@@ -348,9 +349,17 @@
/**
* Start legacy VPN, controlling native daemons as needed. Creates a
* secondary thread to perform connection work, returning quickly.
+ *
+ * Legacy VPN is deprecated starting from Android S. So this API shouldn't be called if the
+ * initial SDK version of device is Android S+. Otherwise, UnsupportedOperationException will be
+ * thrown.
*/
+ @SuppressWarnings("AndroidFrameworkCompatChange") // This is not an app-visible API.
@Override
public void startLegacyVpn(VpnProfile profile) {
+ if (Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.S) {
+ throw new UnsupportedOperationException("Legacy VPN is deprecated");
+ }
int user = UserHandle.getUserId(mDeps.getCallingUid());
// Note that if the caller is not system (uid >= Process.FIRST_APPLICATION_UID),
// the code might not work well since getActiveNetwork might return null if the uid is
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 0f9a6fa..fcd049f 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -676,7 +676,7 @@
final UUID errorId;
if (mTraceErrorLogger.isAddErrorIdEnabled()) {
errorId = mTraceErrorLogger.generateErrorId();
- mTraceErrorLogger.addErrorIdToTrace(errorId);
+ mTraceErrorLogger.addErrorIdToTrace("system_server", errorId);
} else {
errorId = null;
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index a231de3..2a634eb 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -375,6 +375,9 @@
// Cancel account request notification if a permission was preventing the account access
mPackageManager.addOnPermissionsChangeListener(
(int uid) -> {
+ // Permission changes cause requires updating accounts cache.
+ AccountManager.invalidateLocalAccountsDataCaches();
+
Account[] accounts = null;
String[] packageNames = mPackageManager.getPackagesForUid(uid);
if (packageNames != null) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 0802123..89781d3 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1877,7 +1877,6 @@
active.mNumActive++;
}
r.isForeground = true;
- r.mLogEntering = true;
// The logging of FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER event could
// be deferred, make a copy of mAllowStartForeground and
// mAllowWhileInUsePermissionInFgs.
@@ -1903,6 +1902,9 @@
AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
registerAppOpCallbackLocked(r);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, true);
+ logFGSStateChangeLocked(r,
+ FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
+ 0);
}
// Even if the service is already a FGS, we need to update the notification,
// so we need to call it again.
@@ -1958,6 +1960,7 @@
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
r.mFgsExitTime > r.mFgsEnterTime
? (int)(r.mFgsExitTime - r.mFgsEnterTime) : 0);
+ r.mFgsNotificationWasDeferred = false;
resetFgsRestrictionLocked(r);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
if (r.app != null) {
@@ -1973,7 +1976,7 @@
r.foregroundId = 0;
r.foregroundNoti = null;
} else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
- r.stripForegroundServiceFlagFromNotification();
+ dropFgsNotificationStateLocked(r);
if ((flags & Service.STOP_FOREGROUND_DETACH) != 0) {
r.foregroundId = 0;
r.foregroundNoti = null;
@@ -2162,6 +2165,7 @@
}
r.fgDisplayTime = when;
r.mFgsNotificationDeferred = true;
+ r.mFgsNotificationWasDeferred = true;
r.mFgsNotificationShown = false;
mPendingFgsNotifications.add(r);
if (DEBUG_FOREGROUND_SERVICE) {
@@ -2205,11 +2209,6 @@
Slog.d(TAG_SERVICE, " - service no longer running/fg, ignoring");
}
}
- // Regardless of whether we needed to post the notification or the
- // service is no longer running, we may not have logged its FGS
- // transition yet depending on the timing and API sequence that led
- // to this point - so make sure to do so.
- maybeLogFGSStateEnteredLocked(r);
}
}
if (DEBUG_FOREGROUND_SERVICE) {
@@ -2252,16 +2251,6 @@
}
}
- private void maybeLogFGSStateEnteredLocked(ServiceRecord r) {
- if (r.mLogEntering) {
- logFGSStateChangeLocked(r,
- FrameworkStatsLog
- .FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
- 0);
- r.mLogEntering = false;
- }
- }
-
/**
* Callback from NotificationManagerService whenever it posts a notification
* associated with a foreground service. This is the unified handling point
@@ -2280,9 +2269,7 @@
&& id == sr.foregroundId
&& sr.appInfo.packageName.equals(pkg)) {
// Found it. If 'shown' is false, it means that the notification
- // subsystem will not be displaying it yet, so all we do is log
- // the "fgs entered" transition noting deferral, then we're done.
- maybeLogFGSStateEnteredLocked(sr);
+ // subsystem will not be displaying it yet.
if (shown) {
if (DEBUG_FOREGROUND_SERVICE) {
Slog.d(TAG_SERVICE, "Notification shown; canceling deferral of "
@@ -4238,6 +4225,8 @@
}
r.isForeground = false;
+ r.mFgsNotificationWasDeferred = false;
+ dropFgsNotificationStateLocked(r);
r.foregroundId = 0;
r.foregroundNoti = null;
resetFgsRestrictionLocked(r);
@@ -4303,6 +4292,35 @@
smap.ensureNotStartingBackgroundLocked(r);
}
+ private void dropFgsNotificationStateLocked(ServiceRecord r) {
+ // If this is the only FGS using this notification, clear its FGS flag
+ boolean shared = false;
+ final ServiceMap smap = mServiceMap.get(r.userId);
+ if (smap != null) {
+ // Is any other FGS using this notification?
+ final int numServices = smap.mServicesByInstanceName.size();
+ for (int i = 0; i < numServices; i++) {
+ final ServiceRecord sr = smap.mServicesByInstanceName.valueAt(i);
+ if (sr == r) {
+ continue;
+ }
+ if (sr.isForeground
+ && r.foregroundId == sr.foregroundId
+ && r.appInfo.packageName.equals(sr.appInfo.packageName)) {
+ shared = true;
+ break;
+ }
+ }
+ } else {
+ Slog.wtf(TAG, "FGS " + r + " not found!");
+ }
+
+ // No other FGS is sharing this notification, so we're done with it
+ if (!shared) {
+ r.stripForegroundServiceFlagFromNotification();
+ }
+ }
+
void removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp,
ActivityServiceConnectionsHolder skipAct, boolean enqueueOomAdj) {
IBinder binder = c.conn.asBinder();
@@ -6177,8 +6195,10 @@
&& code != REASON_UID_VISIBLE;
}
- // TODO: remove this notification after feature development is done
private void showFgsBgRestrictedNotificationLocked(ServiceRecord r) {
+ if (!mAm.mConstants.mFgsStartRestrictionNotificationEnabled /* default is false */) {
+ return;
+ }
final Context context = mAm.mContext;
final String title = "Foreground Service BG-Launch Restricted";
final String content = "App restricted: " + r.mRecentCallingPackage;
@@ -6263,7 +6283,7 @@
? r.mRecentCallerApplicationInfo.targetSdkVersion : 0,
r.mInfoTempFgsAllowListReason != null
? r.mInfoTempFgsAllowListReason.mCallingUid : INVALID_UID,
- r.mFgsNotificationDeferred,
+ r.mFgsNotificationWasDeferred,
r.mFgsNotificationShown,
durationMs,
r.mStartForegroundCount,
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 0d19efc..445d0ba 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -198,6 +198,13 @@
"default_fgs_starts_restriction_enabled";
/**
+ * Default value for mFgsStartRestrictionNotificationEnabled if not explicitly set in
+ * Settings.Global.
+ */
+ private static final String KEY_DEFAULT_FGS_STARTS_RESTRICTION_NOTIFICATION_ENABLED =
+ "default_fgs_starts_restriction_notification_enabled";
+
+ /**
* Default value for mFgsStartRestrictionCheckCallerTargetSdk if not explicitly set in
* Settings.Global.
*/
@@ -432,6 +439,10 @@
// at all.
volatile boolean mFlagFgsStartRestrictionEnabled = true;
+ // Whether to display a notification when a service is restricted from startForeground due to
+ // foreground service background start restriction.
+ volatile boolean mFgsStartRestrictionNotificationEnabled = false;
+
/**
* Indicates whether the foreground service background start restriction is enabled for
* caller app that is targeting S+.
@@ -652,6 +663,9 @@
case KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED:
updateFgsStartsRestriction();
break;
+ case KEY_DEFAULT_FGS_STARTS_RESTRICTION_NOTIFICATION_ENABLED:
+ updateFgsStartsRestrictionNotification();
+ break;
case KEY_DEFAULT_FGS_STARTS_RESTRICTION_CHECK_CALLER_TARGET_SDK:
updateFgsStartsRestrictionCheckCallerTargetSdk();
break;
@@ -953,6 +967,13 @@
/*defaultValue*/ true);
}
+ private void updateFgsStartsRestrictionNotification() {
+ mFgsStartRestrictionNotificationEnabled = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_DEFAULT_FGS_STARTS_RESTRICTION_NOTIFICATION_ENABLED,
+ /*defaultValue*/ false);
+ }
+
private void updateFgsStartsRestrictionCheckCallerTargetSdk() {
mFgsStartRestrictionCheckCallerTargetSdk = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -1272,6 +1293,9 @@
pw.print("="); pw.println(mFlagBackgroundFgsStartRestrictionEnabled);
pw.print(" "); pw.print(KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED); pw.print("=");
pw.println(mFlagFgsStartRestrictionEnabled);
+ pw.print(" "); pw.print(KEY_DEFAULT_FGS_STARTS_RESTRICTION_NOTIFICATION_ENABLED);
+ pw.print("=");
+ pw.println(mFgsStartRestrictionNotificationEnabled);
pw.print(" "); pw.print(KEY_DEFAULT_FGS_STARTS_RESTRICTION_CHECK_CALLER_TARGET_SDK);
pw.print("="); pw.println(mFgsStartRestrictionCheckCallerTargetSdk);
pw.print(" "); pw.print(KEY_FGS_ATOM_SAMPLE_RATE);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2de091b..0c97724 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5684,6 +5684,16 @@
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
}
+ try {
+ if (uid != 0) { // bypass the root
+ final String[] packageNames = getPackageManager().getPackagesForUid(uid);
+ if (ArrayUtils.isEmpty(packageNames)) {
+ // The uid is not existed or not visible to the caller.
+ return PackageManager.PERMISSION_DENIED;
+ }
+ }
+ } catch (RemoteException e) {
+ }
return mUgmInternal.checkUriPermission(new GrantUri(userId, uri, modeFlags), uid, modeFlags)
? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED;
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 4e6e91a..08f6f1e 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
@@ -78,6 +79,7 @@
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.BatteryUsageStatsProvider;
+import com.android.internal.os.BatteryUsageStatsStore;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.PowerProfile;
import com.android.internal.os.RailStats;
@@ -124,10 +126,12 @@
Watchdog.Monitor {
static final String TAG = "BatteryStatsService";
static final boolean DBG = false;
+ private static final boolean BATTERY_USAGE_STORE_ENABLED = true;
private static IBatteryStats sService;
final BatteryStatsImpl mStats;
+ private final BatteryUsageStatsStore mBatteryUsageStatsStore;
private final BatteryStatsImpl.UserInfoProvider mUserManagerUserInfoProvider;
private final Context mContext;
private final BatteryExternalStatsWorker mWorker;
@@ -348,7 +352,14 @@
mStats.setPowerProfileLocked(new PowerProfile(context));
mStats.startTrackingSystemServerCpuTime();
- mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats);
+ if (BATTERY_USAGE_STORE_ENABLED) {
+ mBatteryUsageStatsStore =
+ new BatteryUsageStatsStore(context, mStats, systemDir, mHandler);
+ } else {
+ mBatteryUsageStatsStore = null;
+ }
+ mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats,
+ mBatteryUsageStatsStore);
}
public void publish() {
@@ -752,6 +763,10 @@
FrameworkStatsLog.BATTERY_USAGE_STATS_SINCE_RESET_USING_POWER_PROFILE_MODEL,
null, // use default PullAtomMetadata values
BackgroundThread.getExecutor(), pullAtomCallback);
+ statsManager.setPullAtomCallback(
+ FrameworkStatsLog.BATTERY_USAGE_STATS_BEFORE_RESET,
+ null, // use default PullAtomMetadata values
+ BackgroundThread.getExecutor(), pullAtomCallback);
}
/** StatsPullAtomCallback for pulling BatteryUsageStats data. */
@@ -768,6 +783,17 @@
new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build();
bus = getBatteryUsageStats(List.of(powerProfileQuery)).get(0);
break;
+ case FrameworkStatsLog.BATTERY_USAGE_STATS_BEFORE_RESET:
+ final long sessionStart = mBatteryUsageStatsStore
+ .getLastBatteryUsageStatsBeforeResetAtomPullTimestamp();
+ final long sessionEnd = mStats.getStartClockTime();
+ final BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
+ .aggregateSnapshots(sessionStart, sessionEnd)
+ .build();
+ bus = getBatteryUsageStats(List.of(query)).get(0);
+ mBatteryUsageStatsStore
+ .setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(sessionEnd);
+ break;
default:
throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
}
@@ -2499,6 +2525,12 @@
* @hide
*/
public CellularBatteryStats getCellularBatteryStats() {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.UPDATE_DEVICE_STATS) == PERMISSION_DENIED) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BATTERY_STATS, null);
+ }
+
// Wait for the completion of pending works if there is any
awaitCompletion();
synchronized (mStats) {
@@ -2511,6 +2543,12 @@
* @hide
*/
public WifiBatteryStats getWifiBatteryStats() {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.UPDATE_DEVICE_STATS) == PERMISSION_DENIED) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BATTERY_STATS, null);
+ }
+
// Wait for the completion of pending works if there is any
awaitCompletion();
synchronized (mStats) {
@@ -2523,6 +2561,8 @@
* @hide
*/
public GpsBatteryStats getGpsBatteryStats() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BATTERY_STATS, null);
+
// Wait for the completion of pending works if there is any
awaitCompletion();
synchronized (mStats) {
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 966e746..317b775 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -203,8 +203,6 @@
updateMinOomAdjThrottle();
} else if (KEY_COMPACT_THROTTLE_MAX_OOM_ADJ.equals(name)) {
updateMaxOomAdjThrottle();
- } else if (KEY_FREEZER_DEBOUNCE_TIMEOUT.equals(name)) {
- updateFreezerDebounceTimeout();
}
}
}
@@ -344,7 +342,6 @@
updateUseFreezer();
updateMinOomAdjThrottle();
updateMaxOomAdjThrottle();
- updateFreezerDebounceTimeout();
}
}
@@ -656,6 +653,7 @@
|| DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) {
mUseFreezer = isFreezerSupported();
+ updateFreezerDebounceTimeout();
}
final boolean useFreezer = mUseFreezer;
@@ -834,7 +832,8 @@
@GuardedBy("mPhenotypeFlagLock")
private void updateFreezerDebounceTimeout() {
- mFreezerDebounceTimeout = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ mFreezerDebounceTimeout = DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
KEY_FREEZER_DEBOUNCE_TIMEOUT, DEFAULT_FREEZER_DEBOUNCE_TIMEOUT);
if (mFreezerDebounceTimeout < 0) {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 6228637..aef402a 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -328,7 +328,8 @@
final int index = mCache.indexOfKey(app.packageName);
Pair<Boolean, WeakReference<ApplicationInfo>> p;
if (index < 0) {
- p = new Pair<>(mPlatformCompat.isChangeEnabled(mChangeId, app),
+ p = new Pair<>(mPlatformCompat.isChangeEnabledInternalNoLogging(mChangeId,
+ app),
new WeakReference<>(app));
mCache.put(app.packageName, p);
return p.first;
@@ -338,7 +339,8 @@
return p.first;
}
// Cache is invalid, regenerate it
- p = new Pair<>(mPlatformCompat.isChangeEnabled(mChangeId, app),
+ p = new Pair<>(mPlatformCompat.isChangeEnabledInternalNoLogging(mChangeId,
+ app),
new WeakReference<>(app));
mCache.setValueAt(index, p);
return p.first;
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index b2266f6..4455fd0 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -267,7 +267,7 @@
if (mService.mTraceErrorLogger.isAddErrorIdEnabled()) {
errorId = mService.mTraceErrorLogger.generateErrorId();
- mService.mTraceErrorLogger.addErrorIdToTrace(errorId);
+ mService.mTraceErrorLogger.addErrorIdToTrace(mApp.processName, errorId);
} else {
errorId = null;
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 8ebc987..1b67679 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -4698,10 +4698,10 @@
final ApplicationInfo ai = AppGlobals.getPackageManager()
.getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
if (ai != null) {
- app.getThread().scheduleApplicationInfoChanged(ai);
if (ai.packageName.equals(app.info.packageName)) {
app.info = ai;
}
+ app.getThread().scheduleApplicationInfoChanged(ai);
targetProcesses.add(app.getWindowProcessController());
}
} catch (RemoteException e) {
@@ -4712,8 +4712,7 @@
});
}
- mService.mActivityTaskManager.updateAssetConfiguration(
- updateFrameworkRes ? null : targetProcesses);
+ mService.mActivityTaskManager.updateAssetConfiguration(targetProcesses, updateFrameworkRes);
}
@GuardedBy("mService")
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 3ba07af..804e442 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -20,6 +20,7 @@
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import static android.os.PowerExemptionManager.REASON_DENIED;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_SERVICE;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -109,7 +110,6 @@
boolean fgWaiting; // is a timeout for going foreground already scheduled?
boolean isNotAppComponentUsage; // is service binding not considered component/package usage?
boolean isForeground; // is service currently in foreground mode?
- boolean mLogEntering; // need to report fgs transition once deferral policy is known
int foregroundId; // Notification ID of last foreground req.
Notification foregroundNoti; // Notification record of foreground state.
long fgDisplayTime; // time at which the FGS notification should become visible
@@ -167,8 +167,10 @@
long mFgsEnterTime = 0;
// The uptime when the service exits FGS state.
long mFgsExitTime = 0;
- // FGS notification was deferred.
+ // FGS notification is deferred.
boolean mFgsNotificationDeferred;
+ // FGS notification was deferred.
+ boolean mFgsNotificationWasDeferred;
// FGS notification was shown before the FGS finishes, or it wasn't deferred in the first place.
boolean mFgsNotificationShown;
@@ -927,13 +929,17 @@
public void postNotification() {
final int appUid = appInfo.uid;
final int appPid = app.getPid();
- if (foregroundId != 0 && foregroundNoti != null) {
+ if (isForeground && foregroundNoti != null) {
// Do asynchronous communication with notification manager to
// avoid deadlocks.
final String localPackageName = packageName;
final int localForegroundId = foregroundId;
final Notification _foregroundNoti = foregroundNoti;
final ServiceRecord record = this;
+ if (DEBUG_FOREGROUND_SERVICE) {
+ Slog.d(TAG, "Posting notification " + _foregroundNoti
+ + " for foreground service " + this);
+ }
ams.mHandler.post(new Runnable() {
public void run() {
NotificationManagerInternal nm = LocalServices.getService(
@@ -1065,10 +1071,6 @@
}
public void stripForegroundServiceFlagFromNotification() {
- if (foregroundId == 0) {
- return;
- }
-
final int localForegroundId = foregroundId;
final int localUserId = userId;
final String localPackageName = packageName;
diff --git a/services/core/java/com/android/server/am/TraceErrorLogger.java b/services/core/java/com/android/server/am/TraceErrorLogger.java
index 55f5715..c658100 100644
--- a/services/core/java/com/android/server/am/TraceErrorLogger.java
+++ b/services/core/java/com/android/server/am/TraceErrorLogger.java
@@ -46,10 +46,12 @@
* can be uniquely identified. We also add the same id to the dropbox entry of the error, so
* that we can join the trace and the error server-side.
*
- * @param errorId The unique id with which to tag the trace.
+ * @param processName The process name to include in the error id.
+ * @param errorId The unique id with which to tag the trace.
*/
- public void addErrorIdToTrace(UUID errorId) {
- Trace.traceCounter(Trace.TRACE_TAG_ACTIVITY_MANAGER, COUNTER_PREFIX + errorId.toString(),
+ public void addErrorIdToTrace(String processName, UUID errorId) {
+ Trace.traceCounter(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ COUNTER_PREFIX + processName + "#" + errorId.toString(),
PLACEHOLDER_VALUE);
}
}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 697f6fa..ae1cd51 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -21,10 +21,18 @@
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static com.android.server.wm.CompatModePackages.DOWNSCALED;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_30;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_35;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_40;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_45;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_50;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_55;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_60;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_65;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_70;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_75;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_80;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_85;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_90;
import android.Manifest;
@@ -213,6 +221,39 @@
}
}
+ // Turn the raw string to the corresponding CompatChange id.
+ static long getCompatChangeId(String raw) {
+ switch (raw) {
+ case "0.3":
+ return DOWNSCALE_30;
+ case "0.35":
+ return DOWNSCALE_35;
+ case "0.4":
+ return DOWNSCALE_40;
+ case "0.45":
+ return DOWNSCALE_45;
+ case "0.5":
+ return DOWNSCALE_50;
+ case "0.55":
+ return DOWNSCALE_55;
+ case "0.6":
+ return DOWNSCALE_60;
+ case "0.65":
+ return DOWNSCALE_65;
+ case "0.7":
+ return DOWNSCALE_70;
+ case "0.75":
+ return DOWNSCALE_75;
+ case "0.8":
+ return DOWNSCALE_80;
+ case "0.85":
+ return DOWNSCALE_85;
+ case "0.9":
+ return DOWNSCALE_90;
+ }
+ return 0;
+ }
+
/**
* GamePackageConfiguration manages all game mode config details for its associated package.
*/
@@ -331,19 +372,7 @@
* Get the corresponding compat change id for the current scaling string.
*/
public long getCompatChangeId() {
- switch (mScaling) {
- case "0.5":
- return DOWNSCALE_50;
- case "0.6":
- return DOWNSCALE_60;
- case "0.7":
- return DOWNSCALE_70;
- case "0.8":
- return DOWNSCALE_80;
- case "0.9":
- return DOWNSCALE_90;
- }
- return 0;
+ return GameManagerService.getCompatChangeId(mScaling);
}
}
@@ -663,10 +692,18 @@
Slog.i(TAG, "Enabling downscale: " + scaleId + " for " + packageName);
final ArrayMap<Long, PackageOverride> overrides = new ArrayMap<>();
overrides.put(DOWNSCALED, COMPAT_ENABLED);
+ overrides.put(DOWNSCALE_30, COMPAT_DISABLED);
+ overrides.put(DOWNSCALE_35, COMPAT_DISABLED);
+ overrides.put(DOWNSCALE_40, COMPAT_DISABLED);
+ overrides.put(DOWNSCALE_45, COMPAT_DISABLED);
overrides.put(DOWNSCALE_50, COMPAT_DISABLED);
+ overrides.put(DOWNSCALE_55, COMPAT_DISABLED);
overrides.put(DOWNSCALE_60, COMPAT_DISABLED);
+ overrides.put(DOWNSCALE_65, COMPAT_DISABLED);
overrides.put(DOWNSCALE_70, COMPAT_DISABLED);
+ overrides.put(DOWNSCALE_75, COMPAT_DISABLED);
overrides.put(DOWNSCALE_80, COMPAT_DISABLED);
+ overrides.put(DOWNSCALE_85, COMPAT_DISABLED);
overrides.put(DOWNSCALE_90, COMPAT_DISABLED);
overrides.put(scaleId, COMPAT_ENABLED);
final CompatibilityOverrideConfig changeConfig = new CompatibilityOverrideConfig(
diff --git a/services/core/java/com/android/server/app/GameManagerShellCommand.java b/services/core/java/com/android/server/app/GameManagerShellCommand.java
index 2074ffa..699f9e2 100644
--- a/services/core/java/com/android/server/app/GameManagerShellCommand.java
+++ b/services/core/java/com/android/server/app/GameManagerShellCommand.java
@@ -16,6 +16,21 @@
package com.android.server.app;
+import static com.android.server.wm.CompatModePackages.DOWNSCALED;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_30;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_35;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_40;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_45;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_50;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_55;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_60;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_65;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_70;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_75;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_80;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_85;
+import static com.android.server.wm.CompatModePackages.DOWNSCALE_90;
+
import android.app.ActivityManager;
import android.app.GameManager;
import android.app.IGameManagerService;
@@ -27,7 +42,6 @@
import com.android.internal.compat.CompatibilityChangeConfig;
import com.android.server.compat.PlatformCompat;
-import com.android.server.wm.CompatModePackages;
import java.io.PrintWriter;
import java.util.Set;
@@ -43,12 +57,21 @@
public GameManagerShellCommand() {}
private static final ArraySet<Long> DOWNSCALE_CHANGE_IDS = new ArraySet<>(new Long[]{
- CompatModePackages.DOWNSCALED,
- CompatModePackages.DOWNSCALE_90,
- CompatModePackages.DOWNSCALE_80,
- CompatModePackages.DOWNSCALE_70,
- CompatModePackages.DOWNSCALE_60,
- CompatModePackages.DOWNSCALE_50});
+ DOWNSCALED,
+ DOWNSCALE_90,
+ DOWNSCALE_85,
+ DOWNSCALE_80,
+ DOWNSCALE_75,
+ DOWNSCALE_70,
+ DOWNSCALE_65,
+ DOWNSCALE_60,
+ DOWNSCALE_55,
+ DOWNSCALE_50,
+ DOWNSCALE_45,
+ DOWNSCALE_40,
+ DOWNSCALE_35,
+ DOWNSCALE_30,
+ });
@Override
public int onCommand(String cmd) {
@@ -62,32 +85,9 @@
final String ratio = getNextArgRequired();
final String packageName = getNextArgRequired();
- final long changeId;
- switch (ratio) {
- case "0.5":
- changeId = CompatModePackages.DOWNSCALE_50;
- break;
- case "0.6":
- changeId = CompatModePackages.DOWNSCALE_60;
- break;
- case "0.7":
- changeId = CompatModePackages.DOWNSCALE_70;
- break;
- case "0.8":
- changeId = CompatModePackages.DOWNSCALE_80;
- break;
- case "0.9":
- changeId = CompatModePackages.DOWNSCALE_90;
- break;
- case "disable":
- changeId = 0;
- break;
- default:
- changeId = -1;
- pw.println("Invalid scaling ratio '" + ratio + "'");
- break;
- }
- if (changeId == -1) {
+ final long changeId = GameManagerService.getCompatChangeId(ratio);
+ if (changeId == 0 && !ratio.equals("disable")) {
+ pw.println("Invalid scaling ratio '" + ratio + "'");
break;
}
@@ -96,10 +96,10 @@
if (changeId == 0) {
disabled = DOWNSCALE_CHANGE_IDS;
} else {
- enabled.add(CompatModePackages.DOWNSCALED);
+ enabled.add(DOWNSCALED);
enabled.add(changeId);
disabled = DOWNSCALE_CHANGE_IDS.stream()
- .filter(it -> it != CompatModePackages.DOWNSCALED && it != changeId)
+ .filter(it -> it != DOWNSCALED && it != changeId)
.collect(Collectors.toSet());
}
@@ -204,7 +204,7 @@
pw.println("Game manager (game) commands:");
pw.println(" help");
pw.println(" Print this help text.");
- pw.println(" downscale [0.5|0.6|0.7|0.8|0.9|disable] <PACKAGE_NAME>");
+ pw.println(" downscale [0.3|0.35|0.4|0.45|0.5|0.55|0.6|0.65|0.7|0.75|0.8|0.85|0.9|disable] <PACKAGE_NAME>");
pw.println(" Force app to run at the specified scaling ratio.");
pw.println(" mode [--user <USER_ID>] [1|2|3|standard|performance|battery] <PACKAGE_NAME>");
pw.println(" Force app to run in the specified game mode, if supported.");
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 4cb2e9b..7f1402d 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -456,7 +456,9 @@
private void hibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackageGlobally");
if (mOatArtifactDeletionEnabled) {
- mPackageManagerInternal.deleteOatArtifactsOfPackage(packageName);
+ state.savedByte = Math.max(
+ mPackageManagerInternal.deleteOatArtifactsOfPackage(packageName),
+ 0);
}
state.hibernated = true;
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
@@ -469,6 +471,7 @@
private void unhibernatePackageGlobally(@NonNull String packageName, GlobalLevelState state) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackageGlobally");
state.hibernated = false;
+ state.savedByte = 0;
state.lastUnhibernatedMs = System.currentTimeMillis();
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
@@ -943,6 +946,9 @@
}
private final class StatsPullAtomCallbackImpl implements StatsPullAtomCallback {
+
+ private static final int MEGABYTE_IN_BYTES = 1000000;
+
@Override
public int onPullAtom(int atomTag, @NonNull List<StatsEvent> data) {
if (!isAppHibernationEnabled()
@@ -969,12 +975,21 @@
break;
case FrameworkStatsLog.GLOBAL_HIBERNATED_APPS:
int hibernatedAppCount = 0;
+ long storage_saved_byte = 0;
synchronized (mLock) {
for (GlobalLevelState state : mGlobalHibernationStates.values()) {
- if (state.hibernated) hibernatedAppCount++;
+ if (state.hibernated) {
+ hibernatedAppCount++;
+ storage_saved_byte += state.savedByte;
+ }
}
}
- data.add(FrameworkStatsLog.buildStatsEvent(atomTag, hibernatedAppCount));
+ data.add(
+ FrameworkStatsLog.buildStatsEvent(
+ atomTag,
+ hibernatedAppCount,
+ storage_saved_byte / MEGABYTE_IN_BYTES)
+ );
break;
default:
return StatsManager.PULL_SKIP;
diff --git a/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java b/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
index 79e995b..018d602 100644
--- a/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
+++ b/services/core/java/com/android/server/apphibernation/GlobalLevelHibernationProto.java
@@ -41,6 +41,7 @@
GlobalLevelState state = data.get(i);
stream.write(GlobalLevelHibernationStateProto.PACKAGE_NAME, state.packageName);
stream.write(GlobalLevelHibernationStateProto.HIBERNATED, state.hibernated);
+ stream.write(GlobalLevelHibernationStateProto.SAVED_BYTE, state.savedByte);
stream.end(token);
}
}
@@ -66,6 +67,10 @@
state.hibernated =
stream.readBoolean(GlobalLevelHibernationStateProto.HIBERNATED);
break;
+ case (int) GlobalLevelHibernationStateProto.SAVED_BYTE:
+ state.savedByte =
+ stream.readLong(GlobalLevelHibernationStateProto.SAVED_BYTE);
+ break;
default:
Slog.w(TAG, "Undefined field in proto: " + stream.getFieldNumber());
}
diff --git a/services/core/java/com/android/server/apphibernation/GlobalLevelState.java b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
index f4433a7..104ecbc 100644
--- a/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
+++ b/services/core/java/com/android/server/apphibernation/GlobalLevelState.java
@@ -29,6 +29,8 @@
public String packageName;
public boolean hibernated;
+ // The number of saved bytes from the current hibernation. It will be 0 if not in hibernation.
+ public long savedByte;
@CurrentTimeMillisLong
public long lastUnhibernatedMs;
@@ -37,6 +39,7 @@
return "GlobalLevelState{"
+ "packageName='" + packageName + '\''
+ ", hibernated=" + hibernated + '\''
+ + ", savedByte=" + savedByte + '\''
+ ", lastUnhibernated=" + DATE_FORMAT.format(lastUnhibernatedMs)
+ '}';
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 104bc9b..55ed0d6 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -19,11 +19,13 @@
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
import static android.app.AppOpsManager.CALL_BACK_ON_SWITCHED_OP;
import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
import static android.app.AppOpsManager.FILTER_BY_UID;
+import static android.app.AppOpsManager.HISTORY_FLAG_GET_ATTRIBUTION_CHAINS;
import static android.app.AppOpsManager.HistoricalOpsRequestFilter;
import static android.app.AppOpsManager.KEY_BG_STATE_SETTLE_TIME;
import static android.app.AppOpsManager.KEY_FG_SERVICE_STATE_SETTLE_TIME;
@@ -130,6 +132,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManagerInternal;
+import android.permission.PermissionManager;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -200,8 +203,8 @@
import java.util.Map;
import java.util.Objects;
import java.util.Scanner;
+import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
public class AppOpsService extends IAppOpsService.Stub {
@@ -848,7 +851,7 @@
return mAttributionFlags;
}
- /** @return attributoin chiang id for the access */
+ /** @return attribution chain id for the access */
public int getAttributionChainId() {
return mAttributionChainId;
}
@@ -912,7 +915,8 @@
proxyAttributionTag, uidState, flags);
mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
- tag, uidState, flags, accessTime);
+ tag, uidState, flags, accessTime, AppOpsManager.ATTRIBUTION_FLAGS_NONE,
+ AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
}
/**
@@ -1053,9 +1057,9 @@
event.numUnfinishedStarts++;
if (isStarted) {
- // TODO: Consider storing the attribution chain flags and id
mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
- parent.packageName, tag, uidState, flags, startTime);
+ parent.packageName, tag, uidState, flags, startTime, attributionFlags,
+ attributionChainId);
}
}
@@ -1112,7 +1116,8 @@
mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
parent.packageName, tag, event.getUidState(),
- event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration());
+ event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration(),
+ event.getAttributionFlags(), event.getAttributionChainId());
if (!isPausing) {
mInProgressStartOpEventPool.release(event);
@@ -1215,7 +1220,8 @@
event.mStartElapsedTime = SystemClock.elapsedRealtime();
event.mStartTime = startTime;
mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
- parent.packageName, tag, event.mUidState, event.mFlags, startTime);
+ parent.packageName, tag, event.mUidState, event.mFlags, startTime,
+ event.getAttributionFlags(), event.getAttributionChainId());
if (shouldSendActive) {
scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, parent.packageName,
tag, true, event.getAttributionFlags(), event.getAttributionChainId());
@@ -2354,10 +2360,21 @@
final String[] opNamesArray = (opNames != null)
? opNames.toArray(new String[opNames.size()]) : null;
+ Set<String> attributionChainExemptPackages = null;
+ if ((dataType & HISTORY_FLAG_GET_ATTRIBUTION_CHAINS) != 0) {
+ attributionChainExemptPackages =
+ PermissionManager.getIndicatorExemptedPackages(mContext);
+ }
+
+ final String[] chainExemptPkgArray = attributionChainExemptPackages != null
+ ? attributionChainExemptPackages.toArray(
+ new String[attributionChainExemptPackages.size()]) : null;
+
// Must not hold the appops lock
mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOps,
mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType,
- filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
+ filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray,
+ callback).recycleOnUse());
}
@Override
@@ -2374,10 +2391,21 @@
final String[] opNamesArray = (opNames != null)
? opNames.toArray(new String[opNames.size()]) : null;
+ Set<String> attributionChainExemptPackages = null;
+ if ((dataType & HISTORY_FLAG_GET_ATTRIBUTION_CHAINS) != 0) {
+ attributionChainExemptPackages =
+ PermissionManager.getIndicatorExemptedPackages(mContext);
+ }
+
+ final String[] chainExemptPkgArray = attributionChainExemptPackages != null
+ ? attributionChainExemptPackages.toArray(
+ new String[attributionChainExemptPackages.size()]) : null;
+
// Must not hold the appops lock
mHandler.post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOpsFromDiskRaw,
mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType,
- filter, beginTimeMillis, endTimeMillis, flags, callback).recycleOnUse());
+ filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray,
+ callback).recycleOnUse());
}
@Override
@@ -3835,7 +3863,8 @@
final boolean isSelfBlame = Binder.getCallingUid() == proxiedUid;
final boolean isProxyTrusted = mContext.checkPermission(
Manifest.permission.UPDATE_APP_OPS_STATS, -1, proxyUid)
- == PackageManager.PERMISSION_GRANTED || isSelfBlame;
+ == PackageManager.PERMISSION_GRANTED || isSelfBlame
+ || attributionChainId != ATTRIBUTION_CHAIN_ID_NONE;
String resolvedProxiedPackageName = AppOpsManager.resolvePackageName(proxiedUid,
proxiedPackageName);
@@ -3998,6 +4027,12 @@
@Override
public void finishOperation(IBinder clientId, int code, int uid, String packageName,
String attributionTag) {
+ mCheckOpsDelegateDispatcher.finishOperation(clientId, code, uid, packageName,
+ attributionTag);
+ }
+
+ private void finishOperationImpl(IBinder clientId, int code, int uid, String packageName,
+ String attributionTag) {
verifyIncomingUid(uid);
verifyIncomingOp(code);
verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
@@ -5171,8 +5206,6 @@
}
static class Shell extends ShellCommand {
- static final AtomicInteger sAttributionChainIds = new AtomicInteger(0);
-
final IAppOpsService mInterface;
final AppOpsService mInternal;
@@ -5642,8 +5675,7 @@
shell.mInterface.startOperation(shell.mToken, shell.op, shell.packageUid,
shell.packageName, shell.attributionTag, true, true,
"appops start shell command", true,
- AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR,
- shell.sAttributionChainIds.incrementAndGet());
+ AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR, ATTRIBUTION_CHAIN_ID_NONE);
} else {
return -1;
}
@@ -7504,6 +7536,29 @@
attributionChainId, AppOpsService.this::startProxyOperationImpl);
}
+ public void finishOperation(IBinder clientId, int code, int uid, String packageName,
+ String attributionTag) {
+ if (mPolicy != null) {
+ if (mCheckOpsDelegate != null) {
+ mPolicy.finishOperation(clientId, code, uid, packageName, attributionTag,
+ this::finishDelegateOperationImpl);
+ } else {
+ mPolicy.finishOperation(clientId, code, uid, packageName, attributionTag,
+ AppOpsService.this::finishOperationImpl);
+ }
+ } else if (mCheckOpsDelegate != null) {
+ finishDelegateOperationImpl(clientId, code, uid, packageName, attributionTag);
+ } else {
+ finishOperationImpl(clientId, code, uid, packageName, attributionTag);
+ }
+ }
+
+ private void finishDelegateOperationImpl(IBinder clientId, int code, int uid,
+ String packageName, String attributionTag) {
+ mCheckOpsDelegate.finishOperation(clientId, code, uid, packageName, attributionTag,
+ AppOpsService.this::finishOperationImpl);
+ }
+
public void finishProxyOperation(int code,
@NonNull AttributionSource attributionSource, boolean skipProxyOperation) {
if (mPolicy != null) {
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java
index 10cfddf..0439660 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteRegistry.java
@@ -16,6 +16,9 @@
package com.android.server.appop;
+import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_RECEIVER;
import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
@@ -26,6 +29,7 @@
import static android.app.AppOpsManager.OP_FLAGS_ALL;
import static android.app.AppOpsManager.OP_FLAG_SELF;
import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
+import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXY;
import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA;
import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE;
@@ -57,7 +61,9 @@
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.time.Duration;
@@ -68,6 +74,8 @@
import java.util.Collections;
import java.util.Date;
import java.util.List;
+import java.util.Objects;
+import java.util.Set;
/**
* This class manages information about recent accesses to ops for permission usage timeline.
@@ -137,6 +145,7 @@
private static final String TAG_HISTORY = "h";
private static final String ATTR_VERSION = "v";
+ private static final String ATTR_LARGEST_CHAIN_ID = "lc";
private static final int CURRENT_VERSION = 1;
private static final String TAG_UID = "u";
@@ -156,8 +165,11 @@
private static final String ATTR_NOTE_DURATION = "nd";
private static final String ATTR_UID_STATE = "us";
private static final String ATTR_FLAGS = "f";
+ private static final String ATTR_ATTRIBUTION_FLAGS = "af";
+ private static final String ATTR_CHAIN_ID = "ci";
- private static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED;
+ private static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED
+ | OP_FLAG_TRUSTED_PROXY;
// Lock for read/write access to on disk state
private final Object mOnDiskLock = new Object();
@@ -178,6 +190,16 @@
DiscreteRegistry(Object inMemoryLock) {
mInMemoryLock = inMemoryLock;
+ synchronized (mOnDiskLock) {
+ mDiscreteAccessDir = new File(
+ new File(Environment.getDataSystemDirectory(), "appops"),
+ "discrete");
+ createDiscreteAccessDirLocked();
+ int largestChainId = readLargestChainIdFromDiskLocked();
+ synchronized (mInMemoryLock) {
+ mDiscreteOps = new DiscreteOps(largestChainId);
+ }
+ }
}
void systemReady() {
@@ -186,15 +208,6 @@
setDiscreteHistoryParameters(p);
});
setDiscreteHistoryParameters(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_PRIVACY));
- synchronized (mOnDiskLock) {
- synchronized (mInMemoryLock) {
- mDiscreteAccessDir = new File(
- new File(Environment.getDataSystemDirectory(), "appops"),
- "discrete");
- createDiscreteAccessDirLocked();
- mDiscreteOps = new DiscreteOps();
- }
- }
}
private void setDiscreteHistoryParameters(DeviceConfig.Properties p) {
@@ -227,13 +240,14 @@
void recordDiscreteAccess(int uid, String packageName, int op, @Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState, long accessTime,
- long accessDuration) {
+ long accessDuration, @AppOpsManager.AttributionFlags int attributionFlags,
+ int attributionChainId) {
if (!isDiscreteOp(op, flags)) {
return;
}
synchronized (mInMemoryLock) {
mDiscreteOps.addDiscreteAccess(op, uid, packageName, attributionTag, flags, uidState,
- accessTime, accessDuration);
+ accessTime, accessDuration, attributionFlags, attributionChainId);
}
}
@@ -246,7 +260,7 @@
DiscreteOps discreteOps;
synchronized (mInMemoryLock) {
discreteOps = mDiscreteOps;
- mDiscreteOps = new DiscreteOps();
+ mDiscreteOps = new DiscreteOps(discreteOps.mChainIdOffset);
mCachedOps = null;
}
deleteOldDiscreteHistoryFilesLocked();
@@ -260,16 +274,109 @@
long beginTimeMillis, long endTimeMillis,
@AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
@Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
- @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+ Set<String> attributionExemptPkgs) {
+ boolean assembleChains = attributionExemptPkgs != null;
DiscreteOps discreteOps = getAllDiscreteOps();
+ ArrayMap<Integer, AttributionChain> attributionChains = new ArrayMap<>();
+ if (assembleChains) {
+ attributionChains = createAttributionChains(discreteOps, attributionExemptPkgs);
+ }
beginTimeMillis = max(beginTimeMillis, Instant.now().minus(sDiscreteHistoryCutoff,
ChronoUnit.MILLIS).toEpochMilli());
discreteOps.filter(beginTimeMillis, endTimeMillis, filter, uidFilter, packageNameFilter,
- opNamesFilter, attributionTagFilter, flagsFilter);
- discreteOps.applyToHistoricalOps(result);
+ opNamesFilter, attributionTagFilter, flagsFilter, attributionChains);
+ discreteOps.applyToHistoricalOps(result, attributionChains);
return;
}
+ private int readLargestChainIdFromDiskLocked() {
+ final File[] files = mDiscreteAccessDir.listFiles();
+ if (files != null && files.length > 0) {
+ File latestFile = null;
+ long latestFileTimestamp = 0;
+ for (File f : files) {
+ final String fileName = f.getName();
+ if (!fileName.endsWith(DISCRETE_HISTORY_FILE_SUFFIX)) {
+ continue;
+ }
+ long timestamp = Long.valueOf(fileName.substring(0,
+ fileName.length() - DISCRETE_HISTORY_FILE_SUFFIX.length()));
+ if (latestFileTimestamp < timestamp) {
+ latestFile = f;
+ latestFileTimestamp = timestamp;
+ }
+ }
+ if (latestFile == null) {
+ return 0;
+ }
+ FileInputStream stream;
+ try {
+ stream = new FileInputStream(latestFile);
+ } catch (FileNotFoundException e) {
+ return 0;
+ }
+ try {
+ TypedXmlPullParser parser = Xml.resolvePullParser(stream);
+ XmlUtils.beginDocument(parser, TAG_HISTORY);
+
+ final int largestChainId = parser.getAttributeInt(null, ATTR_LARGEST_CHAIN_ID, 0);
+ return largestChainId;
+ } catch (Throwable t) {
+ return 0;
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ } else {
+ return 0;
+ }
+ }
+
+ private ArrayMap<Integer, AttributionChain> createAttributionChains(
+ DiscreteOps discreteOps, Set<String> attributionExemptPkgs) {
+ ArrayMap<Integer, AttributionChain> chains = new ArrayMap<>();
+ int nUids = discreteOps.mUids.size();
+ for (int uidNum = 0; uidNum < nUids; uidNum++) {
+ ArrayMap<String, DiscretePackageOps> pkgs = discreteOps.mUids.valueAt(uidNum).mPackages;
+ int uid = discreteOps.mUids.keyAt(uidNum);
+ int nPackages = pkgs.size();
+ for (int pkgNum = 0; pkgNum < nPackages; pkgNum++) {
+ ArrayMap<Integer, DiscreteOp> ops = pkgs.valueAt(pkgNum).mPackageOps;
+ String pkg = pkgs.keyAt(pkgNum);
+ int nOps = ops.size();
+ for (int opNum = 0; opNum < nOps; opNum++) {
+ ArrayMap<String, List<DiscreteOpEvent>> attrOps =
+ ops.valueAt(opNum).mAttributedOps;
+ int op = ops.keyAt(opNum);
+ int nAttrOps = attrOps.size();
+ for (int attrOpNum = 0; attrOpNum < nAttrOps; attrOpNum++) {
+ List<DiscreteOpEvent> opEvents = attrOps.valueAt(attrOpNum);
+ String attributionTag = attrOps.keyAt(attrOpNum);
+ int nOpEvents = opEvents.size();
+ for (int opEventNum = 0; opEventNum < nOpEvents; opEventNum++) {
+ DiscreteOpEvent event = opEvents.get(opEventNum);
+ if (event == null
+ || event.mAttributionChainId == ATTRIBUTION_CHAIN_ID_NONE) {
+ continue;
+ }
+
+ if (!chains.containsKey(event.mAttributionChainId)) {
+ chains.put(event.mAttributionChainId,
+ new AttributionChain(attributionExemptPkgs));
+ }
+ chains.get(event.mAttributionChainId)
+ .addEvent(pkg, uid, attributionTag, op, event);
+ }
+ }
+ }
+ }
+ }
+ return chains;
+ }
+
private void readDiscreteOpsFromDisk(DiscreteOps discreteOps) {
synchronized (mOnDiskLock) {
long beginTimeMillis = Instant.now().minus(sDiscreteHistoryCutoff,
@@ -296,7 +403,7 @@
void clearHistory() {
synchronized (mOnDiskLock) {
synchronized (mInMemoryLock) {
- mDiscreteOps = new DiscreteOps();
+ mDiscreteOps = new DiscreteOps(0);
}
clearOnDiskHistoryLocked();
}
@@ -335,7 +442,11 @@
String[] opNamesFilter = dumpOp == OP_NONE ? null
: new String[]{AppOpsManager.opToPublicName(dumpOp)};
discreteOps.filter(0, Instant.now().toEpochMilli(), filter, uidFilter, packageNameFilter,
- opNamesFilter, attributionTagFilter, OP_FLAGS_ALL);
+ opNamesFilter, attributionTagFilter, OP_FLAGS_ALL, new ArrayMap<>());
+ pw.print(prefix);
+ pw.print("Largest chain id: ");
+ pw.print(mDiscreteOps.mLargestChainId);
+ pw.println();
discreteOps.dump(pw, sdf, date, prefix, nDiscreteOps);
}
@@ -346,14 +457,14 @@
}
private DiscreteOps getAllDiscreteOps() {
- DiscreteOps discreteOps = new DiscreteOps();
+ DiscreteOps discreteOps = new DiscreteOps(0);
synchronized (mOnDiskLock) {
synchronized (mInMemoryLock) {
discreteOps.merge(mDiscreteOps);
}
if (mCachedOps == null) {
- mCachedOps = new DiscreteOps();
+ mCachedOps = new DiscreteOps(0);
readDiscreteOpsFromDisk(mCachedOps);
}
discreteOps.merge(mCachedOps);
@@ -361,11 +472,143 @@
}
}
+ /**
+ * Represents a chain of usages, each attributing its usage to the one before it
+ */
+ private static final class AttributionChain {
+ private static final class OpEvent {
+ String mPkgName;
+ int mUid;
+ String mAttributionTag;
+ int mOpCode;
+ DiscreteOpEvent mOpEvent;
+
+ OpEvent(String pkgName, int uid, String attributionTag, int opCode,
+ DiscreteOpEvent event) {
+ mPkgName = pkgName;
+ mUid = uid;
+ mAttributionTag = attributionTag;
+ mOpCode = opCode;
+ mOpEvent = event;
+ }
+
+ public boolean matches(String pkgName, int uid, String attributionTag, int opCode,
+ DiscreteOpEvent event) {
+ return Objects.equals(pkgName, mPkgName) && mUid == uid
+ && Objects.equals(attributionTag, mAttributionTag) && mOpCode == opCode
+ && mOpEvent.mAttributionChainId == event.mAttributionChainId
+ && mOpEvent.mAttributionFlags == event.mAttributionFlags
+ && mOpEvent.mNoteTime == event.mNoteTime;
+ }
+
+ public boolean packageOpEquals(OpEvent other) {
+ return Objects.equals(other.mPkgName, mPkgName) && other.mUid == mUid
+ && Objects.equals(other.mAttributionTag, mAttributionTag)
+ && mOpCode == other.mOpCode;
+ }
+
+ public boolean equalsExceptDuration(OpEvent other) {
+ if (other.mOpEvent.mNoteDuration == mOpEvent.mNoteDuration) {
+ return false;
+ }
+ return packageOpEquals(other) && mOpEvent.equalsExceptDuration(other.mOpEvent);
+ }
+ }
+
+ ArrayList<OpEvent> mChain = new ArrayList<>();
+ Set<String> mExemptPkgs;
+ OpEvent mStartEvent = null;
+ OpEvent mLastVisibleEvent = null;
+
+ AttributionChain(Set<String> exemptPkgs) {
+ mExemptPkgs = exemptPkgs;
+ }
+
+ boolean isComplete() {
+ return !mChain.isEmpty() && getStart() != null && isEnd(mChain.get(mChain.size() - 1));
+ }
+
+ boolean isStart(String pkgName, int uid, String attributionTag, int op,
+ DiscreteOpEvent opEvent) {
+ if (mStartEvent == null || opEvent == null) {
+ return false;
+ }
+ return mStartEvent.matches(pkgName, uid, attributionTag, op, opEvent);
+ }
+
+ private OpEvent getStart() {
+ return mChain.isEmpty() || !isStart(mChain.get(0)) ? null : mChain.get(0);
+ }
+
+ private OpEvent getLastVisible() {
+ // Search all nodes but the first one, which is the start node
+ for (int i = mChain.size() - 1; i > 0; i--) {
+ OpEvent event = mChain.get(i);
+ if (!mExemptPkgs.contains(event.mPkgName)) {
+ return event;
+ }
+ }
+ return null;
+ }
+
+ void addEvent(String pkgName, int uid, String attributionTag, int op,
+ DiscreteOpEvent opEvent) {
+ OpEvent event = new OpEvent(pkgName, uid, attributionTag, op, opEvent);
+
+ // check if we have a matching event, without duration, replacing duration otherwise
+ for (int i = 0; i < mChain.size(); i++) {
+ OpEvent item = mChain.get(i);
+ if (item.equalsExceptDuration(event)) {
+ if (event.mOpEvent.mNoteDuration != -1) {
+ item.mOpEvent = event.mOpEvent;
+ }
+ return;
+ }
+ }
+
+ if (mChain.isEmpty() || isEnd(event)) {
+ mChain.add(event);
+ } else if (isStart(event)) {
+ mChain.add(0, event);
+
+ } else {
+ for (int i = 0; i < mChain.size(); i++) {
+ OpEvent currEvent = mChain.get(i);
+ if ((!isStart(currEvent)
+ && currEvent.mOpEvent.mNoteTime > event.mOpEvent.mNoteTime)
+ || i == mChain.size() - 1 && isEnd(currEvent)) {
+ mChain.add(i, event);
+ break;
+ } else if (i == mChain.size() - 1) {
+ mChain.add(event);
+ break;
+ }
+ }
+ }
+ mStartEvent = isComplete() ? getStart() : null;
+ mLastVisibleEvent = isComplete() ? getLastVisible() : null;
+ }
+
+ private boolean isEnd(OpEvent event) {
+ return event != null
+ && (event.mOpEvent.mAttributionFlags & ATTRIBUTION_FLAG_ACCESSOR) != 0;
+ }
+
+ private boolean isStart(OpEvent event) {
+ return event != null
+ && (event.mOpEvent.mAttributionFlags & ATTRIBUTION_FLAG_RECEIVER) != 0;
+ }
+ }
+
private final class DiscreteOps {
ArrayMap<Integer, DiscreteUidOps> mUids;
+ int mChainIdOffset;
+ int mLargestChainId;
- DiscreteOps() {
+ DiscreteOps(int chainIdOffset) {
mUids = new ArrayMap<>();
+ mChainIdOffset = chainIdOffset;
+ mLargestChainId = chainIdOffset;
}
boolean isEmpty() {
@@ -373,6 +616,7 @@
}
void merge(DiscreteOps other) {
+ mLargestChainId = max(mLargestChainId, other.mLargestChainId);
int nUids = other.mUids.size();
for (int i = 0; i < nUids; i++) {
int uid = other.mUids.keyAt(i);
@@ -383,15 +627,29 @@
void addDiscreteAccess(int op, int uid, @NonNull String packageName,
@Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
- @AppOpsManager.UidState int uidState, long accessTime, long accessDuration) {
+ @AppOpsManager.UidState int uidState, long accessTime, long accessDuration,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
+ int offsetChainId = attributionChainId;
+ if (attributionChainId != ATTRIBUTION_CHAIN_ID_NONE) {
+ offsetChainId = attributionChainId + mChainIdOffset;
+ if (offsetChainId > mLargestChainId) {
+ mLargestChainId = offsetChainId;
+ } else if (offsetChainId < 0) {
+ // handle overflow
+ offsetChainId = 0;
+ mLargestChainId = 0;
+ mChainIdOffset = -1 * attributionChainId;
+ }
+ }
getOrCreateDiscreteUidOps(uid).addDiscreteAccess(op, packageName, attributionTag, flags,
- uidState, accessTime, accessDuration);
+ uidState, accessTime, accessDuration, attributionFlags, offsetChainId);
}
private void filter(long beginTimeMillis, long endTimeMillis,
@AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
@Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
- @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+ ArrayMap<Integer, AttributionChain> attributionChains) {
if ((filter & FILTER_BY_UID) != 0) {
ArrayMap<Integer, DiscreteUidOps> uids = new ArrayMap<>();
uids.put(uidFilter, getOrCreateDiscreteUidOps(uidFilter));
@@ -400,7 +658,8 @@
int nUids = mUids.size();
for (int i = nUids - 1; i >= 0; i--) {
mUids.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, packageNameFilter,
- opNamesFilter, attributionTagFilter, flagsFilter);
+ opNamesFilter, attributionTagFilter, flagsFilter, mUids.keyAt(i),
+ attributionChains);
if (mUids.valueAt(i).isEmpty()) {
mUids.removeAt(i);
}
@@ -423,10 +682,11 @@
}
}
- private void applyToHistoricalOps(AppOpsManager.HistoricalOps result) {
+ private void applyToHistoricalOps(AppOpsManager.HistoricalOps result,
+ ArrayMap<Integer, AttributionChain> attributionChains) {
int nUids = mUids.size();
for (int i = 0; i < nUids; i++) {
- mUids.valueAt(i).applyToHistory(result, mUids.keyAt(i));
+ mUids.valueAt(i).applyToHistory(result, mUids.keyAt(i), attributionChains);
}
}
@@ -436,6 +696,7 @@
out.startDocument(null, true);
out.startTag(null, TAG_HISTORY);
out.attributeInt(null, ATTR_VERSION, CURRENT_VERSION);
+ out.attributeInt(null, ATTR_LARGEST_CHAIN_ID, mLargestChainId);
int nUids = mUids.size();
for (int i = 0; i < nUids; i++) {
@@ -470,8 +731,13 @@
}
private void readFromFile(File f, long beginTimeMillis) {
+ FileInputStream stream;
try {
- FileInputStream stream = new FileInputStream(f);
+ stream = new FileInputStream(f);
+ } catch (FileNotFoundException e) {
+ return;
+ }
+ try {
TypedXmlPullParser parser = Xml.resolvePullParser(stream);
XmlUtils.beginDocument(parser, TAG_HISTORY);
@@ -481,7 +747,6 @@
if (version != CURRENT_VERSION) {
throw new IllegalStateException("Dropping unsupported discrete history " + f);
}
-
int depth = parser.getDepth();
while (XmlUtils.nextElementWithin(parser, depth)) {
if (TAG_UID.equals(parser.getName())) {
@@ -492,8 +757,12 @@
} catch (Throwable t) {
Slog.e(TAG, "Failed to read file " + f.getName() + " " + t.getMessage() + " "
+ Arrays.toString(t.getStackTrace()));
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
}
-
}
}
@@ -584,7 +853,8 @@
private void filter(long beginTimeMillis, long endTimeMillis,
@AppOpsManager.HistoricalOpsRequestFilter int filter,
@Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
- @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+ int currentUid, ArrayMap<Integer, AttributionChain> attributionChains) {
if ((filter & FILTER_BY_PACKAGE_NAME) != 0) {
ArrayMap<String, DiscretePackageOps> packages = new ArrayMap<>();
packages.put(packageNameFilter, getOrCreateDiscretePackageOps(packageNameFilter));
@@ -593,7 +863,8 @@
int nPackages = mPackages.size();
for (int i = nPackages - 1; i >= 0; i--) {
mPackages.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, opNamesFilter,
- attributionTagFilter, flagsFilter);
+ attributionTagFilter, flagsFilter, currentUid, mPackages.keyAt(i),
+ attributionChains);
if (mPackages.valueAt(i).isEmpty()) {
mPackages.removeAt(i);
}
@@ -613,9 +884,10 @@
void addDiscreteAccess(int op, @NonNull String packageName, @Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
- long accessTime, long accessDuration) {
+ long accessTime, long accessDuration,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
getOrCreateDiscretePackageOps(packageName).addDiscreteAccess(op, attributionTag, flags,
- uidState, accessTime, accessDuration);
+ uidState, accessTime, accessDuration, attributionFlags, attributionChainId);
}
private DiscretePackageOps getOrCreateDiscretePackageOps(String packageName) {
@@ -627,10 +899,12 @@
return result;
}
- private void applyToHistory(AppOpsManager.HistoricalOps result, int uid) {
+ private void applyToHistory(AppOpsManager.HistoricalOps result, int uid,
+ @NonNull ArrayMap<Integer, AttributionChain> attributionChains) {
int nPackages = mPackages.size();
for (int i = 0; i < nPackages; i++) {
- mPackages.valueAt(i).applyToHistory(result, uid, mPackages.keyAt(i));
+ mPackages.valueAt(i).applyToHistory(result, uid, mPackages.keyAt(i),
+ attributionChains);
}
}
@@ -680,9 +954,10 @@
void addDiscreteAccess(int op, @Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
- long accessTime, long accessDuration) {
+ long accessTime, long accessDuration,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
getOrCreateDiscreteOp(op).addDiscreteAccess(attributionTag, flags, uidState, accessTime,
- accessDuration);
+ accessDuration, attributionFlags, attributionChainId);
}
void merge(DiscretePackageOps other) {
@@ -697,7 +972,8 @@
private void filter(long beginTimeMillis, long endTimeMillis,
@AppOpsManager.HistoricalOpsRequestFilter int filter,
@Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
- @AppOpsManager.OpFlags int flagsFilter) {
+ @AppOpsManager.OpFlags int flagsFilter, int currentUid, String currentPkgName,
+ ArrayMap<Integer, AttributionChain> attributionChains) {
int nOps = mPackageOps.size();
for (int i = nOps - 1; i >= 0; i--) {
int opId = mPackageOps.keyAt(i);
@@ -707,7 +983,8 @@
continue;
}
mPackageOps.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter,
- attributionTagFilter, flagsFilter);
+ attributionTagFilter, flagsFilter, currentUid, currentPkgName,
+ mPackageOps.keyAt(i), attributionChains);
if (mPackageOps.valueAt(i).isEmpty()) {
mPackageOps.removeAt(i);
}
@@ -731,11 +1008,12 @@
}
private void applyToHistory(AppOpsManager.HistoricalOps result, int uid,
- @NonNull String packageName) {
+ @NonNull String packageName,
+ @NonNull ArrayMap<Integer, AttributionChain> attributionChains) {
int nPackageOps = mPackageOps.size();
for (int i = 0; i < nPackageOps; i++) {
mPackageOps.valueAt(i).applyToHistory(result, uid, packageName,
- mPackageOps.keyAt(i));
+ mPackageOps.keyAt(i), attributionChains);
}
}
@@ -794,7 +1072,9 @@
private void filter(long beginTimeMillis, long endTimeMillis,
@AppOpsManager.HistoricalOpsRequestFilter int filter,
- @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
+ @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+ int currentUid, String currentPkgName, int currentOp,
+ ArrayMap<Integer, AttributionChain> attributionChains) {
if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0) {
ArrayMap<String, List<DiscreteOpEvent>> attributedOps = new ArrayMap<>();
attributedOps.put(attributionTagFilter,
@@ -806,7 +1086,9 @@
for (int i = nTags - 1; i >= 0; i--) {
String tag = mAttributedOps.keyAt(i);
List<DiscreteOpEvent> list = mAttributedOps.valueAt(i);
- list = filterEventsList(list, beginTimeMillis, endTimeMillis, flagsFilter);
+ list = filterEventsList(list, beginTimeMillis, endTimeMillis, flagsFilter,
+ currentUid, currentPkgName, currentOp, mAttributedOps.keyAt(i),
+ attributionChains);
mAttributedOps.put(tag, list);
if (list.size() == 0) {
mAttributedOps.removeAt(i);
@@ -823,37 +1105,39 @@
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));
+ event.mUidState, event.mOpFlag, event.mAttributionFlags,
+ event.mAttributionChainId));
}
}
}
void addDiscreteAccess(@Nullable String attributionTag,
@AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
- long accessTime, long accessDuration) {
+ long accessTime, long accessDuration,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
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;
for (; i > 0; i--) {
DiscreteOpEvent previousOp = attributedOps.get(i - 1);
- if (previousOp.mNoteTime < accessTime) {
+ if (discretizeTimeStamp(previousOp.mNoteTime) < discretizeTimeStamp(accessTime)) {
break;
}
- if (previousOp.mOpFlag == flags && previousOp.mUidState == uidState) {
- if (accessDuration != previousOp.mNoteDuration) {
+ if (previousOp.mOpFlag == flags && previousOp.mUidState == uidState
+ && previousOp.mAttributionFlags == attributionFlags
+ && previousOp.mAttributionChainId == attributionChainId) {
+ if (discretizeDuration(accessDuration) != discretizeDuration(
+ previousOp.mNoteDuration)) {
break;
} else {
return;
}
}
}
- attributedOps.add(i, new DiscreteOpEvent(accessTime, accessDuration, uidState, flags));
+ attributedOps.add(i, new DiscreteOpEvent(accessTime, accessDuration, uidState, flags,
+ attributionFlags, attributionChainId));
}
private List<DiscreteOpEvent> getOrCreateDiscreteOpEventsList(String attributionTag) {
@@ -866,7 +1150,8 @@
}
private void applyToHistory(AppOpsManager.HistoricalOps result, int uid,
- @NonNull String packageName, int op) {
+ @NonNull String packageName, int op,
+ @NonNull ArrayMap<Integer, AttributionChain> attributionChains) {
int nOps = mAttributedOps.size();
for (int i = 0; i < nOps; i++) {
String tag = mAttributedOps.keyAt(i);
@@ -874,8 +1159,21 @@
int nEvents = events.size();
for (int j = 0; j < nEvents; j++) {
DiscreteOpEvent event = events.get(j);
+ AppOpsManager.OpEventProxyInfo proxy = null;
+ if (event.mAttributionChainId != ATTRIBUTION_CHAIN_ID_NONE
+ && attributionChains != null) {
+ AttributionChain chain = attributionChains.get(event.mAttributionChainId);
+ if (chain != null && chain.isComplete()
+ && chain.isStart(packageName, uid, tag, op, event)
+ && chain.mLastVisibleEvent != null) {
+ AttributionChain.OpEvent proxyEvent = chain.mLastVisibleEvent;
+ proxy = new AppOpsManager.OpEventProxyInfo(proxyEvent.mUid,
+ proxyEvent.mPkgName, proxyEvent.mAttributionTag);
+ }
+ }
result.addDiscreteAccess(op, uid, packageName, tag, event.mUidState,
- event.mOpFlag, event.mNoteTime, event.mNoteDuration);
+ event.mOpFlag, discretizeTimeStamp(event.mNoteTime),
+ discretizeDuration(event.mNoteDuration), proxy);
}
}
}
@@ -932,11 +1230,15 @@
-1);
int uidState = parser.getAttributeInt(null, ATTR_UID_STATE);
int opFlags = parser.getAttributeInt(null, ATTR_FLAGS);
+ int attributionFlags = parser.getAttributeInt(null,
+ ATTR_ATTRIBUTION_FLAGS, AppOpsManager.ATTRIBUTION_FLAGS_NONE);
+ int attributionChainId = parser.getAttributeInt(null, ATTR_CHAIN_ID,
+ AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
if (noteTime + noteDuration < beginTimeMillis) {
continue;
}
DiscreteOpEvent event = new DiscreteOpEvent(noteTime, noteDuration,
- uidState, opFlags);
+ uidState, opFlags, attributionFlags, attributionChainId);
events.add(event);
}
}
@@ -952,13 +1254,25 @@
final long mNoteDuration;
final @AppOpsManager.UidState int mUidState;
final @AppOpsManager.OpFlags int mOpFlag;
+ final @AppOpsManager.AttributionFlags int mAttributionFlags;
+ final int mAttributionChainId;
DiscreteOpEvent(long noteTime, long noteDuration, @AppOpsManager.UidState int uidState,
- @AppOpsManager.OpFlags int opFlag) {
+ @AppOpsManager.OpFlags int opFlag,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
mNoteTime = noteTime;
mNoteDuration = noteDuration;
mUidState = uidState;
mOpFlag = opFlag;
+ mAttributionFlags = attributionFlags;
+ mAttributionChainId = attributionChainId;
+ }
+
+ public boolean equalsExceptDuration(DiscreteOpEvent o) {
+ return mNoteTime == o.mNoteTime && mUidState == o.mUidState && mOpFlag == o.mOpFlag
+ && mAttributionFlags == o.mAttributionFlags
+ && mAttributionChainId == o.mAttributionChainId;
+
}
private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf,
@@ -969,13 +1283,19 @@
pw.print("-");
pw.print(flagsToString(mOpFlag));
pw.print("] at ");
- date.setTime(mNoteTime);
+ date.setTime(discretizeTimeStamp(mNoteTime));
pw.print(sdf.format(date));
if (mNoteDuration != -1) {
pw.print(" for ");
- pw.print(mNoteDuration);
+ pw.print(discretizeDuration(mNoteDuration));
pw.print(" milliseconds ");
}
+ if (mAttributionFlags != AppOpsManager.ATTRIBUTION_FLAGS_NONE) {
+ pw.print(" attribution flags=");
+ pw.print(mAttributionFlags);
+ pw.print(" with chainId=");
+ pw.print(mAttributionChainId);
+ }
pw.println();
}
@@ -984,6 +1304,12 @@
if (mNoteDuration != -1) {
out.attributeLong(null, ATTR_NOTE_DURATION, mNoteDuration);
}
+ if (mAttributionFlags != AppOpsManager.ATTRIBUTION_FLAGS_NONE) {
+ out.attributeInt(null, ATTR_ATTRIBUTION_FLAGS, mAttributionFlags);
+ }
+ if (mAttributionChainId != AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE) {
+ out.attributeInt(null, ATTR_CHAIN_ID, mAttributionChainId);
+ }
out.attributeInt(null, ATTR_UID_STATE, mUidState);
out.attributeInt(null, ATTR_FLAGS, mOpFlag);
}
@@ -1031,11 +1357,20 @@
}
private static List<DiscreteOpEvent> filterEventsList(List<DiscreteOpEvent> list,
- long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flagsFilter) {
+ long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flagsFilter,
+ int currentUid, String currentPackageName, int currentOp, String currentAttrTag,
+ ArrayMap<Integer, AttributionChain> attributionChains) {
int n = list.size();
List<DiscreteOpEvent> result = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
DiscreteOpEvent event = list.get(i);
+ AttributionChain chain = attributionChains.get(event.mAttributionChainId);
+ // If we have an attribution chain, and this event isn't the beginning node, remove it
+ if (chain != null && !chain.isStart(currentPackageName, currentUid, currentAttrTag,
+ currentOp, event) && chain.isComplete()
+ && event.mAttributionChainId != ATTRIBUTION_CHAIN_ID_NONE) {
+ continue;
+ }
if ((event.mOpFlag & flagsFilter) != 0
&& event.mNoteTime + event.mNoteDuration > beginTimeMillis
&& event.mNoteTime < endTimeMillis) {
@@ -1055,6 +1390,16 @@
return true;
}
+ private static long discretizeTimeStamp(long timeStamp) {
+ return timeStamp / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
+
+ }
+
+ private static long discretizeDuration(long duration) {
+ return duration == -1 ? -1 : (duration + sDiscreteHistoryQuantization - 1)
+ / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
+ }
+
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 8b72be7..2c68aaf 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -209,6 +209,7 @@
mMode = other.mMode;
mBaseSnapshotInterval = other.mBaseSnapshotInterval;
mIntervalCompressionMultiplier = other.mIntervalCompressionMultiplier;
+ mDiscreteRegistry = other.mDiscreteRegistry;
}
void systemReady(@NonNull ContentResolver resolver) {
@@ -369,7 +370,7 @@
@Nullable String attributionTag, @Nullable String[] opNames,
@OpHistoryFlags int historyFlags, @HistoricalOpsRequestFilter int filter,
long beginTimeMillis, long endTimeMillis, @OpFlags int flags,
- @NonNull RemoteCallback callback) {
+ String[] attributionExemptedPackages, @NonNull RemoteCallback callback) {
if (!isApiEnabled()) {
callback.sendResult(new Bundle());
return;
@@ -395,7 +396,7 @@
if ((historyFlags & HISTORY_FLAG_DISCRETE) != 0) {
mDiscreteRegistry.addFilteredDiscreteOpsToHistoricalOps(result, beginTimeMillis,
endTimeMillis, filter, uid, packageName, opNames, attributionTag,
- flags);
+ flags, new ArraySet<>(attributionExemptedPackages));
}
final Bundle payload = new Bundle();
@@ -406,7 +407,8 @@
void getHistoricalOps(int uid, @Nullable String packageName, @Nullable String attributionTag,
@Nullable String[] opNames, @OpHistoryFlags int historyFlags,
@HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis,
- @OpFlags int flags, @NonNull RemoteCallback callback) {
+ @OpFlags int flags, @Nullable String[] attributionExemptPkgs,
+ @NonNull RemoteCallback callback) {
if (!isApiEnabled()) {
callback.sendResult(new Bundle());
return;
@@ -428,7 +430,8 @@
if ((historyFlags & HISTORY_FLAG_DISCRETE) != 0) {
mDiscreteRegistry.addFilteredDiscreteOpsToHistoricalOps(result, beginTimeMillis,
- endTimeMillis, filter, uid, packageName, opNames, attributionTag, flags);
+ endTimeMillis, filter, uid, packageName, opNames, attributionTag, flags,
+ new ArraySet<>(attributionExemptPkgs));
}
if ((historyFlags & HISTORY_FLAG_AGGREGATE) != 0) {
@@ -487,7 +490,8 @@
void incrementOpAccessedCount(int op, int uid, @NonNull String packageName,
@Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
- long accessTime) {
+ long accessTime, @AppOpsManager.AttributionFlags int attributionFlags,
+ int attributionChainId) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
@@ -499,7 +503,7 @@
attributionTag, uidState, flags, 1);
mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag,
- flags, uidState, accessTime, -1);
+ flags, uidState, accessTime, -1, attributionFlags, attributionChainId);
}
}
}
@@ -521,7 +525,8 @@
void increaseOpAccessDuration(int op, int uid, @NonNull String packageName,
@Nullable String attributionTag, @UidState int uidState, @OpFlags int flags,
- long eventStartTime, long increment) {
+ long eventStartTime, long increment,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) {
synchronized (mInMemoryLock) {
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
if (!isPersistenceInitializedMLocked()) {
@@ -532,7 +537,8 @@
System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName,
attributionTag, uidState, flags, increment);
mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag,
- flags, uidState, eventStartTime, increment);
+ flags, uidState, eventStartTime, increment, attributionFlags,
+ attributionChainId);
}
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index fbf2492..28e23e3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -192,25 +192,27 @@
mPowerManager.userActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
}
- protected @NonNull VibrationEffect getSuccessVibrationEffect() {
+ protected @Nullable VibrationEffect getSuccessVibrationEffect() {
return mSuccessVibrationEffect;
}
- protected @NonNull VibrationEffect getErrorVibrationEffect() {
+ protected @Nullable VibrationEffect getErrorVibrationEffect() {
return mErrorVibrationEffect;
}
protected final void vibrateSuccess() {
Vibrator vibrator = getContext().getSystemService(Vibrator.class);
- if (vibrator != null) {
- vibrator.vibrate(getSuccessVibrationEffect(), VIBRATION_SONFICATION_ATTRIBUTES);
+ VibrationEffect effect = getSuccessVibrationEffect();
+ if (vibrator != null && effect != null) {
+ vibrator.vibrate(effect, VIBRATION_SONFICATION_ATTRIBUTES);
}
}
protected final void vibrateError() {
Vibrator vibrator = getContext().getSystemService(Vibrator.class);
- if (vibrator != null) {
- vibrator.vibrate(getErrorVibrationEffect(), VIBRATION_SONFICATION_ATTRIBUTES);
+ VibrationEffect effect = getErrorVibrationEffect();
+ if (vibrator != null && effect != null) {
+ vibrator.vibrate(effect, VIBRATION_SONFICATION_ATTRIBUTES);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 8668828..6b9ff6f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -344,21 +344,29 @@
}
@Override
- protected @NonNull VibrationEffect getSuccessVibrationEffect() {
+ protected @Nullable VibrationEffect getSuccessVibrationEffect() {
if (!mCustomHaptics) {
return super.getSuccessVibrationEffect();
}
+ if (Settings.Global.getInt(mContentResolver, "fp_success_enabled", 1) == 0) {
+ return null;
+ }
+
return getVibration(Settings.Global.getString(mContentResolver,
"fp_success_type"), super.getSuccessVibrationEffect());
}
@Override
- protected @NonNull VibrationEffect getErrorVibrationEffect() {
+ protected @Nullable VibrationEffect getErrorVibrationEffect() {
if (!mCustomHaptics) {
return super.getErrorVibrationEffect();
}
+ if (Settings.Global.getInt(mContentResolver, "fp_error_enabled", 1) == 0) {
+ return null;
+ }
+
return getVibration(Settings.Global.getString(mContentResolver,
"fp_error_type"), super.getErrorVibrationEffect());
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
new file mode 100644
index 0000000..1f1309d
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+
+import com.android.internal.R;
+
+/**
+ * Biometric notification helper class.
+ */
+public class BiometricNotificationUtils {
+
+ private static final String RE_ENROLL_NOTIFICATION_TAG = "FaceService";
+ private static final String BAD_CALIBRATION_NOTIFICATION_TAG = "FingerprintService";
+ private static final int NOTIFICATION_ID = 1;
+
+ /**
+ * Shows a face re-enrollment notification.
+ */
+ public static void showReEnrollmentNotification(@NonNull Context context) {
+ final NotificationManager notificationManager =
+ context.getSystemService(NotificationManager.class);
+
+ final String name =
+ context.getString(R.string.face_recalibrate_notification_name);
+ final String title =
+ context.getString(R.string.face_recalibrate_notification_title);
+ final String content =
+ context.getString(R.string.face_recalibrate_notification_content);
+
+ final Intent intent = new Intent("android.settings.FACE_SETTINGS");
+ intent.setPackage("com.android.settings");
+
+ final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
+ 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
+ null /* options */, UserHandle.CURRENT);
+
+ final String channelName = "FaceEnrollNotificationChannel";
+
+ showNotificationHelper(context, name, title, content, pendingIntent, channelName,
+ RE_ENROLL_NOTIFICATION_TAG);
+ }
+
+ /**
+ * Shows a fingerprint bad calibration notification.
+ */
+ public static void showBadCalibrationNotification(@NonNull Context context) {
+ final NotificationManager notificationManager =
+ context.getSystemService(NotificationManager.class);
+
+ final String name =
+ context.getString(R.string.fingerprint_recalibrate_notification_name);
+ final String title =
+ context.getString(R.string.fingerprint_recalibrate_notification_title);
+ final String content =
+ context.getString(R.string.fingerprint_recalibrate_notification_content);
+
+ final Intent intent = new Intent("android.settings.FINGERPRINT_SETTINGS");
+ intent.setPackage("com.android.settings");
+
+ final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
+ 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
+ null /* options */, UserHandle.CURRENT);
+
+ final String channelName = "FingerprintBadCalibrationNotificationChannel";
+
+ showNotificationHelper(context, name, title, content, pendingIntent, channelName,
+ BAD_CALIBRATION_NOTIFICATION_TAG);
+ }
+
+ private static void showNotificationHelper(Context context, String name, String title,
+ String content, PendingIntent pendingIntent, String channelName,
+ String notificationTag) {
+ final NotificationManager notificationManager =
+ context.getSystemService(NotificationManager.class);
+ final NotificationChannel channel = new NotificationChannel(channelName, name,
+ NotificationManager.IMPORTANCE_HIGH);
+ final Notification notification = new Notification.Builder(context, channelName)
+ .setSmallIcon(R.drawable.ic_lock)
+ .setContentTitle(title)
+ .setContentText(content)
+ .setSubText(name)
+ .setOnlyAlertOnce(true)
+ .setLocalOnly(true)
+ .setAutoCancel(true)
+ .setCategory(Notification.CATEGORY_SYSTEM)
+ .setContentIntent(pendingIntent)
+ .setVisibility(Notification.VISIBILITY_SECRET)
+ .build();
+
+ notificationManager.createNotificationChannel(channel);
+ notificationManager.notifyAsUser(notificationTag, NOTIFICATION_ID, notification,
+ UserHandle.CURRENT);
+ }
+
+ /**
+ * Cancels a face re-enrollment notification
+ */
+ public static void cancelReEnrollNotification(@NonNull Context context) {
+ final NotificationManager notificationManager =
+ context.getSystemService(NotificationManager.class);
+ notificationManager.cancelAsUser(RE_ENROLL_NOTIFICATION_TAG, NOTIFICATION_ID,
+ UserHandle.CURRENT);
+ }
+
+ /**
+ * Cancels a fingerprint bad calibration notification
+ */
+ public static void cancelBadCalibrationNotification(@NonNull Context context) {
+ final NotificationManager notificationManager =
+ context.getSystemService(NotificationManager.class);
+ notificationManager.cancelAsUser(BAD_CALIBRATION_NOTIFICATION_TAG, NOTIFICATION_ID,
+ UserHandle.CURRENT);
+ }
+
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ReEnrollNotificationUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/ReEnrollNotificationUtils.java
deleted file mode 100644
index f35a520..0000000
--- a/services/core/java/com/android/server/biometrics/sensors/face/ReEnrollNotificationUtils.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.biometrics.sensors.face;
-
-import android.annotation.NonNull;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.os.UserHandle;
-
-import com.android.internal.R;
-
-public class ReEnrollNotificationUtils {
-
- private static final String NOTIFICATION_TAG = "FaceService";
- private static final int NOTIFICATION_ID = 1;
-
- public static void showReEnrollmentNotification(@NonNull Context context) {
- final NotificationManager notificationManager =
- context.getSystemService(NotificationManager.class);
-
- final String name =
- context.getString(R.string.face_recalibrate_notification_name);
- final String title =
- context.getString(R.string.face_recalibrate_notification_title);
- final String content =
- context.getString(R.string.face_recalibrate_notification_content);
-
- final Intent intent = new Intent("android.settings.FACE_SETTINGS");
- intent.setPackage("com.android.settings");
-
- final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
- 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
- null /* options */, UserHandle.CURRENT);
-
- final String channelName = "FaceEnrollNotificationChannel";
-
- final NotificationChannel channel = new NotificationChannel(channelName, name,
- NotificationManager.IMPORTANCE_HIGH);
- final Notification notification = new Notification.Builder(context, channelName)
- .setSmallIcon(R.drawable.ic_lock)
- .setContentTitle(title)
- .setContentText(content)
- .setSubText(name)
- .setOnlyAlertOnce(true)
- .setLocalOnly(true)
- .setAutoCancel(true)
- .setCategory(Notification.CATEGORY_SYSTEM)
- .setContentIntent(pendingIntent)
- .setVisibility(Notification.VISIBILITY_SECRET)
- .build();
-
- notificationManager.createNotificationChannel(channel);
- notificationManager.notifyAsUser(NOTIFICATION_TAG,
- NOTIFICATION_ID, notification,
- UserHandle.CURRENT);
- }
-
- public static void cancelNotification(@NonNull Context context) {
- final NotificationManager notificationManager =
- context.getSystemService(NotificationManager.class);
- notificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID, UserHandle.CURRENT);
- }
-
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 2adf5f9..98f9fe1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -40,11 +40,11 @@
import com.android.internal.R;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
import com.android.server.biometrics.sensors.LockoutTracker;
-import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils;
import com.android.server.biometrics.sensors.face.UsageStats;
import java.util.ArrayList;
@@ -182,7 +182,7 @@
}
break;
case BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL:
- ReEnrollNotificationUtils.showReEnrollmentNotification(getContext());
+ BiometricNotificationUtils.showReEnrollmentNotification(getContext());
break;
default:
break;
@@ -264,7 +264,7 @@
}
@Override
- protected @NonNull VibrationEffect getSuccessVibrationEffect() {
+ protected @Nullable VibrationEffect getSuccessVibrationEffect() {
if (!mCustomHaptics) {
return super.getSuccessVibrationEffect();
}
@@ -274,7 +274,7 @@
}
@Override
- protected @NonNull VibrationEffect getErrorVibrationEffect() {
+ protected @Nullable VibrationEffect getErrorVibrationEffect() {
if (!mCustomHaptics) {
return super.getErrorVibrationEffect();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index 0ba731e..cb966e7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -59,11 +59,13 @@
@Override
protected void stopHalOperation() {
- try {
- mCancellationSignal.cancel();
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- mCallback.onClientFinished(this, false /* success */);
+ if (mCancellationSignal != null) {
+ try {
+ mCancellationSignal.cancel();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ mCallback.onClientFinished(this, false /* success */);
+ }
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index ff68aa8..0400e22 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -37,11 +37,11 @@
import com.android.internal.R;
import com.android.server.biometrics.HardwareAuthTokenUtils;
import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnrollClient;
import com.android.server.biometrics.sensors.face.FaceUtils;
-import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils;
import java.io.IOException;
import java.util.ArrayList;
@@ -92,7 +92,7 @@
public void start(@NonNull Callback callback) {
super.start(callback);
- ReEnrollNotificationUtils.cancelNotification(getContext());
+ BiometricNotificationUtils.cancelReEnrollNotification(getContext());
}
@NonNull
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index a5bb0f4..26c5bca 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -57,6 +57,7 @@
import com.android.server.biometrics.sensors.AcquisitionClient;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnumerateConsumer;
@@ -68,7 +69,6 @@
import com.android.server.biometrics.sensors.RemovalConsumer;
import com.android.server.biometrics.sensors.face.FaceUtils;
import com.android.server.biometrics.sensors.face.LockoutHalImpl;
-import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils;
import com.android.server.biometrics.sensors.face.ServiceProvider;
import com.android.server.biometrics.sensors.face.UsageStats;
@@ -574,7 +574,7 @@
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
- ReEnrollNotificationUtils.cancelNotification(mContext);
+ BiometricNotificationUtils.cancelReEnrollNotification(mContext);
final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
@@ -866,7 +866,7 @@
private void scheduleUpdateActiveUserWithoutHandler(int targetUserId) {
final boolean hasEnrolled = !getEnrolledFaces(mSensorId, targetUserId).isEmpty();
final FaceUpdateActiveUserClient client = new FaceUpdateActiveUserClient(mContext,
- mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId, mCurrentUserId,
+ mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId,
hasEnrolled, mAuthenticatorIds);
mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index 01dd18f..38e6f08 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -35,9 +35,9 @@
import com.android.internal.R;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutTracker;
-import com.android.server.biometrics.sensors.face.ReEnrollNotificationUtils;
import com.android.server.biometrics.sensors.face.UsageStats;
import java.util.ArrayList;
@@ -195,7 +195,7 @@
mLastAcquire = acquireInfo;
if (acquireInfo == FaceManager.FACE_ACQUIRED_RECALIBRATE) {
- ReEnrollNotificationUtils.showReEnrollmentNotification(getContext());
+ BiometricNotificationUtils.showReEnrollmentNotification(getContext());
}
final boolean shouldSend = shouldSend(acquireInfo, vendorCode);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
index 70e2033..5343d0d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
@@ -34,38 +34,23 @@
private static final String TAG = "FaceUpdateActiveUserClient";
private static final String FACE_DATA_DIR = "facedata";
- private final int mCurrentUserId;
private final boolean mHasEnrolledBiometrics;
@NonNull private final Map<Integer, Long> mAuthenticatorIds;
FaceUpdateActiveUserClient(@NonNull Context context,
- @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
- int sensorId, int currentUserId, boolean hasEnrolledBIometrics,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
+ int sensorId, boolean hasEnrolledBiometrics,
@NonNull Map<Integer, Long> authenticatorIds) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
- mCurrentUserId = currentUserId;
- mHasEnrolledBiometrics = hasEnrolledBIometrics;
+ mHasEnrolledBiometrics = hasEnrolledBiometrics;
mAuthenticatorIds = authenticatorIds;
}
@Override
public void start(@NonNull Callback callback) {
super.start(callback);
-
- if (mCurrentUserId == getTargetUserId()) {
- Slog.d(TAG, "Already user: " + mCurrentUserId + ", refreshing authenticatorId");
- try {
- mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics
- ? getFreshDaemon().getAuthenticatorId().value : 0L);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to refresh authenticatorId", e);
- }
- callback.onClientFinished(this, true /* success */);
- return;
- }
-
startHalOperation();
}
@@ -85,7 +70,10 @@
}
try {
- getFreshDaemon().setActiveUser(getTargetUserId(), storePath.getAbsolutePath());
+ final IBiometricsFace daemon = getFreshDaemon();
+ daemon.setActiveUser(getTargetUserId(), storePath.getAbsolutePath());
+ mAuthenticatorIds.put(getTargetUserId(),
+ mHasEnrolledBiometrics ? daemon.getAuthenticatorId().value : 0L);
mCallback.onClientFinished(this, true /* success */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to setActiveUser: " + e);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlConversionUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlConversionUtils.java
index 66142bf..0ae2e38 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlConversionUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlConversionUtils.java
@@ -46,6 +46,8 @@
return BiometricFingerprintConstants.FINGERPRINT_ERROR_UNABLE_TO_REMOVE;
} else if (aidlError == Error.VENDOR) {
return BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR;
+ } else if (aidlError == Error.BAD_CALIBRATION) {
+ return BiometricFingerprintConstants.FINGERPRINT_ERROR_BAD_CALIBARTION;
} else {
return BiometricFingerprintConstants.FINGERPRINT_ERROR_UNKNOWN;
}
@@ -78,8 +80,7 @@
// No framework constant available
return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN;
} else if (aidlAcquiredInfo == AcquiredInfo.IMMOBILE) {
- // No framework constant available
- return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN;
+ return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_IMMOBILE;
} else if (aidlAcquiredInfo == AcquiredInfo.RETRYING_CAPTURE) {
// No framework constant available
return BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 3c9d802..ba6ef29 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -33,6 +33,7 @@
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutConsumer;
@@ -102,6 +103,10 @@
public void onError(int errorCode, int vendorCode) {
super.onError(errorCode, vendorCode);
+ if (errorCode == BiometricFingerprintConstants.FINGERPRINT_ERROR_BAD_CALIBARTION) {
+ BiometricNotificationUtils.showBadCalibrationNotification(getContext());
+ }
+
UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
}
@@ -178,6 +183,9 @@
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
+
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mCallback.onClientFinished(this, false /* success */);
}
@Override
@@ -192,5 +200,8 @@
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
+
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
+ mCallback.onClientFinished(this, false /* success */);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index 1184966..646b988 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -35,6 +35,7 @@
import android.util.Slog;
import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnrollClient;
@@ -150,6 +151,7 @@
UdfpsHelper.getReasonFromEnrollReason(mEnrollReason),
mUdfpsOverlayController, this);
SidefpsHelper.showOverlay(mSidefpsController);
+ BiometricNotificationUtils.cancelBadCalibrationNotification(getContext());
try {
mCancellationSignal = getFreshDaemon().enroll(
HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken));
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 45e35e3..01136d6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -32,6 +32,7 @@
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.fingerprint.Udfps;
@@ -111,6 +112,10 @@
public void onError(int errorCode, int vendorCode) {
super.onError(errorCode, vendorCode);
+ if (errorCode == BiometricFingerprintConstants.FINGERPRINT_ERROR_BAD_CALIBARTION) {
+ BiometricNotificationUtils.showBadCalibrationNotification(getContext());
+ }
+
UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index 1607364..8d777e1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -124,10 +124,12 @@
final PerformanceTracker pm = PerformanceTracker.getInstanceForSensorId(getSensorId());
pm.incrementAuthForUser(getTargetUserId(), authenticated);
- try {
- getListener().onDetected(getSensorId(), getTargetUserId(), mIsStrongBiometric);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception when sending onDetected", e);
+ if (getListener() != null) {
+ try {
+ getListener().onDetected(getSensorId(), getTargetUserId(), mIsStrongBiometric);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception when sending onDetected", e);
+ }
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index a28a3f6..eba445f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -31,6 +31,7 @@
import android.os.RemoteException;
import android.util.Slog;
+import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnrollClient;
@@ -97,6 +98,7 @@
UdfpsHelper.getReasonFromEnrollReason(mEnrollReason),
mUdfpsOverlayController, this);
SidefpsHelper.showOverlay(mSidefpsController);
+ BiometricNotificationUtils.cancelBadCalibrationNotification(getContext());
try {
// GroupId was never used. In fact, groupId is always the same as userId.
getFreshDaemon().enroll(mHardwareAuthToken, getTargetUserId(), mTimeoutSec);
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 6dca001..de6e494 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -257,8 +257,8 @@
addChange(c);
}
c.addPackageOverride(packageName, overrides, allowedState, versionCode);
- invalidateCache();
}
+ invalidateCache();
return alreadyKnown;
}
@@ -379,9 +379,9 @@
CompatChange change = mChanges.valueAt(i);
removeOverrideUnsafe(change, packageName, versionCode);
}
- saveOverrides();
- invalidateCache();
}
+ saveOverrides();
+ invalidateCache();
}
/**
@@ -603,6 +603,10 @@
try (InputStream in = new BufferedInputStream(new FileInputStream(overridesFile))) {
Overrides overrides = com.android.server.compat.overrides.XmlParser.read(in);
+ if (overrides == null) {
+ Slog.w(TAG, "Parsing " + overridesFile.getPath() + " failed");
+ return;
+ }
for (ChangeOverrides changeOverrides : overrides.getChangeOverrides()) {
long changeId = changeOverrides.getChangeId();
CompatChange compatChange = mChanges.get(changeId);
@@ -626,7 +630,18 @@
if (mOverridesFile == null) {
return;
}
+ Overrides overrides = new Overrides();
synchronized (mChanges) {
+ List<ChangeOverrides> changeOverridesList = overrides.getChangeOverrides();
+ for (int idx = 0; idx < mChanges.size(); ++idx) {
+ CompatChange c = mChanges.valueAt(idx);
+ ChangeOverrides changeOverrides = c.saveOverrides();
+ if (changeOverrides != null) {
+ changeOverridesList.add(changeOverrides);
+ }
+ }
+ }
+ synchronized (mOverridesFile) {
// Create the file if it doesn't already exist
try {
mOverridesFile.createNewFile();
@@ -636,15 +651,6 @@
}
try (PrintWriter out = new PrintWriter(mOverridesFile)) {
XmlWriter writer = new XmlWriter(out);
- Overrides overrides = new Overrides();
- List<ChangeOverrides> changeOverridesList = overrides.getChangeOverrides();
- for (int idx = 0; idx < mChanges.size(); ++idx) {
- CompatChange c = mChanges.valueAt(idx);
- ChangeOverrides changeOverrides = c.saveOverrides();
- if (changeOverrides != null) {
- changeOverridesList.add(changeOverrides);
- }
- }
XmlWriter.write(writer, overrides);
} catch (IOException e) {
Slog.e(TAG, e.toString());
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 19e858c..0d317f4 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -36,6 +36,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
+import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
@@ -359,7 +360,7 @@
private ApplicationInfo getApplicationInfo(String packageName, int userId) {
return LocalServices.getService(PackageManagerInternal.class).getApplicationInfo(
- packageName, 0, userId, userId);
+ packageName, 0, Process.myUid(), userId);
}
private void killPackage(String packageName) {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index ddac19b..7148bec 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1250,6 +1250,8 @@
updateState(DetailedState.CONNECTING, "agentConnect");
final NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig.Builder()
+ .setLegacyType(ConnectivityManager.TYPE_VPN)
+ .setLegacyTypeName("VPN")
.setBypassableVpn(mConfig.allowBypass && !mLockdown)
.build();
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 170564d..88f88a8 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -19,8 +19,11 @@
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
import android.os.Environment;
import android.os.PowerManager;
+import android.text.TextUtils;
import android.util.MathUtils;
import android.util.Slog;
import android.util.Spline;
@@ -34,6 +37,7 @@
import com.android.server.display.config.HighBrightnessMode;
import com.android.server.display.config.NitsMap;
import com.android.server.display.config.Point;
+import com.android.server.display.config.RefreshRateRange;
import com.android.server.display.config.SensorDetails;
import com.android.server.display.config.XmlParser;
@@ -79,10 +83,13 @@
private final Context mContext;
// The details of the ambient light sensor associated with this display.
- private final SensorIdentifier mAmbientLightSensor = new SensorIdentifier();
+ private final SensorData mAmbientLightSensor = new SensorData();
// The details of the proximity sensor associated with this display.
- private final SensorIdentifier mProximitySensor = new SensorIdentifier();
+ private final SensorData mProximitySensor = new SensorData();
+
+ private final List<RefreshRateLimitation> mRefreshRateLimitations =
+ new ArrayList<>(2 /*initialCapacity*/);
// Nits and backlight values that are loaded from either the display device config file, or
// config.xml. These are the raw values and just used for the dumpsys
@@ -113,6 +120,7 @@
private List<String> mQuirks;
private boolean mIsHighBrightnessModeEnabled = false;
private HighBrightnessModeData mHbmData;
+ private String mLoadedFrom = null;
private DisplayDeviceConfig(Context context) {
mContext = context;
@@ -273,11 +281,11 @@
return mBrightnessRampSlowIncrease;
}
- SensorIdentifier getAmbientLightSensor() {
+ SensorData getAmbientLightSensor() {
return mAmbientLightSensor;
}
- SensorIdentifier getProximitySensor() {
+ SensorData getProximitySensor() {
return mProximitySensor;
}
@@ -303,10 +311,15 @@
return hbmData;
}
+ public List<RefreshRateLimitation> getRefreshRateLimitations() {
+ return mRefreshRateLimitations;
+ }
+
@Override
public String toString() {
String str = "DisplayDeviceConfig{"
- + "mBacklight=" + Arrays.toString(mBacklight)
+ + "mLoadedFrom=" + mLoadedFrom
+ + ", mBacklight=" + Arrays.toString(mBacklight)
+ ", mNits=" + Arrays.toString(mNits)
+ ", mRawBacklight=" + Arrays.toString(mRawBacklight)
+ ", mRawNits=" + Arrays.toString(mRawNits)
@@ -325,6 +338,7 @@
+ ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease
+ ", mAmbientLightSensor=" + mAmbientLightSensor
+ ", mProximitySensor=" + mProximitySensor
+ + ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
+ "}";
return str;
}
@@ -336,9 +350,8 @@
final String filename = String.format(CONFIG_FILE_FORMAT, suffix);
final File filePath = Environment.buildPath(
baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename);
- if (filePath.exists()) {
- final DisplayDeviceConfig config = new DisplayDeviceConfig(context);
- config.initFromFile(filePath);
+ final DisplayDeviceConfig config = new DisplayDeviceConfig(context);
+ if (config.initFromFile(filePath)) {
return config;
}
return null;
@@ -356,15 +369,15 @@
return config;
}
- private void initFromFile(File configFile) {
+ private boolean initFromFile(File configFile) {
if (!configFile.exists()) {
// Display configuration files aren't required to exist.
- return;
+ return false;
}
if (!configFile.isFile()) {
Slog.e(TAG, "Display configuration is not a file: " + configFile + ", skipping");
- return;
+ return false;
}
try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
@@ -385,6 +398,8 @@
Slog.e(TAG, "Encountered an error while reading/parsing display config file: "
+ configFile, e);
}
+ mLoadedFrom = configFile.toString();
+ return true;
}
private void initFromGlobalXml() {
@@ -395,10 +410,12 @@
loadBrightnessRampsFromConfigXml();
loadAmbientLightSensorFromConfigXml();
setProxSensorUnspecified();
+ mLoadedFrom = "<config.xml>";
}
private void initFromDefaultValues() {
// Set all to basic values
+ mLoadedFrom = "Static values";
mBacklightMinimum = PowerManager.BRIGHTNESS_MIN;
mBacklightMaximum = PowerManager.BRIGHTNESS_MAX;
mBrightnessDefault = BRIGHTNESS_DEFAULT;
@@ -640,6 +657,13 @@
mHbmData.timeWindowMillis = hbmTiming.getTimeWindowSecs_all().longValue() * 1000;
mHbmData.timeMaxMillis = hbmTiming.getTimeMaxSecs_all().longValue() * 1000;
mHbmData.timeMinMillis = hbmTiming.getTimeMinSecs_all().longValue() * 1000;
+ final RefreshRateRange rr = hbm.getRefreshRate_all();
+ if (rr != null) {
+ final float min = rr.getMinimum().floatValue();
+ final float max = rr.getMaximum().floatValue();
+ mRefreshRateLimitations.add(new RefreshRateLimitation(
+ DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, min, max));
+ }
}
}
@@ -689,6 +713,11 @@
if (sensorDetails != null) {
mAmbientLightSensor.type = sensorDetails.getType();
mAmbientLightSensor.name = sensorDetails.getName();
+ final RefreshRateRange rr = sensorDetails.getRefreshRate();
+ if (rr != null) {
+ mAmbientLightSensor.minRefreshRate = rr.getMinimum().floatValue();
+ mAmbientLightSensor.maxRefreshRate = rr.getMaximum().floatValue();
+ }
} else {
loadAmbientLightSensorFromConfigXml();
}
@@ -704,22 +733,42 @@
if (sensorDetails != null) {
mProximitySensor.name = sensorDetails.getName();
mProximitySensor.type = sensorDetails.getType();
+ final RefreshRateRange rr = sensorDetails.getRefreshRate();
+ if (rr != null) {
+ mProximitySensor.minRefreshRate = rr.getMinimum().floatValue();
+ mProximitySensor.maxRefreshRate = rr.getMaximum().floatValue();
+ }
} else {
setProxSensorUnspecified();
}
}
- static class SensorIdentifier {
+ static class SensorData {
public String type;
public String name;
+ public float minRefreshRate = 0.0f;
+ public float maxRefreshRate = Float.POSITIVE_INFINITY;
@Override
public String toString() {
return "Sensor{"
- + "type: \"" + type + "\""
- + ", name: \"" + name + "\""
+ + "type: " + type
+ + ", name: " + name
+ + ", refreshRateRange: [" + minRefreshRate + ", " + maxRefreshRate + "]"
+ "} ";
}
+
+ /**
+ * @return True if the sensor matches both the specified name and type, or one if only
+ * one is specified (not-empty). Always returns false if both parameters are null or empty.
+ */
+ public boolean matches(String sensorName, String sensorType) {
+ final boolean isNameSpecified = !TextUtils.isEmpty(sensorName);
+ final boolean isTypeSpecified = !TextUtils.isEmpty(sensorType);
+ return (isNameSpecified || isTypeSpecified)
+ && (!isNameSpecified || sensorName.equals(name))
+ && (!isTypeSpecified || sensorType.equals(type));
+ }
}
/**
@@ -771,7 +820,7 @@
+ ", transition: " + transitionPoint
+ ", timeWindow: " + timeWindowMillis + "ms"
+ ", timeMax: " + timeMaxMillis + "ms"
- + ", timeMin: " + timeMinMillis
+ + ", timeMin: " + timeMinMillis + "ms"
+ "} ";
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index fe5ee78..182a038 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -50,6 +50,7 @@
import android.database.ContentObserver;
import android.graphics.ColorSpace;
import android.graphics.Point;
+import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.AmbientBrightnessDayStats;
@@ -62,6 +63,8 @@
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayGroupListener;
import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
+import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
+import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.hardware.display.DisplayViewport;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
@@ -119,6 +122,8 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.UiThread;
+import com.android.server.display.DisplayDeviceConfig.SensorData;
+import com.android.server.display.utils.SensorUtils;
import com.android.server.wm.SurfaceAnimationThread;
import com.android.server.wm.WindowManagerInternal;
@@ -126,6 +131,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
@@ -2107,6 +2113,11 @@
DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED);
}
+ private DisplayDevice getDeviceForDisplayLocked(int displayId) {
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
+ return display == null ? null : display.getPrimaryDisplayDeviceLocked();
+ }
+
private final class DisplayManagerHandler extends Handler {
public DisplayManagerHandler(Looper looper) {
super(looper, null, true /*async*/);
@@ -3257,6 +3268,53 @@
public int getRefreshRateSwitchingType() {
return getRefreshRateSwitchingTypeInternal();
}
+
+ @Override
+ public RefreshRateRange getRefreshRateForDisplayAndSensor(int displayId, String sensorName,
+ String sensorType) {
+ final SensorManager sensorManager;
+ synchronized (mSyncRoot) {
+ sensorManager = mSensorManager;
+ }
+ if (sensorManager == null) {
+ return null;
+ }
+
+ // Verify that the specified sensor exists.
+ final Sensor sensor = SensorUtils.findSensor(sensorManager, sensorType, sensorName,
+ SensorUtils.NO_FALLBACK);
+ if (sensor == null) {
+ return null;
+ }
+
+ synchronized (mSyncRoot) {
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
+ final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
+ if (device == null) {
+ return null;
+ }
+ final DisplayDeviceConfig config = device.getDisplayDeviceConfig();
+ SensorData sensorData = config.getProximitySensor();
+ if (sensorData.matches(sensorName, sensorType)) {
+ return new RefreshRateRange(sensorData.minRefreshRate,
+ sensorData.maxRefreshRate);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public List<RefreshRateLimitation> getRefreshRateLimitations(int displayId) {
+ final DisplayDeviceConfig config;
+ synchronized (mSyncRoot) {
+ final DisplayDevice device = getDeviceForDisplayLocked(displayId);
+ if (device == null) {
+ return null;
+ }
+ config = device.getDisplayDeviceConfig();
+ }
+ return config.getRefreshRateLimitations();
+ }
}
class DesiredDisplayModeSpecsObserver
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 07d13c2..83fc966 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -16,6 +16,8 @@
package com.android.server.display;
+import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
@@ -26,7 +28,11 @@
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
+import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.net.Uri;
import android.os.Handler;
@@ -51,6 +57,8 @@
import com.android.server.LocalServices;
import com.android.server.display.utils.AmbientFilter;
import com.android.server.display.utils.AmbientFilterFactory;
+import com.android.server.sensors.SensorManagerInternal;
+import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.utils.DeviceConfigInterface;
@@ -85,8 +93,7 @@
private static final int INVALID_DISPLAY_MODE_ID = -1;
- // The tolerance within which we consider something approximately equals.
- private static final float FLOAT_TOLERANCE = 0.01f;
+ private static final float FLOAT_TOLERANCE = RefreshRateRange.FLOAT_TOLERANCE;
private final Object mLock = new Object();
private final Context mContext;
@@ -98,6 +105,8 @@
private final SettingsObserver mSettingsObserver;
private final DisplayObserver mDisplayObserver;
private final UdfpsObserver mUdfpsObserver;
+ private final SensorObserver mSensorObserver;
+ private final HbmObserver mHbmObserver;
private final DeviceConfigInterface mDeviceConfig;
private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
@@ -123,7 +132,7 @@
private int mModeSwitchingType = DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS;
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
- this(context, handler, new RealInjector());
+ this(context, handler, new RealInjector(context));
}
public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
@@ -139,6 +148,13 @@
mDisplayObserver = new DisplayObserver(context, handler);
mBrightnessObserver = new BrightnessObserver(context, handler);
mUdfpsObserver = new UdfpsObserver();
+ final BallotBox ballotBox = (displayId, priority, vote) -> {
+ synchronized (mLock) {
+ updateVoteLocked(displayId, priority, vote);
+ }
+ };
+ mSensorObserver = new SensorObserver(context, ballotBox);
+ mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler());
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
mDeviceConfig = injector.getDeviceConfig();
mAlwaysRespectAppRequest = false;
@@ -155,6 +171,8 @@
mSettingsObserver.observe();
mDisplayObserver.observe();
mBrightnessObserver.observe(sensorManager);
+ mSensorObserver.observe();
+ mHbmObserver.observe();
synchronized (mLock) {
// We may have a listener already registered before the call to start, so go ahead and
// notify them to pick up our newly initialized state.
@@ -585,6 +603,8 @@
mAppRequestObserver.dumpLocked(pw);
mBrightnessObserver.dumpLocked(pw);
mUdfpsObserver.dumpLocked(pw);
+ mSensorObserver.dumpLocked(pw);
+ mHbmObserver.dumpLocked(pw);
}
}
@@ -768,66 +788,6 @@
}
/**
- * Information about the min and max refresh rate DM would like to set the display to.
- */
- public static final class RefreshRateRange {
- /**
- * The lowest desired refresh rate.
- */
- public float min;
- /**
- * The highest desired refresh rate.
- */
- public float max;
-
- public RefreshRateRange() {}
-
- public RefreshRateRange(float min, float max) {
- if (min < 0 || max < 0 || min > max + FLOAT_TOLERANCE) {
- Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : "
- + min + " " + max);
- this.min = this.max = 0;
- return;
- }
- if (min > max) {
- // Min and max are within epsilon of each other, but in the wrong order.
- float t = min;
- min = max;
- max = t;
- }
- this.min = min;
- this.max = max;
- }
-
- /**
- * Checks whether the two objects have the same values.
- */
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- if (!(other instanceof RefreshRateRange)) {
- return false;
- }
-
- RefreshRateRange refreshRateRange = (RefreshRateRange) other;
- return (min == refreshRateRange.min && max == refreshRateRange.max);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(min, max);
- }
-
- @Override
- public String toString() {
- return "(" + min + " " + max + ")";
- }
- }
-
- /**
* Information about the desired display mode to be set by the system. Includes the base
* mode ID and the primary and app request refresh rate ranges.
*
@@ -987,9 +947,16 @@
// user seeing the display flickering when the switches occur.
public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 8;
+ // High-brightness-mode may need a specific range of refresh-rates to function properly.
+ public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 9;
+
+ // The proximity sensor needs the refresh rate to be locked in order to function, so this is
+ // set to a high priority.
+ public static final int PRIORITY_PROXIMITY = 10;
+
// The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
// to function, so this needs to be the highest priority of all votes.
- public static final int PRIORITY_UDFPS = 9;
+ public static final int PRIORITY_UDFPS = 11;
// Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
// APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
@@ -1066,27 +1033,30 @@
public static String priorityToString(int priority) {
switch (priority) {
+ case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE:
+ return "PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE";
+ case PRIORITY_APP_REQUEST_MAX_REFRESH_RATE:
+ return "PRIORITY_APP_REQUEST_MAX_REFRESH_RATE";
+ case PRIORITY_APP_REQUEST_SIZE:
+ return "PRIORITY_APP_REQUEST_SIZE";
case PRIORITY_DEFAULT_REFRESH_RATE:
return "PRIORITY_DEFAULT_REFRESH_RATE";
case PRIORITY_FLICKER_REFRESH_RATE:
return "PRIORITY_FLICKER_REFRESH_RATE";
case PRIORITY_FLICKER_REFRESH_RATE_SWITCH:
return "PRIORITY_FLICKER_REFRESH_RATE_SWITCH";
- case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
- return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
- case PRIORITY_APP_REQUEST_MAX_REFRESH_RATE:
- return "PRIORITY_APP_REQUEST_MAX_REFRESH_RATE";
- case PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE:
- return "PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE";
- case PRIORITY_APP_REQUEST_SIZE:
- return "PRIORITY_APP_REQUEST_SIZE";
- case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
- return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
+ case PRIORITY_HIGH_BRIGHTNESS_MODE:
+ return "PRIORITY_HIGH_BRIGHTNESS_MODE";
+ case PRIORITY_PROXIMITY:
+ return "PRIORITY_PROXIMITY";
case PRIORITY_LOW_POWER_MODE:
return "PRIORITY_LOW_POWER_MODE";
case PRIORITY_UDFPS:
return "PRIORITY_UDFPS";
-
+ case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
+ return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
+ case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
+ return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
default:
return Integer.toString(priority);
}
@@ -2142,6 +2112,131 @@
}
}
+ private static class SensorObserver implements ProximityActiveListener {
+ private static final String PROXIMITY_SENSOR_NAME = null;
+ private static final String PROXIMITY_SENSOR_TYPE = Sensor.STRING_TYPE_PROXIMITY;
+
+ private final BallotBox mBallotBox;
+ private final Context mContext;
+
+ private DisplayManager mDisplayManager;
+ private DisplayManagerInternal mDisplayManagerInternal;
+ private boolean mIsProxActive = false;
+
+ SensorObserver(Context context, BallotBox ballotBox) {
+ mContext = context;
+ mBallotBox = ballotBox;
+ }
+
+ @Override
+ public void onProximityActive(boolean isActive) {
+ if (mIsProxActive != isActive) {
+ mIsProxActive = isActive;
+ recalculateVotes();
+ }
+ }
+
+ public void observe() {
+ mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+
+ final SensorManagerInternal sensorManager =
+ LocalServices.getService(SensorManagerInternal.class);
+ sensorManager.addProximityActiveListener(BackgroundThread.getExecutor(), this);
+ }
+
+ private void recalculateVotes() {
+ final Display[] displays = mDisplayManager.getDisplays();
+ for (Display d : displays) {
+ int displayId = d.getDisplayId();
+ Vote vote = null;
+ if (mIsProxActive) {
+ final RefreshRateRange rate =
+ mDisplayManagerInternal.getRefreshRateForDisplayAndSensor(
+ displayId, PROXIMITY_SENSOR_NAME, PROXIMITY_SENSOR_TYPE);
+ if (rate != null) {
+ vote = Vote.forRefreshRates(rate.min, rate.max);
+ }
+ }
+ mBallotBox.vote(displayId, Vote.PRIORITY_PROXIMITY, vote);
+ }
+ }
+
+ void dumpLocked(PrintWriter pw) {
+ pw.println(" SensorObserver");
+ pw.println(" mIsProxActive=" + mIsProxActive);
+ }
+ }
+
+ /**
+ * Listens to DisplayManager for HBM status and applies any refresh-rate restrictions for
+ * HBM that are associated with that display. Restrictions are retrieved from
+ * DisplayManagerInternal but originate in the display-device-config file.
+ */
+ private static class HbmObserver implements DisplayManager.DisplayListener {
+ private final BallotBox mBallotBox;
+ private final Handler mHandler;
+ private final SparseBooleanArray mHbmEnabled = new SparseBooleanArray();
+ private final Injector mInjector;
+
+ private DisplayManagerInternal mDisplayManagerInternal;
+
+ HbmObserver(Injector injector, BallotBox ballotBox, Handler handler) {
+ mInjector = injector;
+ mBallotBox = ballotBox;
+ mHandler = handler;
+ }
+
+ public void observe() {
+ mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+ mInjector.registerDisplayListener(this, mHandler,
+ DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+ | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {}
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, null);
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ final BrightnessInfo info = mInjector.getBrightnessInfo(displayId);
+ if (info == null) {
+ // Display no longer there. Assume we'll get an onDisplayRemoved very soon.
+ return;
+ }
+ final boolean isHbmEnabled =
+ info.highBrightnessMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
+ if (isHbmEnabled == mHbmEnabled.get(displayId)) {
+ // no change, ignore.
+ return;
+ }
+ Vote vote = null;
+ mHbmEnabled.put(displayId, isHbmEnabled);
+ if (isHbmEnabled) {
+ final List<RefreshRateLimitation> limits =
+ mDisplayManagerInternal.getRefreshRateLimitations(displayId);
+ for (int i = 0; limits != null && i < limits.size(); i++) {
+ final RefreshRateLimitation limitation = limits.get(i);
+ if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) {
+ vote = Vote.forRefreshRates(limitation.range.min, limitation.range.max);
+ break;
+ }
+ }
+ }
+ mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, vote);
+ }
+
+ void dumpLocked(PrintWriter pw) {
+ pw.println(" HbmObserver");
+ pw.println(" mHbmEnabled: " + mHbmEnabled);
+ }
+ }
+
private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
public DeviceConfigDisplaySettings() {
}
@@ -2296,10 +2391,21 @@
void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer);
+
+ void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener,
+ Handler handler, long flags);
+
+ BrightnessInfo getBrightnessInfo(int displayId);
}
@VisibleForTesting
static class RealInjector implements Injector {
+ private final Context mContext;
+ private DisplayManager mDisplayManager;
+
+ RealInjector(Context context) {
+ mContext = context;
+ }
@Override
@NonNull
@@ -2326,6 +2432,31 @@
cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
observer, UserHandle.USER_SYSTEM);
}
+
+ @Override
+ public void registerDisplayListener(DisplayManager.DisplayListener listener,
+ Handler handler, long flags) {
+ getDisplayManager().registerDisplayListener(listener, handler, flags);
+ }
+
+ @Override
+ public BrightnessInfo getBrightnessInfo(int displayId) {
+ final Display display = getDisplayManager().getDisplay(displayId);
+ if (display != null) {
+ return display.getBrightnessInfo();
+ }
+ return null;
+ }
+
+ private DisplayManager getDisplayManager() {
+ if (mDisplayManager == null) {
+ mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ }
+ return mDisplayManager;
+ }
}
+ interface BallotBox {
+ void vote(int displayId, int priority, Vote vote);
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 1b50f03..555add4 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -48,7 +48,6 @@
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
-import android.text.TextUtils;
import android.util.Log;
import android.util.MathUtils;
import android.util.Slog;
@@ -65,13 +64,13 @@
import com.android.server.display.RampAnimator.DualRampAnimator;
import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
+import com.android.server.display.utils.SensorUtils;
import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
import com.android.server.display.whitebalance.DisplayWhiteBalanceSettings;
import com.android.server.policy.WindowManagerPolicy;
import java.io.PrintWriter;
-import java.util.List;
/**
* Controls the power state of the display.
@@ -586,26 +585,6 @@
mBrightnessMapper.recalculateSplines(mCdsi.isReduceBrightColorsActivated(), adjustedNits);
}
- private Sensor findSensor(String sensorType, String sensorName, int fallbackType,
- boolean useFallback) {
- final boolean isNameSpecified = !TextUtils.isEmpty(sensorName);
- final boolean isTypeSpecified = !TextUtils.isEmpty(sensorType);
- List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
- if (isNameSpecified || isTypeSpecified) {
- for (Sensor sensor : sensors) {
- if ((!isNameSpecified || sensorName.equals(sensor.getName()))
- && (!isTypeSpecified || sensorType.equals(sensor.getStringType()))) {
- return sensor;
- }
- }
- }
- if (useFallback) {
- return mSensorManager.getDefaultSensor(fallbackType);
- } else {
- return null;
- }
- }
-
/**
* Returns true if the proximity sensor screen-off function is available.
*/
@@ -1654,24 +1633,23 @@
}
private void loadAmbientLightSensor() {
- DisplayDeviceConfig.SensorIdentifier lightSensor =
- mDisplayDeviceConfig.getAmbientLightSensor();
- String lightSensorName = lightSensor.name;
- String lightSensorType = lightSensor.type;
- mLightSensor = findSensor(lightSensorType, lightSensorName, Sensor.TYPE_LIGHT,
- mDisplayId == Display.DEFAULT_DISPLAY);
+ DisplayDeviceConfig.SensorData lightSensor = mDisplayDeviceConfig.getAmbientLightSensor();
+ final int fallbackType = mDisplayId == Display.DEFAULT_DISPLAY
+ ? Sensor.TYPE_LIGHT : SensorUtils.NO_FALLBACK;
+ mLightSensor = SensorUtils.findSensor(mSensorManager, lightSensor.type, lightSensor.name,
+ fallbackType);
}
private void loadProximitySensor() {
if (DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) {
return;
}
- final DisplayDeviceConfig.SensorIdentifier proxSensor =
+ final DisplayDeviceConfig.SensorData proxSensor =
mDisplayDeviceConfig.getProximitySensor();
- final String proxSensorName = proxSensor.name;
- final String proxSensorType = proxSensor.type;
- mProximitySensor = findSensor(proxSensorType, proxSensorName, Sensor.TYPE_PROXIMITY,
- mDisplayId == Display.DEFAULT_DISPLAY);
+ final int fallbackType = mDisplayId == Display.DEFAULT_DISPLAY
+ ? Sensor.TYPE_PROXIMITY : SensorUtils.NO_FALLBACK;
+ mProximitySensor = SensorUtils.findSensor(mSensorManager, proxSensor.type, proxSensor.name,
+ fallbackType);
if (mProximitySensor != null) {
mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(),
TYPICAL_PROXIMITY_THRESHOLD);
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index b948777..57a8c4b 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -22,6 +22,7 @@
import android.os.PowerManager;
import android.os.SystemClock;
import android.util.Slog;
+import android.util.TimeUtils;
import android.view.SurfaceControlHdrLayerInfoListener;
import com.android.internal.annotations.VisibleForTesting;
@@ -189,6 +190,10 @@
}
void dump(PrintWriter pw) {
+ mHandler.runWithScissors(() -> dumpLocal(pw), 1000);
+ }
+
+ private void dumpLocal(PrintWriter pw) {
pw.println("HighBrightnessModeController:");
pw.println(" mCurrentMin=" + getCurrentBrightnessMin());
pw.println(" mCurrentMax=" + getCurrentBrightnessMax());
@@ -202,6 +207,29 @@
pw.println(" mIsHdrLayerPresent=" + mIsHdrLayerPresent);
pw.println(" mBrightnessMin=" + mBrightnessMin);
pw.println(" mBrightnessMax=" + mBrightnessMax);
+ pw.println(" mRunningStartTimeMillis=" + TimeUtils.formatUptime(mRunningStartTimeMillis));
+ pw.println(" mEvents=");
+ final long currentTime = mClock.uptimeMillis();
+ long lastStartTime = currentTime;
+ if (mRunningStartTimeMillis != -1) {
+ lastStartTime = dumpHbmEvent(pw, new HbmEvent(mRunningStartTimeMillis, currentTime));
+ }
+ for (HbmEvent event : mEvents) {
+ if (lastStartTime > event.endTimeMillis) {
+ pw.println(" event: [normal brightness]: "
+ + TimeUtils.formatDuration(lastStartTime - event.endTimeMillis));
+ }
+ lastStartTime = dumpHbmEvent(pw, event);
+ }
+ }
+
+ private long dumpHbmEvent(PrintWriter pw, HbmEvent event) {
+ final long duration = event.endTimeMillis - event.startTimeMillis;
+ pw.println(" event: ["
+ + TimeUtils.formatUptime(event.startTimeMillis) + ", "
+ + TimeUtils.formatUptime(event.endTimeMillis) + "] ("
+ + TimeUtils.formatDuration(duration) + ")");
+ return event.startTimeMillis;
}
private boolean isCurrentlyAllowed() {
diff --git a/services/core/java/com/android/server/display/WifiDisplayController.java b/services/core/java/com/android/server/display/WifiDisplayController.java
index 6db75eb..a7e1a28 100644
--- a/services/core/java/com/android/server/display/WifiDisplayController.java
+++ b/services/core/java/com/android/server/display/WifiDisplayController.java
@@ -550,11 +550,6 @@
private void disconnect() {
mDesiredDevice = null;
- mWifiP2pManager = null;
- if (null != mWifiP2pChannel) {
- mWifiP2pChannel.close();
- mWifiP2pChannel = null;
- }
updateConnection();
}
diff --git a/services/core/java/com/android/server/display/utils/SensorUtils.java b/services/core/java/com/android/server/display/utils/SensorUtils.java
new file mode 100644
index 0000000..cb40b40
--- /dev/null
+++ b/services/core/java/com/android/server/display/utils/SensorUtils.java
@@ -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.
+ */
+
+package com.android.server.display.utils;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.text.TextUtils;
+
+import java.util.List;
+
+/**
+ * Provides utility methods for dealing with sensors.
+ */
+public class SensorUtils {
+ public static final int NO_FALLBACK = 0;
+
+ /**
+ * Finds the specified sensor by type and name using SensorManager.
+ */
+ public static Sensor findSensor(SensorManager sensorManager, String sensorType,
+ String sensorName, int fallbackType) {
+ final boolean isNameSpecified = !TextUtils.isEmpty(sensorName);
+ final boolean isTypeSpecified = !TextUtils.isEmpty(sensorType);
+ if (isNameSpecified || isTypeSpecified) {
+ final List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
+ for (Sensor sensor : sensors) {
+ if ((!isNameSpecified || sensorName.equals(sensor.getName()))
+ && (!isTypeSpecified || sensorType.equals(sensor.getStringType()))) {
+ return sensor;
+ }
+ }
+ }
+ if (fallbackType != NO_FALLBACK) {
+ return sensorManager.getDefaultSensor(fallbackType);
+ }
+
+ return null;
+ }
+
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 2ed160a..a086bda 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -2580,7 +2580,8 @@
return null;
}
- private void addHdmiControlStatusChangeListener(
+ @VisibleForTesting
+ void addHdmiControlStatusChangeListener(
final IHdmiControlStatusChangeListener listener) {
final HdmiControlStatusChangeListenerRecord record =
new HdmiControlStatusChangeListenerRecord(listener);
@@ -2916,13 +2917,17 @@
} else {
mIsCecAvailable = true;
}
+ if (!listeners.isEmpty()) {
+ invokeHdmiControlStatusChangeListenerLocked(listeners,
+ isEnabled, mIsCecAvailable);
+ }
}
});
} else {
mIsCecAvailable = false;
- }
- if (!listeners.isEmpty()) {
- invokeHdmiControlStatusChangeListenerLocked(listeners, isEnabled, mIsCecAvailable);
+ if (!listeners.isEmpty()) {
+ invokeHdmiControlStatusChangeListenerLocked(listeners, isEnabled, mIsCecAvailable);
+ }
}
}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index b9e97e8..e6210b2 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -45,6 +45,7 @@
import android.app.compat.CompatChanges;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.GeocoderParams;
import android.location.Geofence;
@@ -65,7 +66,7 @@
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationManagerInternal;
-import android.location.LocationManagerInternal.OnProviderLocationTagsChangeListener;
+import android.location.LocationManagerInternal.LocationPackageTagsListener;
import android.location.LocationProvider;
import android.location.LocationRequest;
import android.location.LocationTime;
@@ -91,8 +92,10 @@
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.location.eventlog.LocationEventLog;
@@ -133,6 +136,8 @@
import com.android.server.location.provider.PassiveLocationProviderManager;
import com.android.server.location.provider.StationaryThrottlingLocationProvider;
import com.android.server.location.provider.proxy.ProxyLocationProvider;
+import com.android.server.location.settings.LocationSettings;
+import com.android.server.location.settings.LocationUserSettings;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import java.io.FileDescriptor;
@@ -259,7 +264,7 @@
new CopyOnWriteArrayList<>();
@GuardedBy("mLock")
- @Nullable OnProviderLocationTagsChangeListener mOnProviderLocationTagsChangeListener;
+ @Nullable LocationPackageTagsListener mLocationTagsChangedListener;
LocationManagerService(Context context, Injector injector) {
mContext = context.createAttributionContext(ATTRIBUTION_TAG);
@@ -269,6 +274,8 @@
mGeofenceManager = new GeofenceManager(mContext, injector);
+ mInjector.getLocationSettings().registerLocationUserSettingsListener(
+ this::onLocationUserSettingsChanged);
mInjector.getSettingsHelper().addOnLocationEnabledChangedListener(
this::onLocationModeChanged);
mInjector.getSettingsHelper().addIgnoreSettingsAllowlistChangedListener(
@@ -475,6 +482,25 @@
}
}
+ private void onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings,
+ LocationUserSettings newSettings) {
+ if (oldSettings.isAdasGnssLocationEnabled() != newSettings.isAdasGnssLocationEnabled()) {
+ boolean enabled = newSettings.isAdasGnssLocationEnabled();
+
+ if (D) {
+ Log.d(TAG, "[u" + userId + "] adas gnss location enabled = " + enabled);
+ }
+
+ EVENT_LOG.logAdasLocationEnabled(userId, enabled);
+
+ Intent intent = new Intent(LocationManager.ACTION_ADAS_GNSS_ENABLED_CHANGED)
+ .putExtra(LocationManager.EXTRA_ADAS_GNSS_ENABLED, enabled)
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+ }
+ }
+
private void onLocationModeChanged(int userId) {
boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId);
LocationManager.invalidateLocalLocationEnabledCaches();
@@ -660,7 +686,7 @@
// clients in the system process must have an attribution tag set
Preconditions.checkState(identity.getPid() != Process.myPid() || attributionTag != null);
- request = validateLocationRequest(request, identity);
+ request = validateLocationRequest(provider, request, identity);
LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
@@ -686,7 +712,7 @@
new IllegalArgumentException());
}
- request = validateLocationRequest(request, identity);
+ request = validateLocationRequest(provider, request, identity);
LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
@@ -724,7 +750,7 @@
}
}
- request = validateLocationRequest(request, identity);
+ request = validateLocationRequest(provider, request, identity);
LocationProviderManager manager = getLocationProviderManager(provider);
Preconditions.checkArgument(manager != null,
@@ -733,33 +759,27 @@
manager.registerLocationRequest(request, identity, permissionLevel, pendingIntent);
}
- private LocationRequest validateLocationRequest(LocationRequest request,
+ private LocationRequest validateLocationRequest(String provider, LocationRequest request,
CallerIdentity identity) {
+ // validate unsanitized request
if (!request.getWorkSource().isEmpty()) {
mContext.enforceCallingOrSelfPermission(
permission.UPDATE_DEVICE_STATS,
"setting a work source requires " + permission.UPDATE_DEVICE_STATS);
}
- if (request.isHiddenFromAppOps()) {
- mContext.enforceCallingOrSelfPermission(
- permission.UPDATE_APP_OPS_STATS,
- "hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
- }
- if (request.isLocationSettingsIgnored()) {
- mContext.enforceCallingOrSelfPermission(
- permission.WRITE_SECURE_SETTINGS,
- "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
- }
+ // sanitize request
LocationRequest.Builder sanitized = new LocationRequest.Builder(request);
- if (CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS, Binder.getCallingUid())) {
- if (request.isLowPower()) {
- mContext.enforceCallingOrSelfPermission(
- permission.LOCATION_HARDWARE,
- "low power request requires " + permission.LOCATION_HARDWARE);
- }
- } else {
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ && GPS_PROVIDER.equals(provider)
+ && ArrayUtils.contains(mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_locationDriverAssistancePackageNames),
+ identity.getPackageName())) {
+ sanitized.setAdasGnssBypass(true);
+ }
+
+ if (!CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS, Binder.getCallingUid())) {
if (mContext.checkCallingPermission(permission.LOCATION_HARDWARE)
!= PERMISSION_GRANTED) {
sanitized.setLowPower(false);
@@ -785,7 +805,52 @@
}
sanitized.setWorkSource(workSource);
- return sanitized.build();
+ request = sanitized.build();
+
+ // validate sanitized request
+ boolean isLocationProvider = mLocalService.isProvider(null, identity);
+
+ if (request.isLowPower() && CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS,
+ identity.getUid())) {
+ mContext.enforceCallingOrSelfPermission(
+ permission.LOCATION_HARDWARE,
+ "low power request requires " + permission.LOCATION_HARDWARE);
+ }
+ if (request.isHiddenFromAppOps()) {
+ mContext.enforceCallingOrSelfPermission(
+ permission.UPDATE_APP_OPS_STATS,
+ "hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
+ }
+ if (request.isAdasGnssBypass()) {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ throw new IllegalArgumentException(
+ "adas gnss bypass requests are only allowed on automotive devices");
+ }
+ if (!GPS_PROVIDER.equals(provider)) {
+ throw new IllegalArgumentException(
+ "adas gnss bypass requests are only allowed on the \"gps\" provider");
+ }
+ if (!ArrayUtils.contains(mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_locationDriverAssistancePackageNames),
+ identity.getPackageName())) {
+ throw new SecurityException(
+ "only verified adas packages may use adas gnss bypass requests");
+ }
+ if (!isLocationProvider) {
+ mContext.enforceCallingOrSelfPermission(
+ permission.WRITE_SECURE_SETTINGS,
+ "adas gnss bypass requires " + permission.WRITE_SECURE_SETTINGS);
+ }
+ }
+ if (request.isLocationSettingsIgnored()) {
+ if (!isLocationProvider) {
+ mContext.enforceCallingOrSelfPermission(
+ permission.WRITE_SECURE_SETTINGS,
+ "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
+ }
+ }
+
+ return request;
}
@Override
@@ -833,7 +898,7 @@
// clients in the system process must have an attribution tag set
Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null);
- request = validateLastLocationRequest(request);
+ request = validateLastLocationRequest(provider, request, identity);
LocationProviderManager manager = getLocationProviderManager(provider);
if (manager == null) {
@@ -843,16 +908,58 @@
return manager.getLastLocation(request, identity, permissionLevel);
}
- private LastLocationRequest validateLastLocationRequest(LastLocationRequest request) {
+ private LastLocationRequest validateLastLocationRequest(String provider,
+ LastLocationRequest request,
+ CallerIdentity identity) {
+ // sanitize request
+ LastLocationRequest.Builder sanitized = new LastLocationRequest.Builder(request);
+
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ && GPS_PROVIDER.equals(provider)
+ && ArrayUtils.contains(mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_locationDriverAssistancePackageNames),
+ identity.getPackageName())) {
+ sanitized.setAdasGnssBypass(true);
+ }
+
+ request = sanitized.build();
+
+ // validate request
+ boolean isLocationProvider = mLocalService.isProvider(null, identity);
+
if (request.isHiddenFromAppOps()) {
mContext.enforceCallingOrSelfPermission(
permission.UPDATE_APP_OPS_STATS,
"hiding from app ops requires " + permission.UPDATE_APP_OPS_STATS);
}
+
+ if (request.isAdasGnssBypass()) {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ throw new IllegalArgumentException(
+ "adas gnss bypass requests are only allowed on automotive devices");
+ }
+ if (!GPS_PROVIDER.equals(provider)) {
+ throw new IllegalArgumentException(
+ "adas gnss bypass requests are only allowed on the \"gps\" provider");
+ }
+ if (!ArrayUtils.contains(mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_locationDriverAssistancePackageNames),
+ identity.getPackageName())) {
+ throw new SecurityException(
+ "only verified adas packages may use adas gnss bypass requests");
+ }
+ if (!isLocationProvider) {
+ mContext.enforceCallingOrSelfPermission(
+ permission.WRITE_SECURE_SETTINGS,
+ "adas gnss bypass requires " + permission.WRITE_SECURE_SETTINGS);
+ }
+ }
if (request.isLocationSettingsIgnored()) {
- mContext.enforceCallingOrSelfPermission(
- permission.WRITE_SECURE_SETTINGS,
- "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
+ if (!isLocationProvider) {
+ mContext.enforceCallingOrSelfPermission(
+ permission.WRITE_SECURE_SETTINGS,
+ "ignoring location settings requires " + permission.WRITE_SECURE_SETTINGS);
+ }
}
return request;
@@ -1125,6 +1232,24 @@
}
@Override
+ public void setAdasGnssLocationEnabledForUser(boolean enabled, int userId) {
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, false, "setAdasGnssLocationEnabledForUser", null);
+
+ mContext.enforceCallingOrSelfPermission(permission.WRITE_SECURE_SETTINGS, null);
+
+ mInjector.getLocationSettings().updateUserSettings(userId,
+ settings -> settings.withAdasGnssLocationEnabled(enabled));
+ }
+
+ @Override
+ public boolean isAdasGnssLocationEnabledForUser(int userId) {
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ userId, false, false, "isAdasGnssLocationEnabledForUser", null);
+ return mInjector.getLocationSettings().getUserSettings(userId).isAdasGnssLocationEnabled();
+ }
+
+ @Override
public boolean isProviderEnabledForUser(String provider, int userId) {
return mLocalService.isProviderEnabledForUser(provider, userId);
}
@@ -1363,32 +1488,28 @@
refreshAppOpsRestrictions(UserHandle.USER_ALL);
}
- OnProviderLocationTagsChangeListener listener;
- synchronized (mLock) {
- listener = mOnProviderLocationTagsChangeListener;
- }
-
- if (listener != null) {
- if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags)
- || !Objects.equals(oldState.identity, newState.identity)) {
- if (oldState.identity != null) {
- listener.onLocationTagsChanged(
- new LocationManagerInternal.LocationTagInfo(
- oldState.identity.getUid(),
- oldState.identity.getPackageName(),
- Collections.emptySet()));
- }
- if (newState.identity != null) {
- ArraySet<String> attributionTags = new ArraySet<>(
- newState.extraAttributionTags.size() + 1);
- attributionTags.addAll(newState.extraAttributionTags);
- attributionTags.add(newState.identity.getAttributionTag());
-
- listener.onLocationTagsChanged(
- new LocationManagerInternal.LocationTagInfo(
- newState.identity.getUid(),
- newState.identity.getPackageName(),
- attributionTags));
+ if (!oldState.extraAttributionTags.equals(newState.extraAttributionTags)
+ || !Objects.equals(oldState.identity, newState.identity)) {
+ // since we're potentially affecting the tag lists for two different uids, acquire the
+ // lock to ensure providers cannot change while we're looping over the providers
+ // multiple times, which could lead to inconsistent results.
+ synchronized (mLock) {
+ LocationPackageTagsListener listener = mLocationTagsChangedListener;
+ if (listener != null) {
+ int oldUid = oldState.identity != null ? oldState.identity.getUid() : -1;
+ int newUid = newState.identity != null ? newState.identity.getUid() : -1;
+ if (oldUid != -1) {
+ PackageTagsList tags = calculateAppOpsLocationSourceTags(oldUid);
+ FgThread.getHandler().post(
+ () -> listener.onLocationPackageTagsChanged(oldUid, tags));
+ }
+ // if the new app id is the same as the old app id, no need to invoke the
+ // listener twice, it's already been taken care of
+ if (newUid != -1 && newUid != oldUid) {
+ PackageTagsList tags = calculateAppOpsLocationSourceTags(newUid);
+ FgThread.getHandler().post(
+ () -> listener.onLocationPackageTagsChanged(newUid, tags));
+ }
}
}
}
@@ -1436,6 +1557,31 @@
userId);
}
+ PackageTagsList calculateAppOpsLocationSourceTags(int uid) {
+ PackageTagsList.Builder builder = new PackageTagsList.Builder();
+ for (LocationProviderManager manager : mProviderManagers) {
+ AbstractLocationProvider.State managerState = manager.getState();
+ if (managerState.identity == null) {
+ continue;
+ }
+ if (managerState.identity.getUid() != uid) {
+ continue;
+ }
+
+ builder.add(managerState.identity.getPackageName(), managerState.extraAttributionTags);
+ if (managerState.extraAttributionTags.isEmpty()
+ || managerState.identity.getAttributionTag() != null) {
+ builder.add(managerState.identity.getPackageName(),
+ managerState.identity.getAttributionTag());
+ } else {
+ Log.e(TAG, manager.getName() + " provider has specified a null attribution tag and "
+ + "a non-empty set of extra attribution tags - dropping the null "
+ + "attribution tag");
+ }
+ }
+ return builder.build();
+ }
+
private class LocalService extends LocationManagerInternal {
LocalService() {}
@@ -1506,19 +1652,39 @@
}
@Override
- public void setOnProviderLocationTagsChangeListener(
- @Nullable OnProviderLocationTagsChangeListener listener) {
+ public void setLocationPackageTagsListener(
+ @Nullable LocationPackageTagsListener listener) {
synchronized (mLock) {
- mOnProviderLocationTagsChangeListener = listener;
+ mLocationTagsChangedListener = listener;
+
+ // calculate initial tag list and send to listener
+ if (listener != null) {
+ ArraySet<Integer> uids = new ArraySet<>(mProviderManagers.size());
+ for (LocationProviderManager manager : mProviderManagers) {
+ CallerIdentity identity = manager.getIdentity();
+ if (identity != null) {
+ uids.add(identity.getUid());
+ }
+ }
+
+ for (int uid : uids) {
+ PackageTagsList tags = calculateAppOpsLocationSourceTags(uid);
+ if (!tags.isEmpty()) {
+ FgThread.getHandler().post(
+ () -> listener.onLocationPackageTagsChanged(uid, tags));
+ }
+ }
+ }
}
}
}
- private static class SystemInjector implements Injector {
+ private static final class SystemInjector implements Injector {
private final Context mContext;
private final UserInfoHelper mUserInfoHelper;
+ private final LocationSettings mLocationSettings;
private final AlarmHelper mAlarmHelper;
private final SystemAppOpsHelper mAppOpsHelper;
private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
@@ -1543,6 +1709,7 @@
mContext = context;
mUserInfoHelper = userInfoHelper;
+ mLocationSettings = new LocationSettings(context);
mAlarmHelper = new SystemAlarmHelper(context);
mAppOpsHelper = new SystemAppOpsHelper(context);
mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
@@ -1580,6 +1747,11 @@
}
@Override
+ public LocationSettings getLocationSettings() {
+ return mLocationSettings;
+ }
+
+ @Override
public AlarmHelper getAlarmHelper() {
return mAlarmHelper;
}
diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java
index 9378493..b65338d 100644
--- a/services/core/java/com/android/server/location/LocationShellCommand.java
+++ b/services/core/java/com/android/server/location/LocationShellCommand.java
@@ -17,6 +17,7 @@
package com.android.server.location;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.provider.ProviderProperties;
@@ -60,6 +61,14 @@
handleSetLocationEnabled();
return 0;
}
+ case "is-adas-gnss-location-enabled": {
+ handleIsAdasGnssLocationEnabled();
+ return 0;
+ }
+ case "set-adas-gnss-location-enabled": {
+ handleSetAdasGnssLocationEnabled();
+ return 0;
+ }
case "providers": {
String command = getNextArgRequired();
return parseProvidersCommand(command);
@@ -134,6 +143,52 @@
mService.setLocationEnabledForUser(enabled, userId);
}
+ private void handleIsAdasGnssLocationEnabled() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ throw new IllegalStateException("command only recognized on automotive devices");
+ }
+
+ int userId = UserHandle.USER_CURRENT_OR_SELF;
+
+ do {
+ String option = getNextOption();
+ if (option == null) {
+ break;
+ }
+ if ("--user".equals(option)) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ throw new IllegalArgumentException("Unknown option: " + option);
+ }
+ } while (true);
+
+ getOutPrintWriter().println(mService.isAdasGnssLocationEnabledForUser(userId));
+ }
+
+ private void handleSetAdasGnssLocationEnabled() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ throw new IllegalStateException("command only recognized on automotive devices");
+ }
+
+ boolean enabled = Boolean.parseBoolean(getNextArgRequired());
+
+ int userId = UserHandle.USER_CURRENT_OR_SELF;
+
+ do {
+ String option = getNextOption();
+ if (option == null) {
+ break;
+ }
+ if ("--user".equals(option)) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ throw new IllegalArgumentException("Unknown option: " + option);
+ }
+ } while (true);
+
+ mService.setAdasGnssLocationEnabledForUser(enabled, userId);
+ }
+
private void handleAddTestProvider() {
String provider = getNextArgRequired();
@@ -297,6 +352,14 @@
pw.println(" set-location-enabled true|false [--user <USER_ID>]");
pw.println(" Sets the master location switch enabled state. If no user is specified,");
pw.println(" the current user is assumed.");
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ pw.println(" is-adas-gnss-location-enabled [--user <USER_ID>]");
+ pw.println(" Gets the ADAS GNSS location enabled state. If no user is specified,");
+ pw.println(" the current user is assumed.");
+ pw.println(" set-adas-gnss-location-enabled true|false [--user <USER_ID>]");
+ pw.println(" Sets the ADAS GNSS location enabled state. If no user is specified,");
+ pw.println(" the current user is assumed.");
+ }
pw.println(" providers");
pw.println(" The providers command is followed by a subcommand, as listed below:");
pw.println();
@@ -323,9 +386,8 @@
pw.println(" Common commands that may be supported by the gps provider, depending on");
pw.println(" hardware and software configurations:");
pw.println(" delete_aiding_data - requests deletion of any predictive aiding data");
- pw.println(" force_time_injection - requests NTP time injection to chipset");
- pw.println(" force_psds_injection - "
- + "requests predictive aiding data injection to chipset");
- pw.println(" request_power_stats - requests GNSS power stats update from chipset");
+ pw.println(" force_time_injection - requests NTP time injection");
+ pw.println(" force_psds_injection - requests predictive aiding data injection");
+ pw.println(" request_power_stats - requests GNSS power stats update");
}
}
diff --git a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
index e6d25ec..db2a43f 100644
--- a/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocationEventLog.java
@@ -55,19 +55,20 @@
private static final int EVENT_USER_SWITCHED = 1;
private static final int EVENT_LOCATION_ENABLED = 2;
- private static final int EVENT_PROVIDER_ENABLED = 3;
- private static final int EVENT_PROVIDER_MOCKED = 4;
- private static final int EVENT_PROVIDER_CLIENT_REGISTER = 5;
- private static final int EVENT_PROVIDER_CLIENT_UNREGISTER = 6;
- private static final int EVENT_PROVIDER_CLIENT_FOREGROUND = 7;
- private static final int EVENT_PROVIDER_CLIENT_BACKGROUND = 8;
- private static final int EVENT_PROVIDER_CLIENT_PERMITTED = 9;
- private static final int EVENT_PROVIDER_CLIENT_UNPERMITTED = 10;
- private static final int EVENT_PROVIDER_UPDATE_REQUEST = 11;
- private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 12;
- private static final int EVENT_PROVIDER_DELIVER_LOCATION = 13;
- private static final int EVENT_PROVIDER_STATIONARY_THROTTLED = 14;
- private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 15;
+ private static final int EVENT_ADAS_LOCATION_ENABLED = 3;
+ private static final int EVENT_PROVIDER_ENABLED = 4;
+ private static final int EVENT_PROVIDER_MOCKED = 5;
+ private static final int EVENT_PROVIDER_CLIENT_REGISTER = 6;
+ private static final int EVENT_PROVIDER_CLIENT_UNREGISTER = 7;
+ private static final int EVENT_PROVIDER_CLIENT_FOREGROUND = 8;
+ private static final int EVENT_PROVIDER_CLIENT_BACKGROUND = 9;
+ private static final int EVENT_PROVIDER_CLIENT_PERMITTED = 10;
+ private static final int EVENT_PROVIDER_CLIENT_UNPERMITTED = 11;
+ private static final int EVENT_PROVIDER_UPDATE_REQUEST = 12;
+ private static final int EVENT_PROVIDER_RECEIVE_LOCATION = 13;
+ private static final int EVENT_PROVIDER_DELIVER_LOCATION = 14;
+ private static final int EVENT_PROVIDER_STATIONARY_THROTTLED = 15;
+ private static final int EVENT_LOCATION_POWER_SAVE_MODE_CHANGE = 16;
@GuardedBy("mAggregateStats")
private final ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> mAggregateStats;
@@ -116,6 +117,11 @@
addLogEvent(EVENT_LOCATION_ENABLED, userId, enabled);
}
+ /** Logs a location enabled/disabled event. */
+ public void logAdasLocationEnabled(int userId, boolean enabled) {
+ addLogEvent(EVENT_ADAS_LOCATION_ENABLED, userId, enabled);
+ }
+
/** Logs a location provider enabled/disabled event. */
public void logProviderEnabled(String provider, int userId, boolean enabled) {
addLogEvent(EVENT_PROVIDER_ENABLED, provider, userId, enabled);
@@ -219,6 +225,9 @@
return new UserSwitchedEvent(timeDelta, (Integer) args[0], (Integer) args[1]);
case EVENT_LOCATION_ENABLED:
return new LocationEnabledEvent(timeDelta, (Integer) args[0], (Boolean) args[1]);
+ case EVENT_ADAS_LOCATION_ENABLED:
+ return new LocationAdasEnabledEvent(timeDelta, (Integer) args[0],
+ (Boolean) args[1]);
case EVENT_PROVIDER_ENABLED:
return new ProviderEnabledEvent(timeDelta, (String) args[0], (Integer) args[1],
(Boolean) args[2]);
@@ -517,6 +526,23 @@
}
}
+ private static final class LocationAdasEnabledEvent extends LogEvent {
+
+ private final int mUserId;
+ private final boolean mEnabled;
+
+ LocationAdasEnabledEvent(long timeDelta, int userId, boolean enabled) {
+ super(timeDelta);
+ mUserId = userId;
+ mEnabled = enabled;
+ }
+
+ @Override
+ public String getLogString() {
+ return "adas location [u" + mUserId + "] " + (mEnabled ? "enabled" : "disabled");
+ }
+ }
+
/**
* Aggregate statistics for a single package under a single provider.
*/
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 1cccf08..f3dcfbb 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -771,10 +771,10 @@
boolean enabled = mContext.getSystemService(LocationManager.class)
.isLocationEnabledForUser(UserHandle.CURRENT);
- // .. but enable anyway, if there's an active settings-ignored request (e.g. ELS)
+ // .. but enable anyway, if there's an active bypass request (e.g. ELS or ADAS)
enabled |= (mProviderRequest != null
&& mProviderRequest.isActive()
- && mProviderRequest.isLocationSettingsIgnored());
+ && mProviderRequest.isBypass());
// ... and, finally, disable anyway, if device is being shut down
enabled &= !mShutdown;
diff --git a/services/core/java/com/android/server/location/injector/Injector.java b/services/core/java/com/android/server/location/injector/Injector.java
index b035118..173fd13 100644
--- a/services/core/java/com/android/server/location/injector/Injector.java
+++ b/services/core/java/com/android/server/location/injector/Injector.java
@@ -17,6 +17,7 @@
package com.android.server.location.injector;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.location.settings.LocationSettings;
/**
* Injects various location dependencies so that they may be controlled by tests.
@@ -27,6 +28,9 @@
/** Returns a UserInfoHelper. */
UserInfoHelper getUserInfoHelper();
+ /** Returns a LocationSettings. */
+ LocationSettings getLocationSettings();
+
/** Returns an AlarmHelper. */
AlarmHelper getAlarmHelper();
diff --git a/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java b/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java
index c315da4..3e8da7d 100644
--- a/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java
+++ b/services/core/java/com/android/server/location/injector/SystemSettingsHelper.java
@@ -670,8 +670,6 @@
}
}
-
-
private static class PackageTagsListSetting extends DeviceConfigSetting {
private final Supplier<ArrayMap<String, ArraySet<String>>> mBaseValuesSupplier;
diff --git a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
index 1da45bd..eb7b77a 100644
--- a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
@@ -263,10 +263,10 @@
}
/**
- * The current allowed state of this provider.
+ * The current state of the provider.
*/
- public final boolean isAllowed() {
- return mInternalState.get().state.allowed;
+ public final State getState() {
+ return mInternalState.get().state;
}
/**
@@ -277,13 +277,6 @@
}
/**
- * The current provider properties of this provider.
- */
- public final @Nullable ProviderProperties getProperties() {
- return mInternalState.get().state.properties;
- }
-
- /**
* Call this method to report a change in provider properties.
*/
protected void setProperties(@Nullable ProviderProperties properties) {
@@ -291,13 +284,6 @@
}
/**
- * The current identity of this provider.
- */
- public final @Nullable CallerIdentity getIdentity() {
- return mInternalState.get().state.identity;
- }
-
- /**
* Call this method to report a change in the provider's identity.
*/
protected void setIdentity(@Nullable CallerIdentity identity) {
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 4f8b87b..43886f7 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -113,6 +113,8 @@
import com.android.server.location.injector.UserInfoHelper.UserListener;
import com.android.server.location.listeners.ListenerMultiplexer;
import com.android.server.location.listeners.RemoteListenerRegistration;
+import com.android.server.location.settings.LocationSettings;
+import com.android.server.location.settings.LocationUserSettings;
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
@@ -549,6 +551,19 @@
}
@GuardedBy("mLock")
+ final boolean onAdasGnssLocationEnabledChanged(int userId) {
+ if (Build.IS_DEBUGGABLE) {
+ Preconditions.checkState(Thread.holdsLock(mLock));
+ }
+
+ if (getIdentity().getUserId() == userId) {
+ return onProviderLocationRequestChanged();
+ }
+
+ return false;
+ }
+
+ @GuardedBy("mLock")
final boolean onForegroundChanged(int uid, boolean foreground) {
if (Build.IS_DEBUGGABLE) {
Preconditions.checkState(Thread.holdsLock(mLock));
@@ -592,8 +607,8 @@
onHighPowerUsageChanged();
updateService();
- // if location settings ignored has changed then the active state may have changed
- return oldRequest.isLocationSettingsIgnored() != newRequest.isLocationSettingsIgnored();
+ // if bypass state has changed then the active state may have changed
+ return oldRequest.isBypass() != newRequest.isBypass();
}
private LocationRequest calculateProviderLocationRequest() {
@@ -616,9 +631,24 @@
if (!mSettingsHelper.getIgnoreSettingsAllowlist().contains(
getIdentity().getPackageName(), getIdentity().getAttributionTag())
&& !mLocationManagerInternal.isProvider(null, getIdentity())) {
- builder.setLocationSettingsIgnored(false);
locationSettingsIgnored = false;
}
+
+ builder.setLocationSettingsIgnored(locationSettingsIgnored);
+ }
+
+ boolean adasGnssBypass = baseRequest.isAdasGnssBypass();
+ if (adasGnssBypass) {
+ // if we are not currently allowed use adas gnss bypass, disable it
+ if (!GPS_PROVIDER.equals(mName)) {
+ Log.e(TAG, "adas gnss bypass request received in non-gps provider");
+ adasGnssBypass = false;
+ } else if (!mLocationSettings.getUserSettings(
+ getIdentity().getUserId()).isAdasGnssLocationEnabled()) {
+ adasGnssBypass = false;
+ }
+
+ builder.setAdasGnssBypass(adasGnssBypass);
}
if (!locationSettingsIgnored && !isThrottlingExempt()) {
@@ -769,7 +799,7 @@
Location lastLocation = getLastLocationUnsafe(
getIdentity().getUserId(),
getPermissionLevel(),
- getRequest().isLocationSettingsIgnored(),
+ getRequest().isBypass(),
maxLocationAgeMs);
if (lastLocation != null) {
executeOperation(acceptLocationChange(LocationResult.wrap(lastLocation)));
@@ -1114,7 +1144,7 @@
Location lastLocation = getLastLocationUnsafe(
getIdentity().getUserId(),
getPermissionLevel(),
- getRequest().isLocationSettingsIgnored(),
+ getRequest().isBypass(),
MAX_CURRENT_LOCATION_AGE_MS);
if (lastLocation != null) {
executeOperation(acceptLocationChange(LocationResult.wrap(lastLocation)));
@@ -1267,6 +1297,7 @@
private final CopyOnWriteArrayList<IProviderRequestListener> mProviderRequestListeners;
protected final LocationManagerInternal mLocationManagerInternal;
+ protected final LocationSettings mLocationSettings;
protected final SettingsHelper mSettingsHelper;
protected final UserInfoHelper mUserHelper;
protected final AlarmHelper mAlarmHelper;
@@ -1280,6 +1311,8 @@
protected final LocationFudger mLocationFudger;
private final UserListener mUserChangedListener = this::onUserChanged;
+ private final LocationSettings.LocationUserSettingsListener mLocationUserSettingsListener =
+ this::onLocationUserSettingsChanged;
private final UserSettingChangedListener mLocationEnabledChangedListener =
this::onLocationEnabledChanged;
private final GlobalSettingChangedListener mBackgroundThrottlePackageWhitelistChangedListener =
@@ -1332,6 +1365,7 @@
mLocationManagerInternal = Objects.requireNonNull(
LocalServices.getService(LocationManagerInternal.class));
+ mLocationSettings = injector.getLocationSettings();
mSettingsHelper = injector.getSettingsHelper();
mUserHelper = injector.getUserInfoHelper();
mAlarmHelper = injector.getAlarmHelper();
@@ -1362,6 +1396,7 @@
mStateChangedListener = listener;
mUserHelper.addListener(mUserChangedListener);
+ mLocationSettings.registerLocationUserSettingsListener(mLocationUserSettingsListener);
mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
final long identity = Binder.clearCallingIdentity();
@@ -1389,6 +1424,7 @@
}
mUserHelper.removeListener(mUserChangedListener);
+ mLocationSettings.unregisterLocationUserSettingsListener(mLocationUserSettingsListener);
mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
// if external entities are registering listeners it's their responsibility to
@@ -1407,12 +1443,16 @@
return mName;
}
+ public AbstractLocationProvider.State getState() {
+ return mProvider.getState();
+ }
+
public @Nullable CallerIdentity getIdentity() {
- return mProvider.getIdentity();
+ return mProvider.getState().identity;
}
public @Nullable ProviderProperties getProperties() {
- return mProvider.getProperties();
+ return mProvider.getState().properties;
}
public boolean hasProvider() {
@@ -1546,7 +1586,7 @@
public @Nullable Location getLastLocation(LastLocationRequest request,
CallerIdentity identity, @PermissionLevel int permissionLevel) {
- if (!isActive(request.isLocationSettingsIgnored(), identity)) {
+ if (!isActive(request.isBypass(), identity)) {
return null;
}
@@ -1560,7 +1600,7 @@
getLastLocationUnsafe(
identity.getUserId(),
permissionLevel,
- request.isLocationSettingsIgnored(),
+ request.isBypass(),
Long.MAX_VALUE),
permissionLevel);
@@ -1580,7 +1620,7 @@
* location if necessary.
*/
public @Nullable Location getLastLocationUnsafe(int userId,
- @PermissionLevel int permissionLevel, boolean ignoreLocationSettings,
+ @PermissionLevel int permissionLevel, boolean isBypass,
long maximumAgeMs) {
if (userId == UserHandle.USER_ALL) {
// find the most recent location across all users
@@ -1588,7 +1628,7 @@
final int[] runningUserIds = mUserHelper.getRunningUserIds();
for (int i = 0; i < runningUserIds.length; i++) {
Location next = getLastLocationUnsafe(runningUserIds[i], permissionLevel,
- ignoreLocationSettings, maximumAgeMs);
+ isBypass, maximumAgeMs);
if (lastLocation == null || (next != null && next.getElapsedRealtimeNanos()
> lastLocation.getElapsedRealtimeNanos())) {
lastLocation = next;
@@ -1597,7 +1637,7 @@
return lastLocation;
} else if (userId == UserHandle.USER_CURRENT) {
return getLastLocationUnsafe(mUserHelper.getCurrentUserId(), permissionLevel,
- ignoreLocationSettings, maximumAgeMs);
+ isBypass, maximumAgeMs);
}
Preconditions.checkArgument(userId >= 0);
@@ -1609,7 +1649,7 @@
if (lastLocation == null) {
location = null;
} else {
- location = lastLocation.get(permissionLevel, ignoreLocationSettings);
+ location = lastLocation.get(permissionLevel, isBypass);
}
}
@@ -1921,7 +1961,7 @@
// provider, under the assumption that once we send the request off, the provider will
// immediately attempt to deliver a new location satisfying that request.
long delayMs;
- if (!oldRequest.isLocationSettingsIgnored() && newRequest.isLocationSettingsIgnored()) {
+ if (!oldRequest.isBypass() && newRequest.isBypass()) {
delayMs = 0;
} else if (newRequest.getIntervalMillis() > oldRequest.getIntervalMillis()) {
// if the interval has increased, tell the provider immediately, so it can save power
@@ -1998,12 +2038,12 @@
return false;
}
- boolean locationSettingsIgnored = registration.getRequest().isLocationSettingsIgnored();
- if (!isActive(locationSettingsIgnored, registration.getIdentity())) {
+ boolean isBypass = registration.getRequest().isBypass();
+ if (!isActive(isBypass, registration.getIdentity())) {
return false;
}
- if (!locationSettingsIgnored) {
+ if (!isBypass) {
switch (mLocationPowerSaveModeHelper.getLocationPowerSaveMode()) {
case LOCATION_MODE_FOREGROUND_ONLY:
if (!registration.isForeground()) {
@@ -2032,15 +2072,15 @@
return true;
}
- private boolean isActive(boolean locationSettingsIgnored, CallerIdentity identity) {
+ private boolean isActive(boolean isBypass, CallerIdentity identity) {
if (identity.isSystemServer()) {
- if (!locationSettingsIgnored) {
+ if (!isBypass) {
if (!isEnabled(mUserHelper.getCurrentUserId())) {
return false;
}
}
} else {
- if (!locationSettingsIgnored) {
+ if (!isBypass) {
if (!isEnabled(identity.getUserId())) {
return false;
}
@@ -2067,6 +2107,7 @@
long intervalMs = ProviderRequest.INTERVAL_DISABLED;
int quality = LocationRequest.QUALITY_LOW_POWER;
long maxUpdateDelayMs = Long.MAX_VALUE;
+ boolean adasGnssBypass = false;
boolean locationSettingsIgnored = false;
boolean lowPower = true;
@@ -2082,6 +2123,7 @@
intervalMs = min(request.getIntervalMillis(), intervalMs);
quality = min(request.getQuality(), quality);
maxUpdateDelayMs = min(request.getMaxUpdateDelayMillis(), maxUpdateDelayMs);
+ adasGnssBypass |= request.isAdasGnssBypass();
locationSettingsIgnored |= request.isLocationSettingsIgnored();
lowPower &= request.isLowPower();
}
@@ -2119,6 +2161,7 @@
.setIntervalMillis(intervalMs)
.setQuality(quality)
.setMaxUpdateDelayMillis(maxUpdateDelayMs)
+ .setAdasGnssBypass(adasGnssBypass)
.setLocationSettingsIgnored(locationSettingsIgnored)
.setLowPower(lowPower)
.setWorkSource(workSource)
@@ -2187,6 +2230,16 @@
}
}
+ private void onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings,
+ LocationUserSettings newSettings) {
+ if (oldSettings.isAdasGnssLocationEnabled() != newSettings.isAdasGnssLocationEnabled()) {
+ synchronized (mLock) {
+ updateRegistrations(
+ registration -> registration.onAdasGnssLocationEnabledChanged(userId));
+ }
+ }
+ }
+
private void onLocationEnabledChanged(int userId) {
synchronized (mLock) {
if (mState == STATE_STOPPED) {
@@ -2403,7 +2456,7 @@
Preconditions.checkArgument(userId >= 0);
boolean enabled = mState == STATE_STARTED
- && mProvider.isAllowed()
+ && mProvider.getState().allowed
&& mSettingsHelper.isLocationEnabled(userId);
int index = mEnabled.indexOfKey(userId);
@@ -2556,16 +2609,16 @@
}
public @Nullable Location get(@PermissionLevel int permissionLevel,
- boolean ignoreLocationSettings) {
+ boolean isBypass) {
switch (permissionLevel) {
case PERMISSION_FINE:
- if (ignoreLocationSettings) {
+ if (isBypass) {
return mFineBypassLocation;
} else {
return mFineLocation;
}
case PERMISSION_COARSE:
- if (ignoreLocationSettings) {
+ if (isBypass) {
return mCoarseBypassLocation;
} else {
return mCoarseLocation;
diff --git a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
index 021e8db..22295fe 100644
--- a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
@@ -21,9 +21,7 @@
import android.annotation.Nullable;
import android.location.Location;
import android.location.LocationResult;
-import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
-import android.location.util.identity.CallerIdentity;
import android.os.Bundle;
import com.android.internal.annotations.GuardedBy;
@@ -32,7 +30,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collections;
-import java.util.Set;
/**
* Represents a location provider that may switch between a mock implementation and a real
@@ -290,21 +287,21 @@
Preconditions.checkState(!Thread.holdsLock(mOwnerLock));
AbstractLocationProvider provider;
+ State providerState;
synchronized (mOwnerLock) {
provider = mProvider;
- pw.println("allowed=" + isAllowed());
- CallerIdentity identity = getIdentity();
- if (identity != null) {
- pw.println("identity=" + identity);
- }
- Set<String> extraAttributionTags = getExtraAttributionTags();
- if (!extraAttributionTags.isEmpty()) {
- pw.println("extra attribution tags=" + extraAttributionTags);
- }
- ProviderProperties properties = getProperties();
- if (properties != null) {
- pw.println("properties=" + properties);
- }
+ providerState = getState();
+ }
+
+ pw.println("allowed=" + providerState.allowed);
+ if (providerState.identity != null) {
+ pw.println("identity=" + providerState.identity);
+ }
+ if (!providerState.extraAttributionTags.isEmpty()) {
+ pw.println("extra attribution tags=" + providerState.extraAttributionTags);
+ }
+ if (providerState.properties != null) {
+ pw.println("properties=" + providerState.properties);
}
if (provider != null) {
diff --git a/services/core/java/com/android/server/location/settings/LocationSettings.java b/services/core/java/com/android/server/location/settings/LocationSettings.java
new file mode 100644
index 0000000..d521538
--- /dev/null
+++ b/services/core/java/com/android/server/location/settings/LocationSettings.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.settings;
+
+import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
+
+import android.content.Context;
+import android.os.Environment;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.FgThread;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Function;
+
+/**
+ * Accessor for location user settings. Ensure there is only ever one instance as multiple instances
+ * don't play nicely with each other.
+ */
+public class LocationSettings {
+
+ /** Listens for changes to location user settings. */
+ public interface LocationUserSettingsListener {
+ /** Invoked when location user settings have changed for the given user. */
+ void onLocationUserSettingsChanged(int userId, LocationUserSettings oldSettings,
+ LocationUserSettings newSettings);
+ }
+
+ private static final String LOCATION_DIRNAME = "location";
+ private static final String LOCATION_SETTINGS_FILENAME = "settings";
+
+ final Context mContext;
+
+ @GuardedBy("mUserSettings")
+ private final SparseArray<LocationUserSettingsStore> mUserSettings;
+ private final CopyOnWriteArrayList<LocationUserSettingsListener> mUserSettingsListeners;
+
+ public LocationSettings(Context context) {
+ mContext = context;
+ mUserSettings = new SparseArray<>(1);
+ mUserSettingsListeners = new CopyOnWriteArrayList<>();
+ }
+
+ /** Registers a listener for changes to location user settings. */
+ public final void registerLocationUserSettingsListener(LocationUserSettingsListener listener) {
+ mUserSettingsListeners.add(listener);
+ }
+
+ /** Unregisters a listener for changes to location user settings. */
+ public final void unregisterLocationUserSettingsListener(
+ LocationUserSettingsListener listener) {
+ mUserSettingsListeners.remove(listener);
+ }
+
+ protected File getUserSettingsDir(int userId) {
+ return Environment.getDataSystemDeDirectory(userId);
+ }
+
+ protected LocationUserSettingsStore createUserSettingsStore(int userId, File file) {
+ return new LocationUserSettingsStore(userId, file);
+ }
+
+ private LocationUserSettingsStore getUserSettingsStore(int userId) {
+ synchronized (mUserSettings) {
+ LocationUserSettingsStore settingsStore = mUserSettings.get(userId);
+ if (settingsStore == null) {
+ File file = new File(new File(getUserSettingsDir(userId), LOCATION_DIRNAME),
+ LOCATION_SETTINGS_FILENAME);
+ settingsStore = createUserSettingsStore(userId, file);
+ mUserSettings.put(userId, settingsStore);
+ }
+ return settingsStore;
+ }
+ }
+
+ /** Retrieves the current state of location user settings. */
+ public final LocationUserSettings getUserSettings(int userId) {
+ return getUserSettingsStore(userId).get();
+ }
+
+ /** Updates the current state of location user settings for the given user. */
+ public final void updateUserSettings(int userId,
+ Function<LocationUserSettings, LocationUserSettings> updater) {
+ getUserSettingsStore(userId).update(updater);
+ }
+
+ @VisibleForTesting
+ final void flushFiles() throws InterruptedException {
+ synchronized (mUserSettings) {
+ int size = mUserSettings.size();
+ for (int i = 0; i < size; i++) {
+ mUserSettings.valueAt(i).flushFile();
+ }
+ }
+ }
+
+ @VisibleForTesting
+ final void deleteFiles() throws InterruptedException {
+ synchronized (mUserSettings) {
+ int size = mUserSettings.size();
+ for (int i = 0; i < size; i++) {
+ mUserSettings.valueAt(i).deleteFile();
+ }
+ }
+ }
+
+ protected final void fireListeners(int userId, LocationUserSettings oldSettings,
+ LocationUserSettings newSettings) {
+ for (LocationUserSettingsListener listener : mUserSettingsListeners) {
+ listener.onLocationUserSettingsChanged(userId, oldSettings, newSettings);
+ }
+ }
+
+ class LocationUserSettingsStore extends SettingsStore<LocationUserSettings> {
+
+ protected final int mUserId;
+
+ LocationUserSettingsStore(int userId, File file) {
+ super(file);
+ mUserId = userId;
+ }
+
+ @Override
+ protected LocationUserSettings read(int version, DataInput in) throws IOException {
+ return filterSettings(LocationUserSettings.read(mContext.getResources(), version, in));
+ }
+
+ @Override
+ protected void write(DataOutput out, LocationUserSettings settings) throws IOException {
+ settings.write(out);
+ }
+
+ @Override
+ public void update(Function<LocationUserSettings, LocationUserSettings> updater) {
+ super.update(settings -> filterSettings(updater.apply(settings)));
+ }
+
+ @Override
+ protected void onChange(LocationUserSettings oldSettings,
+ LocationUserSettings newSettings) {
+ FgThread.getExecutor().execute(() -> fireListeners(mUserId, oldSettings, newSettings));
+ }
+
+ private LocationUserSettings filterSettings(LocationUserSettings settings) {
+ if (settings.isAdasGnssLocationEnabled()
+ && !mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE)) {
+ // prevent non-automotive devices from ever enabling this
+ settings = settings.withAdasGnssLocationEnabled(false);
+ }
+ return settings;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/settings/LocationUserSettings.java b/services/core/java/com/android/server/location/settings/LocationUserSettings.java
new file mode 100644
index 0000000..283255e
--- /dev/null
+++ b/services/core/java/com/android/server/location/settings/LocationUserSettings.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.settings;
+
+import android.content.res.Resources;
+
+import com.android.internal.R;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Objects;
+
+/** Holds the state of location user settings. */
+public final class LocationUserSettings implements SettingsStore.VersionedSettings {
+
+ // remember to bump this version code and add the appropriate upgrade logic whenever the format
+ // is changed.
+ private static final int VERSION = 1;
+
+ private final boolean mAdasGnssLocationEnabled;
+
+ private LocationUserSettings(boolean adasGnssLocationEnabled) {
+ mAdasGnssLocationEnabled = adasGnssLocationEnabled;
+ }
+
+ @Override
+ public int getVersion() {
+ return VERSION;
+ }
+
+ public boolean isAdasGnssLocationEnabled() {
+ return mAdasGnssLocationEnabled;
+ }
+
+ /** Returns an instance with ADAS GNSS location enabled state set as given. */
+ public LocationUserSettings withAdasGnssLocationEnabled(boolean adasEnabled) {
+ if (adasEnabled == mAdasGnssLocationEnabled) {
+ return this;
+ }
+
+ return new LocationUserSettings(adasEnabled);
+ }
+
+ void write(DataOutput out) throws IOException {
+ out.writeBoolean(mAdasGnssLocationEnabled);
+ }
+
+ static LocationUserSettings read(Resources resources, int version, DataInput in)
+ throws IOException {
+ boolean adasGnssLocationEnabled;
+
+ // upgrade code goes here. remember to bump the version field when changing the format
+ switch (version) {
+ default:
+ // set all fields to defaults
+ adasGnssLocationEnabled = resources.getBoolean(
+ R.bool.config_defaultAdasGnssLocationEnabled);
+ break;
+ case 1:
+ adasGnssLocationEnabled = in.readBoolean();
+ // fall through
+ }
+
+ return new LocationUserSettings(adasGnssLocationEnabled);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof LocationUserSettings)) {
+ return false;
+ }
+ LocationUserSettings that = (LocationUserSettings) o;
+ return mAdasGnssLocationEnabled == that.mAdasGnssLocationEnabled;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mAdasGnssLocationEnabled);
+ }
+}
diff --git a/services/core/java/com/android/server/location/settings/SettingsStore.java b/services/core/java/com/android/server/location/settings/SettingsStore.java
new file mode 100644
index 0000000..01338a3
--- /dev/null
+++ b/services/core/java/com/android/server/location/settings/SettingsStore.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.settings;
+
+import static com.android.server.location.LocationManagerService.TAG;
+import static com.android.server.location.settings.SettingsStore.VersionedSettings.VERSION_DOES_NOT_EXIST;
+
+import android.util.AtomicFile;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.DataOutput;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Function;
+
+/** Base class for read/write/versioning functionality for storing persistent settings to a file. */
+abstract class SettingsStore<T extends SettingsStore.VersionedSettings> {
+
+ interface VersionedSettings {
+ /** Represents that the settings do not exist. */
+ int VERSION_DOES_NOT_EXIST = Integer.MAX_VALUE;
+
+ /** Must always return a version number less than {@link #VERSION_DOES_NOT_EXIST}. */
+ int getVersion();
+ }
+
+ private final AtomicFile mFile;
+
+ @GuardedBy("this")
+ private boolean mInitialized;
+ @GuardedBy("this")
+ private T mCache;
+
+ protected SettingsStore(File file) {
+ mFile = new AtomicFile(file);
+ }
+
+ /**
+ * Must be implemented to read in a settings instance, and upgrade to the appropriate version
+ * where necessary. If the provided version is {@link VersionedSettings#VERSION_DOES_NOT_EXIST}
+ * then the DataInput will be empty, and the method should return a settings instance with all
+ * settings set to the default value.
+ */
+ protected abstract T read(int version, DataInput in) throws IOException;
+
+ /**
+ * Must be implemented to write the given settings to the given DataOutput.
+ */
+ protected abstract void write(DataOutput out, T settings) throws IOException;
+
+ /**
+ * Invoked when settings change, and while holding the internal lock. If used to invoke
+ * listeners, ensure they are not invoked while holding the lock (ie, asynchronously).
+ */
+ protected abstract void onChange(T oldSettings, T newSettings);
+
+ public final synchronized void initializeCache() {
+ if (!mInitialized) {
+ if (mFile.exists()) {
+ try (DataInputStream is = new DataInputStream(mFile.openRead())) {
+ mCache = read(is.readInt(), is);
+ Preconditions.checkState(mCache.getVersion() < VERSION_DOES_NOT_EXIST);
+ } catch (IOException e) {
+ Log.e(TAG, "error reading location settings (" + mFile
+ + "), falling back to defaults", e);
+ }
+ }
+
+ if (mCache == null) {
+ try {
+ mCache = read(VERSION_DOES_NOT_EXIST,
+ new DataInputStream(new ByteArrayInputStream(new byte[0])));
+ Preconditions.checkState(mCache.getVersion() < VERSION_DOES_NOT_EXIST);
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ mInitialized = true;
+ }
+ }
+
+ public final synchronized T get() {
+ initializeCache();
+ return mCache;
+ }
+
+ public synchronized void update(Function<T, T> updater) {
+ initializeCache();
+
+ T oldSettings = mCache;
+ T newSettings = Objects.requireNonNull(updater.apply(oldSettings));
+ if (oldSettings.equals(newSettings)) {
+ return;
+ }
+
+ mCache = newSettings;
+ Preconditions.checkState(mCache.getVersion() < VERSION_DOES_NOT_EXIST);
+
+ writeLazily(newSettings);
+
+ onChange(oldSettings, newSettings);
+ }
+
+ @VisibleForTesting
+ synchronized void flushFile() throws InterruptedException {
+ CountDownLatch latch = new CountDownLatch(1);
+ BackgroundThread.getExecutor().execute(latch::countDown);
+ latch.await();
+ }
+
+ @VisibleForTesting
+ synchronized void deleteFile() throws InterruptedException {
+ CountDownLatch latch = new CountDownLatch(1);
+ BackgroundThread.getExecutor().execute(() -> {
+ mFile.delete();
+ latch.countDown();
+ });
+ latch.await();
+ }
+
+ private void writeLazily(T settings) {
+ BackgroundThread.getExecutor().execute(() -> {
+ FileOutputStream os = null;
+ try {
+ os = mFile.startWrite();
+ DataOutputStream out = new DataOutputStream(os);
+ out.writeInt(settings.getVersion());
+ write(out, settings);
+ mFile.finishWrite(os);
+ } catch (IOException e) {
+ mFile.failWrite(os);
+ Log.e(TAG, "failure serializing location settings", e);
+ } catch (Throwable e) {
+ mFile.failWrite(os);
+ throw e;
+ }
+ });
+ }
+}
diff --git a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
index 2cc2ebf..981e759 100644
--- a/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
+++ b/services/core/java/com/android/server/media/metrics/MediaMetricsManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.media.metrics;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.media.metrics.IMediaMetricsManager;
import android.media.metrics.NetworkEvent;
import android.media.metrics.PlaybackErrorEvent;
@@ -24,19 +25,60 @@
import android.media.metrics.PlaybackStateEvent;
import android.media.metrics.TrackChangeEvent;
import android.os.Binder;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfig.Properties;
import android.util.Base64;
+import android.util.Slog;
import android.util.StatsEvent;
import android.util.StatsLog;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;
import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.List;
/**
* System service manages media metrics.
*/
public final class MediaMetricsManagerService extends SystemService {
+ private static final String TAG = "MediaMetricsManagerService";
+
+ private static final String MEDIA_METRICS_MODE = "media_metrics_mode";
+ private static final String PLAYER_METRICS_PER_APP_ATTRIBUTION_ALLOWLIST =
+ "player_metrics_per_app_attribution_allowlist";
+ private static final String PLAYER_METRICS_APP_ALLOWLIST = "player_metrics_app_allowlist";
+
+ private static final String PLAYER_METRICS_PER_APP_ATTRIBUTION_BLOCKLIST =
+ "player_metrics_per_app_attribution_blocklist";
+ private static final String PLAYER_METRICS_APP_BLOCKLIST = "player_metrics_app_blocklist";
+
+ private static final int MEDIA_METRICS_MODE_OFF = 0;
+ private static final int MEDIA_METRICS_MODE_ON = 1;
+ private static final int MEDIA_METRICS_MODE_BLOCKLIST = 2;
+ private static final int MEDIA_METRICS_MODE_ALLOWLIST = 3;
+
+ // Cascading logging levels. The higher value, the more constrains (less logging data).
+ // The unused values between 2 consecutive levels are reserved for potential extra levels.
+ private static final int LOGGING_LEVEL_EVERYTHING = 0;
+ private static final int LOGGING_LEVEL_NO_UID = 1000;
+ private static final int LOGGING_LEVEL_BLOCKED = 99999;
+
+ private static final String FAILED_TO_GET = "failed_to_get";
private final SecureRandom mSecureRandom;
+ @GuardedBy("mLock")
+ private Integer mMode = null;
+ @GuardedBy("mLock")
+ private List<String> mAllowlist = null;
+ @GuardedBy("mLock")
+ private List<String> mNoUidAllowlist = null;
+ @GuardedBy("mLock")
+ private List<String> mBlockList = null;
+ @GuardedBy("mLock")
+ private List<String> mNoUidBlocklist = null;
+ private final Object mLock = new Object();
+ private final Context mContext;
/**
* Initializes the playback metrics manager service.
@@ -45,20 +87,73 @@
*/
public MediaMetricsManagerService(Context context) {
super(context);
+ mContext = context;
mSecureRandom = new SecureRandom();
}
@Override
public void onStart() {
publishBinderService(Context.MEDIA_METRICS_SERVICE, new BinderService());
+ DeviceConfig.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_MEDIA,
+ mContext.getMainExecutor(),
+ this::updateConfigs);
+ }
+
+ private void updateConfigs(Properties properties) {
+ synchronized (mLock) {
+ mMode = properties.getInt(
+ MEDIA_METRICS_MODE,
+ MEDIA_METRICS_MODE_BLOCKLIST);
+ List<String> newList = getListLocked(PLAYER_METRICS_APP_ALLOWLIST);
+ if (newList != null || mMode != MEDIA_METRICS_MODE_ALLOWLIST) {
+ // don't overwrite the list if the mode IS MEDIA_METRICS_MODE_ALLOWLIST
+ // but failed to get
+ mAllowlist = newList;
+ }
+ newList = getListLocked(PLAYER_METRICS_PER_APP_ATTRIBUTION_ALLOWLIST);
+ if (newList != null || mMode != MEDIA_METRICS_MODE_ALLOWLIST) {
+ mNoUidAllowlist = newList;
+ }
+ newList = getListLocked(PLAYER_METRICS_APP_BLOCKLIST);
+ if (newList != null || mMode != MEDIA_METRICS_MODE_BLOCKLIST) {
+ mBlockList = newList;
+ }
+ newList = getListLocked(PLAYER_METRICS_PER_APP_ATTRIBUTION_BLOCKLIST);
+ if (newList != null || mMode != MEDIA_METRICS_MODE_BLOCKLIST) {
+ mNoUidBlocklist = newList;
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private List<String> getListLocked(String listName) {
+ final long identity = Binder.clearCallingIdentity();
+ String listString = FAILED_TO_GET;
+ try {
+ listString = DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_MEDIA, listName, FAILED_TO_GET);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ if (listString.equals(FAILED_TO_GET)) {
+ Slog.d(TAG, "failed to get " + listName + " from DeviceConfig");
+ return null;
+ }
+ String[] pkgArr = listString.split(",");
+ return Arrays.asList(pkgArr);
}
private final class BinderService extends IMediaMetricsManager.Stub {
@Override
public void reportPlaybackMetrics(String sessionId, PlaybackMetrics metrics, int userId) {
+ int level = loggingLevel();
+ if (level == LOGGING_LEVEL_BLOCKED) {
+ return;
+ }
StatsEvent statsEvent = StatsEvent.newBuilder()
.setAtomId(320)
- .writeInt(Binder.getCallingUid())
+ .writeInt(level == LOGGING_LEVEL_EVERYTHING ? Binder.getCallingUid() : 0)
.writeString(sessionId)
.writeLong(metrics.getMediaDurationMillis())
.writeInt(metrics.getStreamSource())
@@ -85,6 +180,10 @@
@Override
public void reportPlaybackStateEvent(
String sessionId, PlaybackStateEvent event, int userId) {
+ int level = loggingLevel();
+ if (level == LOGGING_LEVEL_BLOCKED) {
+ return;
+ }
StatsEvent statsEvent = StatsEvent.newBuilder()
.setAtomId(322)
.writeString(sessionId)
@@ -116,6 +215,10 @@
@Override
public void reportPlaybackErrorEvent(
String sessionId, PlaybackErrorEvent event, int userId) {
+ int level = loggingLevel();
+ if (level == LOGGING_LEVEL_BLOCKED) {
+ return;
+ }
StatsEvent statsEvent = StatsEvent.newBuilder()
.setAtomId(323)
.writeString(sessionId)
@@ -130,6 +233,10 @@
public void reportNetworkEvent(
String sessionId, NetworkEvent event, int userId) {
+ int level = loggingLevel();
+ if (level == LOGGING_LEVEL_BLOCKED) {
+ return;
+ }
StatsEvent statsEvent = StatsEvent.newBuilder()
.setAtomId(321)
.writeString(sessionId)
@@ -143,6 +250,10 @@
@Override
public void reportTrackChangeEvent(
String sessionId, TrackChangeEvent event, int userId) {
+ int level = loggingLevel();
+ if (level == LOGGING_LEVEL_BLOCKED) {
+ return;
+ }
StatsEvent statsEvent = StatsEvent.newBuilder()
.setAtomId(324)
.writeString(sessionId)
@@ -165,5 +276,140 @@
.build();
StatsLog.write(statsEvent);
}
+
+ private int loggingLevel() {
+ synchronized (mLock) {
+ int uid = Binder.getCallingUid();
+
+ if (mMode == null) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mMode = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_MEDIA,
+ MEDIA_METRICS_MODE,
+ MEDIA_METRICS_MODE_BLOCKLIST);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ if (mMode == MEDIA_METRICS_MODE_ON) {
+ return LOGGING_LEVEL_EVERYTHING;
+ }
+ if (mMode == MEDIA_METRICS_MODE_OFF) {
+ return LOGGING_LEVEL_BLOCKED;
+ }
+
+ PackageManager pm = getContext().getPackageManager();
+ String[] packages = pm.getPackagesForUid(uid);
+ if (packages == null || packages.length == 0) {
+ // The valid application UID range is from
+ // android.os.Process.FIRST_APPLICATION_UID to
+ // android.os.Process.LAST_APPLICATION_UID.
+ // UIDs outside this range will not have a package.
+ Slog.d(TAG, "empty package from uid " + uid);
+ // block the data if the mode is MEDIA_METRICS_MODE_ALLOWLIST
+ return mMode == MEDIA_METRICS_MODE_BLOCKLIST
+ ? LOGGING_LEVEL_NO_UID : LOGGING_LEVEL_BLOCKED;
+ }
+ if (mMode == MEDIA_METRICS_MODE_BLOCKLIST) {
+ if (mBlockList == null) {
+ mBlockList = getListLocked(PLAYER_METRICS_APP_BLOCKLIST);
+ if (mBlockList == null) {
+ // failed to get the blocklist. Block it.
+ return LOGGING_LEVEL_BLOCKED;
+ }
+ }
+ Integer level = loggingLevelInternal(
+ packages, mBlockList, PLAYER_METRICS_APP_BLOCKLIST);
+ if (level != null) {
+ return level;
+ }
+ if (mNoUidBlocklist == null) {
+ mNoUidBlocklist =
+ getListLocked(PLAYER_METRICS_PER_APP_ATTRIBUTION_BLOCKLIST);
+ if (mNoUidBlocklist == null) {
+ // failed to get the blocklist. Block it.
+ return LOGGING_LEVEL_BLOCKED;
+ }
+ }
+ level = loggingLevelInternal(
+ packages,
+ mNoUidBlocklist,
+ PLAYER_METRICS_PER_APP_ATTRIBUTION_BLOCKLIST);
+ if (level != null) {
+ return level;
+ }
+ // Not detected in any blocklist. Log everything.
+ return LOGGING_LEVEL_EVERYTHING;
+ }
+ if (mMode == MEDIA_METRICS_MODE_ALLOWLIST) {
+ if (mNoUidAllowlist == null) {
+ mNoUidAllowlist =
+ getListLocked(PLAYER_METRICS_PER_APP_ATTRIBUTION_ALLOWLIST);
+ if (mNoUidAllowlist == null) {
+ // failed to get the allowlist. Block it.
+ return LOGGING_LEVEL_BLOCKED;
+ }
+ }
+ Integer level = loggingLevelInternal(
+ packages,
+ mNoUidAllowlist,
+ PLAYER_METRICS_PER_APP_ATTRIBUTION_ALLOWLIST);
+ if (level != null) {
+ return level;
+ }
+ if (mAllowlist == null) {
+ mAllowlist = getListLocked(PLAYER_METRICS_APP_ALLOWLIST);
+ if (mAllowlist == null) {
+ // failed to get the allowlist. Block it.
+ return LOGGING_LEVEL_BLOCKED;
+ }
+ }
+ level = loggingLevelInternal(
+ packages, mAllowlist, PLAYER_METRICS_APP_ALLOWLIST);
+ if (level != null) {
+ return level;
+ }
+ // Not detected in any allowlist. Block.
+ return LOGGING_LEVEL_BLOCKED;
+ }
+ }
+ // Blocked by default.
+ return LOGGING_LEVEL_BLOCKED;
+ }
+
+ private Integer loggingLevelInternal(
+ String[] packages, List<String> cached, String listName) {
+ if (inList(packages, cached)) {
+ return listNameToLoggingLevel(listName);
+ }
+ return null;
+ }
+
+ private boolean inList(String[] packages, List<String> arr) {
+ for (String p : packages) {
+ for (String element : arr) {
+ if (p.equals(element)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private int listNameToLoggingLevel(String listName) {
+ switch (listName) {
+ case PLAYER_METRICS_APP_BLOCKLIST:
+ return LOGGING_LEVEL_BLOCKED;
+ case PLAYER_METRICS_APP_ALLOWLIST:
+ return LOGGING_LEVEL_EVERYTHING;
+ case PLAYER_METRICS_PER_APP_ATTRIBUTION_ALLOWLIST:
+ case PLAYER_METRICS_PER_APP_ATTRIBUTION_BLOCKLIST:
+ return LOGGING_LEVEL_NO_UID;
+ default:
+ return LOGGING_LEVEL_BLOCKED;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 21f68ae..d791bd6 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1235,11 +1235,7 @@
final private BroadcastReceiver mWifiReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- synchronized (mUidRulesFirstLock) {
- synchronized (mNetworkPoliciesSecondLock) {
- upgradeWifiMeteredOverrideAL();
- }
- }
+ upgradeWifiMeteredOverride();
// Only need to perform upgrade logic once
mContext.unregisterReceiver(this);
}
@@ -2617,34 +2613,43 @@
* Perform upgrade step of moving any user-defined meterness overrides over
* into {@link WifiConfiguration}.
*/
- @GuardedBy({"mNetworkPoliciesSecondLock", "mUidRulesFirstLock"})
- private void upgradeWifiMeteredOverrideAL() {
- boolean modified = false;
- final WifiManager wm = mContext.getSystemService(WifiManager.class);
- final List<WifiConfiguration> configs = wm.getConfiguredNetworks();
- for (int i = 0; i < mNetworkPolicy.size(); ) {
- final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
- if (policy.template.getMatchRule() == NetworkTemplate.MATCH_WIFI
- && !policy.inferred) {
- mNetworkPolicy.removeAt(i);
- modified = true;
-
- final String networkId = resolveNetworkId(policy.template.getNetworkId());
- for (WifiConfiguration config : configs) {
- if (Objects.equals(resolveNetworkId(config), networkId)) {
- Slog.d(TAG, "Found network " + networkId + "; upgrading metered hint");
- config.meteredOverride = policy.metered
- ? WifiConfiguration.METERED_OVERRIDE_METERED
- : WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
- wm.updateNetwork(config);
- }
+ private void upgradeWifiMeteredOverride() {
+ final ArrayMap<String, Boolean> wifiNetworkIds = new ArrayMap<>();
+ synchronized (mNetworkPoliciesSecondLock) {
+ for (int i = 0; i < mNetworkPolicy.size();) {
+ final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
+ if (policy.template.getMatchRule() == NetworkTemplate.MATCH_WIFI
+ && !policy.inferred) {
+ mNetworkPolicy.removeAt(i);
+ wifiNetworkIds.put(policy.template.getNetworkId(), policy.metered);
+ } else {
+ i++;
}
- } else {
- i++;
}
}
- if (modified) {
- writePolicyAL();
+
+ if (wifiNetworkIds.isEmpty()) {
+ return;
+ }
+ final WifiManager wm = mContext.getSystemService(WifiManager.class);
+ final List<WifiConfiguration> configs = wm.getConfiguredNetworks();
+ for (int i = 0; i < configs.size(); ++i) {
+ final WifiConfiguration config = configs.get(i);
+ final String networkId = resolveNetworkId(config);
+ final Boolean metered = wifiNetworkIds.get(networkId);
+ if (metered != null) {
+ Slog.d(TAG, "Found network " + networkId + "; upgrading metered hint");
+ config.meteredOverride = metered
+ ? WifiConfiguration.METERED_OVERRIDE_METERED
+ : WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
+ wm.updateNetwork(config);
+ }
+ }
+
+ synchronized (mUidRulesFirstLock) {
+ synchronized (mNetworkPoliciesSecondLock) {
+ writePolicyAL();
+ }
}
}
diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java
index 2caad50..41e067e 100644
--- a/services/core/java/com/android/server/notification/BubbleExtractor.java
+++ b/services/core/java/com/android/server/notification/BubbleExtractor.java
@@ -167,20 +167,20 @@
// TODO: check the shortcut intent / ensure it can show in activity view
return true;
}
- return canLaunchInActivityView(mContext, metadata.getIntent(), pkg);
+ return canLaunchInTaskView(mContext, metadata.getIntent(), pkg);
}
/**
* Whether an intent is properly configured to display in an {@link
- * android.app.ActivityView} for bubbling.
+ * com.android.wm.shell.TaskView} for bubbling.
*
* @param context the context to use.
* @param pendingIntent the pending intent of the bubble.
* @param packageName the notification package name for this bubble.
*/
- // Keep checks in sync with BubbleController#canLaunchInActivityView.
+ // Keep checks in sync with BubbleController#canLaunchInTaskView.
@VisibleForTesting
- protected boolean canLaunchInActivityView(Context context, PendingIntent pendingIntent,
+ protected boolean canLaunchInTaskView(Context context, PendingIntent pendingIntent,
String packageName) {
if (pendingIntent == null) {
Slog.w(TAG, "Unable to create bubble -- no intent");
diff --git a/services/core/java/com/android/server/notification/InlineReplyUriRecord.java b/services/core/java/com/android/server/notification/InlineReplyUriRecord.java
index 76cfb03..fa5c09b 100644
--- a/services/core/java/com/android/server/notification/InlineReplyUriRecord.java
+++ b/services/core/java/com/android/server/notification/InlineReplyUriRecord.java
@@ -16,9 +16,11 @@
package com.android.server.notification;
+import android.app.ActivityManager;
import android.net.Uri;
import android.os.IBinder;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.ArraySet;
/**
@@ -74,7 +76,9 @@
*/
public int getUserId() {
int userId = mUser.getIdentifier();
- if (userId == UserHandle.USER_ALL) {
+ if (UserManager.isHeadlessSystemUserMode() && userId == UserHandle.USER_ALL) {
+ return ActivityManager.getCurrentUser();
+ } else if (userId == UserHandle.USER_ALL) {
return UserHandle.USER_SYSTEM;
} else {
return userId;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b26485b..d78fbdb 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -140,6 +140,7 @@
import android.app.ITransientNotification;
import android.app.ITransientNotificationCallback;
import android.app.IUriGrantsManager;
+import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
@@ -549,6 +550,8 @@
// Used for rate limiting toasts by package.
private MultiRateLimiter mToastRateLimiter;
+ private KeyguardManager mKeyguardManager;
+
// The last key in this list owns the hardware.
ArrayList<String> mLights = new ArrayList<>();
@@ -2008,6 +2011,11 @@
}
@VisibleForTesting
+ void setKeyguardManager(KeyguardManager keyguardManager) {
+ mKeyguardManager = keyguardManager;
+ }
+
+ @VisibleForTesting
ShortcutHelper getShortcutHelper() {
return mShortcutHelper;
}
@@ -2653,6 +2661,7 @@
mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
+ mKeyguardManager = getContext().getSystemService(KeyguardManager.class);
mZenModeHelper.onSystemReady();
RoleObserver roleObserver = new RoleObserver(getContext(),
getContext().getSystemService(RoleManager.class),
@@ -3806,15 +3815,18 @@
enforceDeletingChannelHasNoFgService(pkg, callingUser, channelId);
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
callingUser, REASON_CHANNEL_REMOVED, null);
- mPreferencesHelper.deleteNotificationChannel(pkg, callingUid, channelId);
- // Remove from both recent notification archive and notification history
- mArchive.removeChannelNotifications(pkg, callingUser, channelId);
- mHistoryManager.deleteNotificationChannel(pkg, callingUid, channelId);
- mListeners.notifyNotificationChannelChanged(pkg,
- UserHandle.getUserHandleForUid(callingUid),
- mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true),
- NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
- handleSavePolicyFile();
+ boolean previouslyExisted = mPreferencesHelper.deleteNotificationChannel(
+ pkg, callingUid, channelId);
+ if (previouslyExisted) {
+ // Remove from both recent notification archive and notification history
+ mArchive.removeChannelNotifications(pkg, callingUser, channelId);
+ mHistoryManager.deleteNotificationChannel(pkg, callingUid, channelId);
+ mListeners.notifyNotificationChannelChanged(pkg,
+ UserHandle.getUserHandleForUid(callingUid),
+ mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true),
+ NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
+ handleSavePolicyFile();
+ }
}
@Override
@@ -4785,7 +4797,7 @@
}
@Override
- public String addAutomaticZenRule(AutomaticZenRule automaticZenRule) {
+ public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String pkg) {
Objects.requireNonNull(automaticZenRule, "automaticZenRule is null");
Objects.requireNonNull(automaticZenRule.getName(), "Name is null");
if (automaticZenRule.getOwner() == null
@@ -4794,6 +4806,7 @@
"Rule must have a conditionproviderservice and/or configuration activity");
}
Objects.requireNonNull(automaticZenRule.getConditionId(), "ConditionId is null");
+ checkCallerIsSameApp(pkg);
if (automaticZenRule.getZenPolicy() != null
&& automaticZenRule.getInterruptionFilter() != INTERRUPTION_FILTER_PRIORITY) {
throw new IllegalArgumentException("ZenPolicy is only applicable to "
@@ -4801,7 +4814,7 @@
}
enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
- return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
+ return mZenModeHelper.addAutomaticZenRule(pkg, automaticZenRule,
"addAutomaticZenRule");
}
@@ -7387,7 +7400,6 @@
boolean beep = false;
boolean blink = false;
- final Notification notification = record.getSbn().getNotification();
final String key = record.getKey();
// Should this notification make noise, vibe, or use the LED?
@@ -7409,7 +7421,7 @@
if (!record.isUpdate
&& record.getImportance() > IMPORTANCE_MIN
&& !suppressedByDnd) {
- sendAccessibilityEvent(notification, record.getSbn().getPackageName());
+ sendAccessibilityEvent(record);
sentAccessibilityEvent = true;
}
@@ -7432,7 +7444,7 @@
boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
if (!sentAccessibilityEvent) {
- sendAccessibilityEvent(notification, record.getSbn().getPackageName());
+ sendAccessibilityEvent(record);
sentAccessibilityEvent = true;
}
if (DBG) Slog.v(TAG, "Interrupting!");
@@ -8260,17 +8272,30 @@
return (x < low) ? low : ((x > high) ? high : x);
}
- void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
+ void sendAccessibilityEvent(NotificationRecord record) {
if (!mAccessibilityManager.isEnabled()) {
return;
}
- AccessibilityEvent event =
+ final Notification notification = record.getNotification();
+ final CharSequence packageName = record.getSbn().getPackageName();
+ final AccessibilityEvent event =
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
event.setPackageName(packageName);
event.setClassName(Notification.class.getName());
- event.setParcelableData(notification);
- CharSequence tickerText = notification.tickerText;
+ final int visibilityOverride = record.getPackageVisibilityOverride();
+ final int notifVisibility = visibilityOverride == NotificationManager.VISIBILITY_NO_OVERRIDE
+ ? notification.visibility : visibilityOverride;
+ final int userId = record.getUser().getIdentifier();
+ final boolean needPublic = userId >= 0 && mKeyguardManager.isDeviceLocked(userId);
+ if (needPublic && notifVisibility != Notification.VISIBILITY_PUBLIC) {
+ // Emit the public version if we're on the lockscreen and this notification isn't
+ // publicly visible.
+ event.setParcelableData(notification.publicVersion);
+ } else {
+ event.setParcelableData(notification);
+ }
+ final CharSequence tickerText = notification.tickerText;
if (!TextUtils.isEmpty(tickerText)) {
event.getText().add(tickerText);
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 03676b55..96bde3d 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1126,20 +1126,21 @@
}
@Override
- public void deleteNotificationChannel(String pkg, int uid, String channelId) {
+ public boolean deleteNotificationChannel(String pkg, int uid, String channelId) {
synchronized (mPackagePreferences) {
PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
if (r == null) {
- return;
+ return false;
}
NotificationChannel channel = r.channels.get(channelId);
if (channel != null) {
- deleteNotificationChannelLocked(channel, pkg, uid);
+ return deleteNotificationChannelLocked(channel, pkg, uid);
}
+ return false;
}
}
- private void deleteNotificationChannelLocked(NotificationChannel channel, String pkg, int uid) {
+ private boolean deleteNotificationChannelLocked(NotificationChannel channel, String pkg, int uid) {
if (!channel.isDeleted()) {
channel.setDeleted(true);
channel.setDeletedTimeMs(System.currentTimeMillis());
@@ -1151,7 +1152,9 @@
if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
updateChannelsBypassingDnd();
}
+ return true;
}
+ return false;
}
@Override
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index b1d6546..3982593 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -53,7 +53,7 @@
NotificationChannel getConversationNotificationChannel(String pkg, int uid, String channelId,
String conversationId, boolean returnParentIfNoConversationChannel,
boolean includeDeleted);
- void deleteNotificationChannel(String pkg, int uid, String channelId);
+ boolean deleteNotificationChannel(String pkg, int uid, String channelId);
void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId);
void permanentlyDeleteNotificationChannels(String pkg, int uid);
ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 4cb6c3b..b144ff2 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -307,7 +307,8 @@
return null;
}
- public String addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
+ public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule,
+ String reason) {
if (!isSystemRule(automaticZenRule)) {
PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner());
if (component == null) {
@@ -340,7 +341,7 @@
}
newConfig = mConfig.copy();
ZenRule rule = new ZenRule();
- populateZenRule(automaticZenRule, rule, true);
+ populateZenRule(pkg, automaticZenRule, rule, true);
newConfig.automaticRules.put(rule.id, rule);
if (setConfigLocked(newConfig, reason, rule.component, true)) {
return rule.id;
@@ -376,7 +377,7 @@
? AUTOMATIC_RULE_STATUS_ENABLED : AUTOMATIC_RULE_STATUS_DISABLED);
}
- populateZenRule(automaticZenRule, rule, false);
+ populateZenRule(rule.pkg, automaticZenRule, rule, false);
return setConfigLocked(newConfig, reason, rule.component, true);
}
}
@@ -416,7 +417,14 @@
newConfig = mConfig.copy();
for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i));
- if (rule.pkg.equals(packageName) && canManageAutomaticZenRule(rule)) {
+ String pkg = rule.pkg != null
+ ? rule.pkg
+ : (rule.component != null)
+ ? rule.component.getPackageName()
+ : (rule.configurationActivity != null)
+ ? rule.configurationActivity.getPackageName()
+ : null;
+ if (Objects.equals(pkg, packageName) && canManageAutomaticZenRule(rule)) {
newConfig.automaticRules.removeAt(i);
}
}
@@ -585,7 +593,8 @@
return null;
}
- private void populateZenRule(AutomaticZenRule automaticZenRule, ZenRule rule, boolean isNew) {
+ private void populateZenRule(String pkg, AutomaticZenRule automaticZenRule, ZenRule rule,
+ boolean isNew) {
rule.name = automaticZenRule.getName();
rule.condition = null;
rule.conditionId = automaticZenRule.getConditionId();
@@ -600,9 +609,7 @@
rule.id = ZenModeConfig.newRuleId();
rule.creationTime = System.currentTimeMillis();
rule.component = automaticZenRule.getOwner();
- rule.pkg = (rule.component != null)
- ? rule.component.getPackageName()
- : rule.configurationActivity.getPackageName();
+ rule.pkg = pkg;
}
if (rule.enabled != automaticZenRule.isEnabled()) {
@@ -611,10 +618,13 @@
}
protected AutomaticZenRule createAutomaticZenRule(ZenRule rule) {
- return new AutomaticZenRule(rule.name, rule.component, rule.configurationActivity,
+ AutomaticZenRule azr = new AutomaticZenRule(rule.name, rule.component,
+ rule.configurationActivity,
rule.conditionId, rule.zenPolicy,
NotificationManager.zenModeToInterruptionFilter(rule.zenMode),
rule.enabled, rule.creationTime);
+ azr.setPackageName(rule.pkg);
+ return azr;
}
public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason) {
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 293c59d..346f311 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -20,6 +20,7 @@
import android.annotation.RequiresPermission;
import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
@@ -31,6 +32,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.UserManager;
import android.telephony.TelephonyManager;
import android.util.ArraySet;
@@ -81,7 +83,7 @@
== BugreportParams.BUGREPORT_MODE_TELEPHONY /* checkCarrierPrivileges */);
final long identity = Binder.clearCallingIdentity();
try {
- ensureIsPrimaryUser();
+ ensureUserCanTakeBugReport(bugreportMode);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -166,11 +168,12 @@
}
/**
- * Validates that the current user is the primary user.
+ * Validates that the current user is the primary user or when bugreport is requested remotely
+ * and current user is affiliated user.
*
* @throws IllegalArgumentException if the current user is not the primary user
*/
- private void ensureIsPrimaryUser() {
+ private void ensureUserCanTakeBugReport(int bugreportMode) {
UserInfo currentUser = null;
try {
currentUser = ActivityManager.getService().getCurrentUser();
@@ -186,11 +189,40 @@
logAndThrow("No primary user. Only primary user is allowed to take bugreports.");
}
if (primaryUser.id != currentUser.id) {
+ if (bugreportMode == BugreportParams.BUGREPORT_MODE_REMOTE
+ && isCurrentUserAffiliated(currentUser.id)) {
+ return;
+ }
logAndThrow("Current user not primary user. Only primary user"
+ " is allowed to take bugreports.");
}
}
+ /**
+ * Returns {@code true} if the device has device owner and the current user is affiliated
+ * with the device owner.
+ */
+ private boolean isCurrentUserAffiliated(int currentUserId) {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ int deviceOwnerUid = dpm.getDeviceOwnerUserId();
+ if (deviceOwnerUid == UserHandle.USER_NULL) {
+ return false;
+ }
+
+ int callingUserId = UserHandle.getUserId(Binder.getCallingUid());
+
+ Slog.i(TAG, "callingUid: " + callingUserId + " deviceOwnerUid: " + deviceOwnerUid
+ + " currentUserId: " + currentUserId);
+
+ if (callingUserId != deviceOwnerUid) {
+ logAndThrow("Caller is not device owner on provisioned device.");
+ }
+ if (!dpm.isAffiliatedUser(currentUserId)) {
+ logAndThrow("Current user is not affiliated to the device owner.");
+ }
+ return true;
+ }
+
@GuardedBy("mLock")
private void startBugreportLocked(int callingUid, String callingPackage,
FileDescriptor bugreportFd, FileDescriptor screenshotFd,
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 06ff691..1401fa9 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -17,6 +17,8 @@
package com.android.server.pm;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.os.UserHandle.USER_ALL;
+import static android.os.UserHandle.USER_NULL;
import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
@@ -685,7 +687,7 @@
synchronized (mCacheLock) {
if (mShouldFilterCache != null) {
updateShouldFilterCacheForPackage(mShouldFilterCache, null, newPkgSetting,
- settings, users, settings.size());
+ settings, users, USER_ALL, settings.size());
if (additionalChangedPackages != null) {
for (int index = 0; index < additionalChangedPackages.size(); index++) {
String changedPackage = additionalChangedPackages.valueAt(index);
@@ -698,7 +700,8 @@
}
updateShouldFilterCacheForPackage(mShouldFilterCache, null,
- changedPkgSetting, settings, users, settings.size());
+ changedPkgSetting, settings, users, USER_ALL,
+ settings.size());
}
}
} // else, rebuild entire cache when system is ready
@@ -830,24 +833,57 @@
}
}
}
-
private void updateEntireShouldFilterCache() {
+ updateEntireShouldFilterCache(USER_ALL);
+ }
+
+ private void updateEntireShouldFilterCache(int subjectUserId) {
mStateProvider.runWithState((settings, users) -> {
+ int userId = USER_NULL;
+ for (int u = 0; u < users.length; u++) {
+ if (subjectUserId == users[u].id) {
+ userId = subjectUserId;
+ break;
+ }
+ }
+ if (userId == USER_NULL) {
+ Slog.e(TAG, "We encountered a new user that isn't a member of known users, "
+ + "updating the whole cache");
+ userId = USER_ALL;
+ }
WatchedSparseBooleanMatrix cache =
- updateEntireShouldFilterCacheInner(settings, users);
+ updateEntireShouldFilterCacheInner(settings, users, userId);
synchronized (mCacheLock) {
+ if (userId != USER_ALL) {
+ // if we're only updating a single user id, we need to copy over the prior
+ // cached values for the other users.
+ int[] uids = mShouldFilterCache.keys();
+ for (int i = 0; i < uids.length; i++) {
+ int uid1 = uids[i];
+ if (UserHandle.getUserId(uid1) == userId) {
+ continue;
+ }
+ for (int j = 0; j < uids.length; j++) {
+ int uid2 = uids[j];
+ if (UserHandle.getUserId(uid2) == userId) {
+ continue;
+ }
+ cache.put(uid1, uid2, mShouldFilterCache.get(uid1, uid2));
+ }
+ }
+ }
mShouldFilterCache = cache;
}
});
}
private WatchedSparseBooleanMatrix updateEntireShouldFilterCacheInner(
- ArrayMap<String, PackageSetting> settings, UserInfo[] users) {
+ ArrayMap<String, PackageSetting> settings, UserInfo[] users, int subjectUserId) {
WatchedSparseBooleanMatrix cache =
new WatchedSparseBooleanMatrix(users.length * settings.size());
for (int i = settings.size() - 1; i >= 0; i--) {
updateShouldFilterCacheForPackage(cache,
- null /*skipPackage*/, settings.valueAt(i), settings, users, i);
+ null /*skipPackage*/, settings.valueAt(i), settings, users, subjectUserId, i);
}
return cache;
}
@@ -868,8 +904,8 @@
packagesCache.put(settings.keyAt(i), pkg);
}
});
- WatchedSparseBooleanMatrix cache =
- updateEntireShouldFilterCacheInner(settingsCopy, usersRef[0]);
+ WatchedSparseBooleanMatrix cache = updateEntireShouldFilterCacheInner(
+ settingsCopy, usersRef[0], USER_ALL);
boolean[] changed = new boolean[1];
// We have a cache, let's make sure the world hasn't changed out from under us.
mStateProvider.runWithState((settings, users) -> {
@@ -899,10 +935,10 @@
});
}
- public void onUsersChanged() {
+ public void onUserCreated(int newUserId) {
synchronized (mCacheLock) {
if (mShouldFilterCache != null) {
- updateEntireShouldFilterCache();
+ updateEntireShouldFilterCache(newUserId);
onChanged();
}
}
@@ -913,7 +949,7 @@
if (mShouldFilterCache != null) {
mStateProvider.runWithState((settings, users) -> {
updateShouldFilterCacheForPackage(mShouldFilterCache, null /* skipPackage */,
- settings.get(packageName), settings, users,
+ settings.get(packageName), settings, users, USER_ALL,
settings.size() /*maxIndex*/);
});
}
@@ -922,7 +958,7 @@
private void updateShouldFilterCacheForPackage(WatchedSparseBooleanMatrix cache,
@Nullable String skipPackageName, PackageSetting subjectSetting, ArrayMap<String,
- PackageSetting> allSettings, UserInfo[] allUsers, int maxIndex) {
+ PackageSetting> allSettings, UserInfo[] allUsers, int subjectUserId, int maxIndex) {
for (int i = Math.min(maxIndex, allSettings.size() - 1); i >= 0; i--) {
PackageSetting otherSetting = allSettings.valueAt(i);
if (subjectSetting.appId == otherSetting.appId) {
@@ -932,25 +968,34 @@
if (subjectSetting.name == skipPackageName || otherSetting.name == skipPackageName) {
continue;
}
- final int userCount = allUsers.length;
- final int appxUidCount = userCount * allSettings.size();
- for (int su = 0; su < userCount; su++) {
- int subjectUser = allUsers[su].id;
- for (int ou = 0; ou < userCount; ou++) {
- int otherUser = allUsers[ou].id;
- int subjectUid = UserHandle.getUid(subjectUser, subjectSetting.appId);
- int otherUid = UserHandle.getUid(otherUser, otherSetting.appId);
- cache.put(subjectUid, otherUid,
- shouldFilterApplicationInternal(
- subjectUid, subjectSetting, otherSetting, otherUser));
- cache.put(otherUid, subjectUid,
- shouldFilterApplicationInternal(
- otherUid, otherSetting, subjectSetting, subjectUser));
+ if (subjectUserId == USER_ALL) {
+ for (int su = 0; su < allUsers.length; su++) {
+ updateShouldFilterCacheForUser(cache, subjectSetting, allUsers, otherSetting,
+ allUsers[su].id);
}
+ } else {
+ updateShouldFilterCacheForUser(cache, subjectSetting, allUsers, otherSetting,
+ subjectUserId);
}
}
}
+ private void updateShouldFilterCacheForUser(WatchedSparseBooleanMatrix cache,
+ PackageSetting subjectSetting, UserInfo[] allUsers, PackageSetting otherSetting,
+ int subjectUserId) {
+ for (int ou = 0; ou < allUsers.length; ou++) {
+ int otherUser = allUsers[ou].id;
+ int subjectUid = UserHandle.getUid(subjectUserId, subjectSetting.appId);
+ int otherUid = UserHandle.getUid(otherUser, otherSetting.appId);
+ cache.put(subjectUid, otherUid,
+ shouldFilterApplicationInternal(
+ subjectUid, subjectSetting, otherSetting, otherUser));
+ cache.put(otherUid, subjectUid,
+ shouldFilterApplicationInternal(
+ otherUid, otherSetting, subjectSetting, subjectUserId));
+ }
+ }
+
private static boolean isSystemSigned(@NonNull PackageParser.SigningDetails sysSigningDetails,
PackageSetting pkgSetting) {
return pkgSetting.isSystem()
@@ -1145,7 +1190,7 @@
continue;
}
updateShouldFilterCacheForPackage(mShouldFilterCache, setting.name,
- siblingSetting, settings, users, settings.size());
+ siblingSetting, settings, users, USER_ALL, settings.size());
}
}
@@ -1162,7 +1207,7 @@
}
updateShouldFilterCacheForPackage(mShouldFilterCache, null,
- changedPkgSetting, settings, users, settings.size());
+ changedPkgSetting, settings, users, USER_ALL, settings.size());
}
}
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index cd383b9..8fd545f 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -80,6 +80,17 @@
/** Indicates that dexopt may be run with different performance / priority tuned for restore */
public static final int DEXOPT_FOR_RESTORE = 1 << 13; // TODO(b/135202722): remove
+ /** The result of the profile analysis indicating that the app should be optimized. */
+ public static final int PROFILE_ANALYSIS_OPTIMIZE = 1;
+ /** The result of the profile analysis indicating that the app should not be optimized. */
+ public static final int PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA = 2;
+ /**
+ * The result of the profile analysis indicating that the app should not be optimized because
+ * the profiles are empty.
+ */
+ public static final int PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES = 3;
+
+
public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE;
public static final int FLAG_STORAGE_CE = IInstalld.FLAG_STORAGE_CE;
public static final int FLAG_STORAGE_EXTERNAL = IInstalld.FLAG_STORAGE_EXTERNAL;
@@ -496,9 +507,18 @@
}
}
- public boolean mergeProfiles(int uid, String packageName, String profileName)
+ /**
+ * Analyzes the ART profiles of the given package, possibly merging the information
+ * into the reference profile. Returns whether or not we should optimize the package
+ * based on how much information is in the profile.
+ *
+ * @return one of {@link #PROFILE_ANALYSIS_OPTIMIZE},
+ * {@link #PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA},
+ * {@link #PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES}
+ */
+ public int mergeProfiles(int uid, String packageName, String profileName)
throws InstallerException {
- if (!checkBeforeRemote()) return false;
+ if (!checkBeforeRemote()) return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
try {
return mInstalld.mergeProfiles(uid, packageName, profileName);
} catch (Exception e) {
@@ -645,13 +665,17 @@
}
}
- public void deleteOdex(String apkPath, String instructionSet, String outputPath)
+ /**
+ * Deletes the optimized artifacts generated by ART and returns the number
+ * of freed bytes.
+ */
+ public long deleteOdex(String apkPath, String instructionSet, String outputPath)
throws InstallerException {
- if (!checkBeforeRemote()) return;
+ if (!checkBeforeRemote()) return -1;
BlockGuard.getVmPolicy().onPathAccess(apkPath);
BlockGuard.getVmPolicy().onPathAccess(outputPath);
try {
- mInstalld.deleteOdex(apkPath, instructionSet, outputPath);
+ return mInstalld.deleteOdex(apkPath, instructionSet, outputPath);
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 9370b14..5b2c809 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -1000,16 +1000,15 @@
intents[0].setSourceBounds(sourceBounds);
// Replace theme for splash screen
- final int splashScreenThemeResId =
- mShortcutServiceInternal.getShortcutStartingThemeResId(getCallingUserId(),
+ final String splashScreenThemeResName =
+ mShortcutServiceInternal.getShortcutStartingThemeResName(getCallingUserId(),
callingPackage, packageName, shortcutId, targetUserId);
- if (splashScreenThemeResId != 0) {
+ if (splashScreenThemeResName != null && !splashScreenThemeResName.isEmpty()) {
if (startActivityOptions == null) {
startActivityOptions = new Bundle();
}
- startActivityOptions.putInt(KEY_SPLASH_SCREEN_THEME, splashScreenThemeResId);
+ startActivityOptions.putString(KEY_SPLASH_SCREEN_THEME, splashScreenThemeResName);
}
-
return startShortcutIntentsAsPublisher(
intents, packageName, featureId, startActivityOptions, targetUserId);
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 131539e..44f7d88 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -31,6 +31,9 @@
import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
+import static com.android.server.pm.Installer.PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES;
+import static com.android.server.pm.Installer.PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
+import static com.android.server.pm.Installer.PROFILE_ANALYSIS_OPTIMIZE;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -174,8 +177,10 @@
private int performDexOptLI(AndroidPackage pkg, @NonNull PackageSetting pkgSetting,
String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
+ // ClassLoader only refers non-native (jar) shared libraries and must ignore
+ // native (so) shared libraries. See also LoadedApk#createSharedLibraryLoader().
final List<SharedLibraryInfo> sharedLibraries = pkgSetting.getPkgState()
- .getUsesLibraryInfos();
+ .getNonNativeUsesLibraryInfos();
final String[] instructionSets = targetInstructionSets != null ?
targetInstructionSets : getAppDexInstructionSets(
AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
@@ -248,8 +253,12 @@
|| packageUseInfo.isUsedByOtherApps(path);
final String compilerFilter = getRealCompilerFilter(pkg,
options.getCompilerFilter(), isUsedByOtherApps);
- final boolean profileUpdated = options.isCheckForProfileUpdates() &&
- isProfileUpdated(pkg, sharedGid, profileName, compilerFilter);
+ // If we don't have to check for profiles updates assume
+ // PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA which will be a no-op with respect to
+ // profiles.
+ final int profileAnalysisResult = options.isCheckForProfileUpdates()
+ ? analyseProfiles(pkg, sharedGid, profileName, compilerFilter)
+ : PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
// Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
// flags.
@@ -257,7 +266,7 @@
for (String dexCodeIsa : dexCodeInstructionSets) {
int newResult = dexOptPath(pkg, pkgSetting, path, dexCodeIsa, compilerFilter,
- profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
+ profileAnalysisResult, classLoaderContexts[i], dexoptFlags, sharedGid,
packageStats, options.isDowngrade(), profileName, dexMetadataPath,
options.getCompilationReason());
@@ -306,11 +315,11 @@
*/
@GuardedBy("mInstallLock")
private int dexOptPath(AndroidPackage pkg, @NonNull PackageSetting pkgSetting, String path,
- String isa, String compilerFilter, boolean profileUpdated, String classLoaderContext,
+ String isa, String compilerFilter, int profileAnalysisResult, String classLoaderContext,
int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade,
String profileName, String dexMetadataPath, int compilationReason) {
int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext,
- profileUpdated, downgrade);
+ profileAnalysisResult, downgrade);
if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
return DEX_OPT_SKIPPED;
}
@@ -364,7 +373,7 @@
isa,
options.getCompilerFilter(),
dexUseInfo.getClassLoaderContext(),
- /* newProfile= */false,
+ PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES,
/* downgrade= */ false);
if (dexoptNeeded == DexFile.NO_DEXOPT_NEEDED) {
@@ -750,11 +759,25 @@
* configuration (isa, compiler filter, profile).
*/
private int getDexoptNeeded(String path, String isa, String compilerFilter,
- String classLoaderContext, boolean newProfile, boolean downgrade) {
+ String classLoaderContext, int profileAnalysisResult, boolean downgrade) {
int dexoptNeeded;
try {
- dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, classLoaderContext,
- newProfile, downgrade);
+ // A profile guided optimizations with an empty profile is essentially 'verify' and
+ // dex2oat already makes this transformation. However DexFile.getDexOptNeeded() cannot
+ // check the profiles because system server does not have access to them.
+ // As such, we rely on the previous profile analysis (done with dexoptanalyzer) and
+ // manually adjust the actual filter before checking.
+ //
+ // TODO: ideally. we'd move this check in dexoptanalyzer, but that's a large change,
+ // and in the interim we can still improve things here.
+ String actualCompilerFilter = compilerFilter;
+ if (compilerFilterDependsOnProfiles(compilerFilter)
+ && profileAnalysisResult == PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES) {
+ actualCompilerFilter = "verify";
+ }
+ boolean newProfile = profileAnalysisResult == PROFILE_ANALYSIS_OPTIMIZE;
+ dexoptNeeded = DexFile.getDexOptNeeded(path, isa, actualCompilerFilter,
+ classLoaderContext, newProfile, downgrade);
} catch (IOException ioe) {
Slog.w(TAG, "IOException reading apk: " + path, ioe);
return DEX_OPT_FAILED;
@@ -765,27 +788,34 @@
return adjustDexoptNeeded(dexoptNeeded);
}
+ /** Returns true if the compiler filter depends on profiles (e.g speed-profile). */
+ private boolean compilerFilterDependsOnProfiles(String compilerFilter) {
+ return compilerFilter.endsWith("-profile");
+ }
+
/**
* Checks if there is an update on the profile information of the {@code pkg}.
- * If the compiler filter is not profile guided the method returns false.
+ * If the compiler filter is not profile guided the method returns a safe default:
+ * PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA.
*
* Note that this is a "destructive" operation with side effects. Under the hood the
* current profile and the reference profile will be merged and subsequent calls
* may return a different result.
*/
- private boolean isProfileUpdated(AndroidPackage pkg, int uid, String profileName,
+ private int analyseProfiles(AndroidPackage pkg, int uid, String profileName,
String compilerFilter) {
// Check if we are allowed to merge and if the compiler filter is profile guided.
if (!isProfileGuidedCompilerFilter(compilerFilter)) {
- return false;
+ return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
}
// Merge profiles. It returns whether or not there was an updated in the profile info.
try {
return mInstaller.mergeProfiles(uid, pkg.getPackageName(), profileName);
} catch (InstallerException e) {
Slog.w(TAG, "Failed to merge profiles", e);
+ // We don't need to optimize if we failed to merge.
+ return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
}
- return false;
}
/**
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 60a7571..d2ed08f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -157,6 +157,7 @@
private volatile boolean mOkToSendBroadcasts = false;
private volatile boolean mBypassNextStagedInstallerCheck = false;
+ private volatile boolean mBypassNextAllowedApexUpdateCheck = false;
/**
* File storing persisted {@link #mSessions} metadata.
@@ -650,6 +651,13 @@
throw new IllegalArgumentException(
"Non-staged APEX session doesn't support INSTALL_ENABLE_ROLLBACK");
}
+ if (isCalledBySystemOrShell(callingUid) || mBypassNextAllowedApexUpdateCheck) {
+ params.installFlags |= PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK;
+ } else {
+ // Only specific APEX updates (installed through ADB, or for CTS tests) can disable
+ // allowed APEX update check.
+ params.installFlags &= ~PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK;
+ }
}
if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0
@@ -674,6 +682,8 @@
}
mBypassNextStagedInstallerCheck = false;
+ mBypassNextAllowedApexUpdateCheck = false;
+
if (!params.isMultiPackage) {
// Only system components can circumvent runtime permissions when installing.
if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
@@ -1106,6 +1116,14 @@
mBypassNextStagedInstallerCheck = value;
}
+ @Override
+ public void bypassNextAllowedApexUpdateCheck(boolean value) {
+ if (!isCalledBySystemOrShell(Binder.getCallingUid())) {
+ throw new SecurityException("Caller not allowed to bypass allowed apex update check");
+ }
+ mBypassNextAllowedApexUpdateCheck = value;
+ }
+
/**
* Set an installer to allow for the unlimited silent updates.
*/
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4b0eb65..acc83cf 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -147,6 +147,7 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
+import com.android.server.SystemConfig;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -2238,6 +2239,26 @@
.setAdmin(mInstallSource.installerPackageName)
.write();
}
+
+ // Check if APEX update is allowed. We do this check in handleInstall, since this is one of
+ // the places that:
+ // * Shared between staged and non-staged APEX update flows.
+ // * Only is called after boot completes.
+ // The later is important, since isApexUpdateAllowed check depends on the
+ // ModuleInfoProvider, which is only populated after device has booted.
+ if (isApexSession()) {
+ boolean checkApexUpdateAllowed =
+ (params.installFlags & PackageManager.INSTALL_DISABLE_ALLOWED_APEX_UPDATE_CHECK)
+ == 0;
+ synchronized (mLock) {
+ if (checkApexUpdateAllowed && !isApexUpdateAllowed(mPackageName)) {
+ onSessionValidationFailure(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
+ "Update of APEX package " + mPackageName + " is not allowed");
+ return;
+ }
+ }
+ }
+
if (params.isStaged) {
mStagingManager.commitSession(mStagedSession);
// TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
@@ -2776,6 +2797,11 @@
return sessionContains((s) -> !s.isApexSession());
}
+ private boolean isApexUpdateAllowed(String apexPackageName) {
+ return mPm.getModuleInfo(apexPackageName, 0) != null
+ || SystemConfig.getInstance().getAllowedVendorApexes().contains(apexPackageName);
+ }
+
/**
* Validate apex install.
* <p>
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a8cc5fd..f44241d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -245,7 +245,6 @@
import android.content.pm.parsing.ParsingPackageUtils.ParseFlags;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedInstrumentation;
-import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedMainComponent;
import android.content.pm.parsing.component.ParsedPermission;
import android.content.pm.parsing.component.ParsedPermissionGroup;
@@ -341,6 +340,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
+import com.android.internal.content.F2fsUtils;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.content.om.OverlayConfig;
@@ -435,8 +435,10 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
+import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.nio.charset.StandardCharsets;
import java.security.DigestException;
import java.security.DigestInputStream;
@@ -896,6 +898,20 @@
* Only non-null during an OTA, and even then it is nulled again once systemReady().
*/
private @Nullable ArraySet<String> mExistingPackages = null;
+
+ /**
+ * List of code paths that need to be released when the system becomes ready.
+ * <p>
+ * NOTE: We have to delay releasing cblocks for no other reason than we cannot
+ * retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}. When
+ * we no longer need to read that setting, cblock release can occur in the
+ * constructor.
+ *
+ * @see Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL
+ * @see #systemReady()
+ */
+ private @Nullable List<File> mReleaseOnSystemReady;
+
/**
* Whether or not system app permissions should be promoted from install to runtime.
*/
@@ -1903,14 +1919,65 @@
}
/**
- * A computer provides the functional interface to the snapshot.
+ * A {@link Computer} provides a set of functions that can operate on live data or snapshot
+ * data. At this time, the {@link Computer} is implemented by the
+ * {@link ComputerEngine}, which is in turn extended by {@link ComputerLocked}.
+ *
+ * New functions must be added carefully.
+ * <ol>
+ * <li> New functions must be true functions with respect to data collected in a
+ * {@link Snapshot}. Such data may never be modified from inside a {@link Computer}
+ * function.
+ * </li>
+ *
+ * <li> A new function must be implemented in {@link ComputerEngine}.
+ * </li>
+ *
+ * <li> A new function must be overridden in {@link ComputerLocked} if the function
+ * cannot safely access live data without holding the PackageManagerService lock. The
+ * form of the {@link ComputerLocked} function must be a single call to the
+ * {@link ComputerEngine} implementation, wrapped in a <code>synchronized</code>
+ * block. Functions in {@link ComputerLocked} should never include any other code.
+ * </li>
+ *
+ * Care must be taken when deciding if a function should be overridden in
+ * {@link ComputerLocked}. The complex lock relationships of PackageManagerService
+ * and other managers (like PermissionManager) mean deadlock is possible. On the
+ * other hand, not overriding in {@link ComputerLocked} may leave a function walking
+ * unstable data.
+ *
+ * To coax developers to consider such issues carefully, all methods in
+ * {@link Computer} must be annotated with <code>@LiveImplementation(override =
+ * MANDATORY)</code> or <code>LiveImplementation(locked = NOT_ALLOWED)</code>. A unit
+ * test verifies the annotation and that the annotation corresponds to the code in
+ * {@link ComputerEngine} and {@link ComputerLocked}.
*/
- private interface Computer {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected interface Computer {
+
+ /**
+ * Every method must be annotated.
+ */
+ @Target({ ElementType.METHOD })
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface LiveImplementation {
+ // A Computer method must be annotated with one of the following values:
+ // MANDATORY - the method must be overridden in ComputerEngineLive. The
+ // format of the override is a call to the super method, wrapped in a
+ // synchronization block.
+ // NOT_ALLOWED - the method may not appear in the live computer. It must
+ // be final in the ComputerEngine.
+ int MANDATORY = 1;
+ int NOT_ALLOWED = 2;
+ int override() default MANDATORY;
+ String rationale() default "";
+ }
/**
* Administrative statistics: record that the snapshot has been used. Every call
* to use() increments the usage counter.
*/
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
default void use() {
}
@@ -1918,95 +1985,154 @@
* Fetch the snapshot usage counter.
* @return The number of times this snapshot was used.
*/
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
default int getUsed() {
return 0;
}
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
@NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType,
int flags, @PrivateResolveFlags int privateResolveFlags, int filterCallingUid,
int userId, boolean resolveForStart, boolean allowDynamicSplits);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
@NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType,
int flags, int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
@NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent, String resolvedType,
int flags, int userId, int callingUid, boolean includeInstantApps);
+ @LiveImplementation(override = LiveImplementation.MANDATORY)
@NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(Intent intent,
String resolvedType, int flags, int filterCallingUid, int userId,
boolean resolveForStart, boolean allowDynamicSplits, String pkgName,
String instantAppPkgName);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ActivityInfo getActivityInfo(ComponentName component, int flags, int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
int filterCallingUid, int userId);
+ @LiveImplementation(override = LiveImplementation.MANDATORY)
AndroidPackage getPackage(String packageName);
+ @LiveImplementation(override = LiveImplementation.MANDATORY)
AndroidPackage getPackage(int uid);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
int filterCallingUid, int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ApplicationInfo getApplicationInfo(String packageName, int flags, int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
int filterCallingUid, int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ComponentName getDefaultHomeActivity(int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates, int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent, String resolvedType,
int flags, int sourceUserId, int parentUserId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
Intent getHomeIntent();
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
String resolvedType, int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
boolean resolveForStart, int userId, Intent intent);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
PackageInfo getPackageInfo(String packageName, int flags, int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
PackageInfo getPackageInfoInternal(String packageName, long versionCode, int flags,
int filterCallingUid, int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
PackageSetting getPackageSetting(String packageName);
+ @LiveImplementation(override = LiveImplementation.MANDATORY)
PackageSetting getPackageSettingInternal(String packageName, int callingUid);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
int sourceUserId, int targetUserId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
ServiceInfo getServiceInfo(ComponentName component, int flags, int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version);
+ @LiveImplementation(override = LiveImplementation.MANDATORY)
String getInstantAppPackageName(int callingUid);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
String resolveExternalPackageNameLPr(AndroidPackage pkg);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
String resolveInternalPackageNameLPr(String packageName, long versionCode);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
String[] getPackagesForUid(int uid);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
UserInfo getProfileParent(int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean canViewInstantApps(int callingUid, int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
int flags);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean isCallerSameApp(String packageName, int uid);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean isComponentVisibleToInstantApp(@Nullable ComponentName component);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean isComponentVisibleToInstantApp(@Nullable ComponentName component,
@ComponentType int type);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId,
String resolvedType, int flags);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean isInstantApp(String packageName, int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean isInstantAppInternal(String packageName, @UserIdInt int userId, int callingUid);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
@Nullable ComponentName component, @ComponentType int componentType, int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus, int callingUid,
int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
int checkUidPermission(String permName, int uid);
+ @LiveImplementation(override = LiveImplementation.MANDATORY)
int getPackageUidInternal(String packageName, int flags, int userId, int callingUid);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
int updateFlagsForApplication(int flags, int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
int updateFlagsForComponent(int flags, int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
int updateFlagsForPackage(int flags, int userId);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
boolean isImplicitImageCaptureIntentAndNotSetByDpc);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
boolean onlyExposedExplicitly, boolean isImplicitImageCaptureIntentAndNotSetByDpc);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell, String message);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell, String message);
+ @LiveImplementation(override = LiveImplementation.NOT_ALLOWED)
void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell,
boolean requirePermissionWhenSameUser, String message);
+ @LiveImplementation(override = LiveImplementation.MANDATORY)
SigningDetails getSigningDetails(@NonNull String packageName);
+ @LiveImplementation(override = LiveImplementation.MANDATORY)
SigningDetails getSigningDetails(int uid);
+ @LiveImplementation(override = LiveImplementation.MANDATORY)
boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId);
+ @LiveImplementation(override = LiveImplementation.MANDATORY)
boolean filterAppAccess(String packageName, int callingUid, int userId);
+ @LiveImplementation(override = LiveImplementation.MANDATORY)
void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState);
}
@@ -2015,7 +2141,8 @@
* is entirely self-contained - it has no implicit access to
* PackageManagerService.
*/
- private static class ComputerEngine implements Computer {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected static class ComputerEngine implements Computer {
// The administrative use counter.
private int mUsed = 0;
@@ -2061,7 +2188,7 @@
// PackageManagerService attributes that are primitives are referenced through the
// pms object directly. Primitives are the only attributes so referenced.
protected final PackageManagerService mService;
- protected boolean safeMode() {
+ private boolean safeMode() {
return mService.mSafeMode;
}
protected ComponentName resolveComponentName() {
@@ -2115,18 +2242,18 @@
/**
* Record that the snapshot was used.
*/
- public void use() {
+ public final void use() {
mUsed++;
}
/**
* Return the usage counter.
*/
- public int getUsed() {
+ public final int getUsed() {
return mUsed;
}
- public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
+ public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, @PrivateResolveFlags int privateResolveFlags,
int filterCallingUid, int userId, boolean resolveForStart,
boolean allowDynamicSplits) {
@@ -2225,14 +2352,14 @@
resolveForStart, userId, intent);
}
- public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
+ public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
String resolvedType, int flags, int userId) {
return queryIntentActivitiesInternal(
intent, resolvedType, flags, 0 /*privateResolveFlags*/, Binder.getCallingUid(),
userId, false /*resolveForStart*/, true /*allowDynamicSplits*/);
}
- public @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
+ public final @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent,
String resolvedType, int flags, int userId, int callingUid,
boolean includeInstantApps) {
if (!mUserManager.exists(userId)) return Collections.emptyList();
@@ -2456,7 +2583,7 @@
return null;
}
- public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
+ public final ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId);
}
@@ -2466,7 +2593,7 @@
* to clearing. Because it can only be provided by trusted code, its value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
- public ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
+ public final ActivityInfo getActivityInfoInternal(ComponentName component, int flags,
int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForComponent(flags, userId);
@@ -2520,8 +2647,8 @@
return pkg;
}
- public ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags,
- int filterCallingUid, int userId) {
+ public final ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName,
+ int flags, int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null) {
@@ -2548,7 +2675,7 @@
return null;
}
- public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
+ public final ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
return getApplicationInfoInternal(packageName, flags, Binder.getCallingUid(), userId);
}
@@ -2558,7 +2685,7 @@
* to clearing. Because it can only be provided by trusted code, its value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
- public ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
+ public final ApplicationInfo getApplicationInfoInternal(String packageName, int flags,
int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForApplication(flags, userId);
@@ -2750,7 +2877,7 @@
* Report the 'Home' activity which is currently set as "always use this one". If non is set
* then reports the most likely home activity or null if there are more than one.
*/
- public ComponentName getDefaultHomeActivity(int userId) {
+ public final ComponentName getDefaultHomeActivity(int userId) {
List<ResolveInfo> allHomeCandidates = new ArrayList<>();
ComponentName cn = getHomeActivitiesAsUser(allHomeCandidates, userId);
if (cn != null) {
@@ -2778,7 +2905,7 @@
return lastComponent;
}
- public ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
+ public final ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
int userId) {
Intent intent = getHomeIntent();
List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
@@ -2807,7 +2934,7 @@
return null;
}
- public CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
+ public final CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
String resolvedType, int flags, int sourceUserId, int parentUserId) {
if (!mUserManager.hasUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
sourceUserId)) {
@@ -2853,15 +2980,15 @@
return result;
}
- public Intent getHomeIntent() {
+ public final Intent getHomeIntent() {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.addCategory(Intent.CATEGORY_DEFAULT);
return intent;
}
- public List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
- String resolvedType, int userId) {
+ public final List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(
+ Intent intent, String resolvedType, int userId) {
CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolver(userId);
if (resolver != null) {
return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId);
@@ -2880,7 +3007,8 @@
* @param intent
* @return A filtered list of resolved activities.
*/
- public List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos,
+ public final List<ResolveInfo> applyPostResolutionFilter(
+ @NonNull List<ResolveInfo> resolveInfos,
String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
boolean resolveForStart, int userId, Intent intent) {
final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId);
@@ -3026,7 +3154,7 @@
return resolveInfos;
}
- public List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent,
+ private List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent,
int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo,
int userId) {
final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0;
@@ -3173,7 +3301,7 @@
return result;
}
- public PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) {
+ public final PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
if (ps == null) {
return null;
@@ -3243,7 +3371,7 @@
}
}
- public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
+ public final PackageInfo getPackageInfo(String packageName, int flags, int userId) {
return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
flags, Binder.getCallingUid(), userId);
}
@@ -3254,7 +3382,7 @@
* to clearing. Because it can only be provided by trusted code, its value can be
* trusted and will be used as-is; unlike userId which will be validated by this method.
*/
- public PackageInfo getPackageInfoInternal(String packageName, long versionCode,
+ public final PackageInfo getPackageInfoInternal(String packageName, long versionCode,
int flags, int filterCallingUid, int userId) {
if (!mUserManager.exists(userId)) return null;
flags = updateFlagsForPackage(flags, userId);
@@ -3325,7 +3453,7 @@
}
@Nullable
- public PackageSetting getPackageSetting(String packageName) {
+ public final PackageSetting getPackageSetting(String packageName) {
return getPackageSettingInternal(packageName, Binder.getCallingUid());
}
@@ -3335,7 +3463,7 @@
return mSettings.getPackageLPr(packageName);
}
- public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
+ public final ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
final int callingUid = Binder.getCallingUid();
if (getInstantAppPackageName(callingUid) != null) {
return ParceledListSlice.emptyList();
@@ -3474,7 +3602,7 @@
return new CrossProfileDomainInfo(forwardingInfo, highestApprovalLevel);
}
- public ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
+ public final ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
int sourceUserId, int targetUserId) {
ResolveInfo forwardingResolveInfo = new ResolveInfo();
final long ident = Binder.clearCallingIdentity();
@@ -3586,7 +3714,7 @@
return null;
}
- public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
+ public final ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
if (!mUserManager.exists(userId)) return null;
final int callingUid = Binder.getCallingUid();
flags = updateFlagsForComponent(flags, userId);
@@ -3620,7 +3748,7 @@
}
@Nullable
- public SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) {
+ public final SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) {
return getSharedLibraryInfo(name, version, mSharedLibraries, null);
}
@@ -3656,7 +3784,7 @@
return ownerUid;
}
- public String resolveExternalPackageNameLPr(AndroidPackage pkg) {
+ public final String resolveExternalPackageNameLPr(AndroidPackage pkg) {
if (pkg.getStaticSharedLibName() != null) {
return pkg.getManifestPackageName();
}
@@ -3730,7 +3858,7 @@
return packageName;
}
- public String resolveInternalPackageNameLPr(String packageName, long versionCode) {
+ public final String resolveInternalPackageNameLPr(String packageName, long versionCode) {
final int callingUid = Binder.getCallingUid();
return resolveInternalPackageNameInternalLocked(packageName, versionCode,
callingUid);
@@ -3751,7 +3879,7 @@
* calls to invalidateGetPackagesForUidCache() to locate the points at
* which the cache is invalidated.
*/
- public String[] getPackagesForUid(int uid) {
+ public final String[] getPackagesForUid(int uid) {
return getPackagesForUidInternal(uid, Binder.getCallingUid());
}
@@ -3792,7 +3920,7 @@
return null;
}
- public UserInfo getProfileParent(int userId) {
+ public final UserInfo getProfileParent(int userId) {
final long identity = Binder.clearCallingIdentity();
try {
return mUserManager.getProfileParent(userId);
@@ -3822,7 +3950,7 @@
* <li>The calling application is the default app prediction service.</li>
* </ol>
*/
- public boolean canViewInstantApps(int callingUid, int userId) {
+ public final boolean canViewInstantApps(int callingUid, int userId) {
if (callingUid < Process.FIRST_APPLICATION_UID) {
return true;
}
@@ -3846,8 +3974,8 @@
return false;
}
- public boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId,
- int flags) {
+ public final boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid,
+ int userId, int flags) {
// Callers can access only the libs they depend on, otherwise they need to explicitly
// ask for the shared libraries given the caller is allowed to access all static libs.
if ((flags & PackageManager.MATCH_STATIC_SHARED_LIBRARIES) != 0) {
@@ -3930,13 +4058,13 @@
== PackageManager.PERMISSION_GRANTED;
}
- public boolean isCallerSameApp(String packageName, int uid) {
+ public final boolean isCallerSameApp(String packageName, int uid) {
AndroidPackage pkg = mPackages.get(packageName);
return pkg != null
&& UserHandle.getAppId(uid) == pkg.getUid();
}
- public boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) {
+ public final boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) {
if (isComponentVisibleToInstantApp(component, TYPE_ACTIVITY)) {
return true;
}
@@ -3949,7 +4077,7 @@
return false;
}
- public boolean isComponentVisibleToInstantApp(
+ public final boolean isComponentVisibleToInstantApp(
@Nullable ComponentName component, @ComponentType int type) {
if (type == TYPE_ACTIVITY) {
final ParsedActivity activity = mComponentResolver.getActivity(component);
@@ -3997,13 +4125,13 @@
* @return {@code true} if the intent is a camera intent and the persistent preferred
* activity was not set by the DPC.
*/
- public boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId,
- String resolvedType, int flags) {
+ public final boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent,
+ int userId, String resolvedType, int flags) {
return intent.isImplicitImageCaptureIntent() && !isPersistentPreferredActivitySetByDpm(
intent, userId, resolvedType, flags);
}
- public boolean isInstantApp(String packageName, int userId) {
+ public final boolean isInstantApp(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
false /* checkShell */, "isInstantApp");
@@ -4011,7 +4139,7 @@
return isInstantAppInternal(packageName, userId, callingUid);
}
- public boolean isInstantAppInternal(String packageName, @UserIdInt int userId,
+ public final boolean isInstantAppInternal(String packageName, @UserIdInt int userId,
int callingUid) {
if (HIDE_EPHEMERAL_APIS) {
return false;
@@ -4145,7 +4273,8 @@
}
}
- public boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
+ public final boolean isSameProfileGroup(@UserIdInt int callerUserId,
+ @UserIdInt int userId) {
final long identity = Binder.clearCallingIdentity();
try {
return UserManagerService.getInstance().isSameProfileGroup(callerUserId, userId);
@@ -4172,7 +4301,8 @@
*
* @see #canViewInstantApps(int, int)
*/
- public boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid,
+ public final boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps,
+ int callingUid,
@Nullable ComponentName component, @ComponentType int componentType, int userId) {
// if we're in an isolated process, get the real calling UID
if (Process.isIsolated(callingUid)) {
@@ -4232,7 +4362,7 @@
/**
* @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int)
*/
- public boolean shouldFilterApplicationLocked(
+ public final boolean shouldFilterApplicationLocked(
@Nullable PackageSetting ps, int callingUid, int userId) {
return shouldFilterApplicationLocked(ps, callingUid, null, TYPE_UNKNOWN, userId);
}
@@ -4240,8 +4370,8 @@
/**
* @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int)
*/
- public boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus, int callingUid,
- int userId) {
+ public final boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus,
+ int callingUid, int userId) {
boolean filterApp = true;
for (int index = sus.packages.size() - 1; index >= 0 && filterApp; index--) {
filterApp &= shouldFilterApplicationLocked(sus.packages.valueAt(index),
@@ -4265,7 +4395,7 @@
}
// NOTE: Can't remove without a major refactor. Keep around for now.
- public int checkUidPermission(String permName, int uid) {
+ public final int checkUidPermission(String permName, int uid) {
return mPermissionManager.checkUidPermission(uid, permName);
}
@@ -4315,21 +4445,21 @@
/**
* Update given flags when being used to request {@link ApplicationInfo}.
*/
- public int updateFlagsForApplication(int flags, int userId) {
+ public final int updateFlagsForApplication(int flags, int userId) {
return updateFlagsForPackage(flags, userId);
}
/**
* Update given flags when being used to request {@link ComponentInfo}.
*/
- public int updateFlagsForComponent(int flags, int userId) {
+ public final int updateFlagsForComponent(int flags, int userId) {
return updateFlags(flags, userId);
}
/**
* Update given flags when being used to request {@link PackageInfo}.
*/
- public int updateFlagsForPackage(int flags, int userId) {
+ public final int updateFlagsForPackage(int flags, int userId) {
final boolean isCallerSystemUser = UserHandle.getCallingUserId()
== UserHandle.USER_SYSTEM;
if ((flags & PackageManager.MATCH_ANY_USER) != 0) {
@@ -4365,14 +4495,14 @@
* action and a {@code android.intent.category.BROWSABLE} category</li>
* </ul>
*/
- public int updateFlagsForResolve(int flags, int userId, int callingUid,
+ public final int updateFlagsForResolve(int flags, int userId, int callingUid,
boolean wantInstantApps, boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
return updateFlagsForResolve(flags, userId, callingUid,
wantInstantApps, false /*onlyExposedExplicitly*/,
isImplicitImageCaptureIntentAndNotSetByDpc);
}
- public int updateFlagsForResolve(int flags, int userId, int callingUid,
+ public final int updateFlagsForResolve(int flags, int userId, int callingUid,
boolean wantInstantApps, boolean onlyExposedExplicitly,
boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
// Safe mode means we shouldn't match any third-party components
@@ -4414,7 +4544,7 @@
* @param checkShell whether to prevent shell from access if there's a debugging restriction
* @param message the message to log on security exception
*/
- public void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
+ public final void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell, String message) {
if (userId < 0) {
throw new IllegalArgumentException("Invalid userId " + userId);
@@ -4452,7 +4582,7 @@
* @param checkShell whether to prevent shell from access if there's a debugging restriction
* @param message the message to log on security exception
*/
- public void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
+ public final void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell, String message) {
enforceCrossUserPermission(callingUid, userId, requireFullPermission, checkShell, false,
message);
@@ -4469,7 +4599,7 @@
* reference the same user.
* @param message the message to log on security exception
*/
- public void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
+ public final void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell,
boolean requirePermissionWhenSameUser, String message) {
if (userId < 0) {
@@ -4721,31 +4851,14 @@
}
/**
- * The live computer differs from the ComputerEngine in the methods that fetch data
- * from PackageManagerService.
- **/
- private static class ComputerEngineLive extends ComputerEngine {
- ComputerEngineLive(Snapshot args) {
- super(args);
- }
- protected ComponentName resolveComponentName() {
- return mService.mResolveComponentName;
- }
- protected ActivityInfo instantAppInstallerActivity() {
- return mService.mInstantAppInstallerActivity;
- }
- protected ApplicationInfo androidApplication() {
- return mService.mAndroidApplication;
- }
- }
-
- /**
- * This subclass is the external interface to the live computer. For each
- * interface, it takes the PM lock and then delegates to the live
- * computer engine. This is required because there are no locks taken in
- * the engine itself.
+ * This subclass is the external interface to the live computer. Some internal helper
+ * methods are overridden to fetch live data instead of snapshot data. For each
+ * Computer interface that is overridden in this class, the override takes the PM lock
+ * and then delegates to the live computer engine. This is required because there are
+ * no locks taken in the engine itself.
*/
- private static class ComputerLocked extends ComputerEngineLive {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ protected static class ComputerLocked extends ComputerEngine {
private final Object mLock;
ComputerLocked(Snapshot args) {
@@ -4753,18 +4866,17 @@
mLock = mService.mLock;
}
- /**
- * Explicilty snapshot {@link Settings#mPackages} for cases where the caller must not lock
- * in order to get package data. It is expected that the caller locks itself to be able
- * to block on changes to the package data and bring itself up to date once the change
- * propagates to it. Use with heavy caution.
- * @return
- */
- private Map<String, PackageSetting> snapshotPackageSettings() {
- return mSettings.snapshot().mPackages;
+ protected final ComponentName resolveComponentName() {
+ return mService.mResolveComponentName;
+ }
+ protected final ActivityInfo instantAppInstallerActivity() {
+ return mService.mInstantAppInstallerActivity;
+ }
+ protected final ApplicationInfo androidApplication() {
+ return mService.mAndroidApplication;
}
- public @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
+ public final @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
String resolvedType, int flags, int userId, int callingUid,
String instantAppPkgName) {
synchronized (mLock) {
@@ -4772,8 +4884,8 @@
callingUid, instantAppPkgName);
}
}
- public @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(Intent intent,
- String resolvedType, int flags, int filterCallingUid, int userId,
+ public final @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(
+ Intent intent, String resolvedType, int flags, int filterCallingUid, int userId,
boolean resolveForStart, boolean allowDynamicSplits, String pkgName,
String instantAppPkgName) {
synchronized (mLock) {
@@ -4782,31 +4894,31 @@
instantAppPkgName);
}
}
- public ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags,
+ public final ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags,
int filterCallingUid, int userId) {
synchronized (mLock) {
return super.getActivityInfoInternalBody(component, flags, filterCallingUid,
userId);
}
}
- public AndroidPackage getPackage(String packageName) {
+ public final AndroidPackage getPackage(String packageName) {
synchronized (mLock) {
return super.getPackage(packageName);
}
}
- public AndroidPackage getPackage(int uid) {
+ public final AndroidPackage getPackage(int uid) {
synchronized (mLock) {
return super.getPackage(uid);
}
}
- public ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags,
+ public final ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags,
int filterCallingUid, int userId) {
synchronized (mLock) {
return super.getApplicationInfoInternalBody(packageName, flags, filterCallingUid,
userId);
}
}
- public ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody(
+ public final ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody(
Intent intent, int matchFlags, List<ResolveInfo> candidates,
CrossProfileDomainInfo xpDomainInfo, int userId, boolean debug) {
synchronized (mLock) {
@@ -4814,49 +4926,49 @@
matchFlags, candidates, xpDomainInfo, userId, debug);
}
}
- public PackageInfo getPackageInfoInternalBody(String packageName, long versionCode,
+ public final PackageInfo getPackageInfoInternalBody(String packageName, long versionCode,
int flags, int filterCallingUid, int userId) {
synchronized (mLock) {
return super.getPackageInfoInternalBody(packageName, versionCode, flags,
filterCallingUid, userId);
}
}
- public PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
+ public final PackageSetting getPackageSettingInternal(String packageName, int callingUid) {
synchronized (mLock) {
return super.getPackageSettingInternal(packageName, callingUid);
}
}
- public ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId,
+ public final ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId,
int callingUid) {
synchronized (mLock) {
return super.getInstalledPackagesBody(flags, userId, callingUid);
}
}
- public ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId,
+ public final ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId,
int callingUid) {
synchronized (mLock) {
return super.getServiceInfoBody(component, flags, userId, callingUid);
}
}
- public String getInstantAppPackageName(int callingUid) {
+ public final String getInstantAppPackageName(int callingUid) {
synchronized (mLock) {
return super.getInstantAppPackageName(callingUid);
}
}
- public String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId,
+ public final String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId,
boolean isCallerInstantApp) {
synchronized (mLock) {
return super.getPackagesForUidInternalBody(callingUid, userId, appId,
isCallerInstantApp);
}
}
- public boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId,
+ public final boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId,
int callingUid) {
synchronized (mLock) {
return super.isInstantAppInternalBody(packageName, userId, callingUid);
}
}
- public boolean isInstantAppResolutionAllowedBody(Intent intent,
+ public final boolean isInstantAppResolutionAllowedBody(Intent intent,
List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck,
int flags) {
synchronized (mLock) {
@@ -4864,33 +4976,33 @@
skipPackageCheck, flags);
}
}
- public int getPackageUidInternal(String packageName, int flags, int userId,
+ public final int getPackageUidInternal(String packageName, int flags, int userId,
int callingUid) {
synchronized (mLock) {
return super.getPackageUidInternal(packageName, flags, userId, callingUid);
}
}
- public SigningDetails getSigningDetails(@NonNull String packageName) {
+ public final SigningDetails getSigningDetails(@NonNull String packageName) {
synchronized (mLock) {
return super.getSigningDetails(packageName);
}
}
- public SigningDetails getSigningDetails(int uid) {
+ public final SigningDetails getSigningDetails(int uid) {
synchronized (mLock) {
return super.getSigningDetails(uid);
}
}
- public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) {
+ public final boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) {
synchronized (mLock) {
return super.filterAppAccess(pkg, callingUid, userId);
}
}
- public boolean filterAppAccess(String packageName, int callingUid, int userId) {
+ public final boolean filterAppAccess(String packageName, int callingUid, int userId) {
synchronized (mLock) {
return super.filterAppAccess(packageName, callingUid, userId);
}
}
- public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
+ public final void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
synchronized (mLock) {
super.dump(type, fd, pw, dumpState);
}
@@ -7966,6 +8078,21 @@
IoUtils.closeQuietly(handle);
}
}
+ if (ret == PackageManager.INSTALL_SUCCEEDED) {
+ // NOTE: During boot, we have to delay releasing cblocks for no other reason than
+ // we cannot retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}.
+ // When we no longer need to read that setting, cblock release can occur always
+ // occur here directly
+ if (!mSystemReady) {
+ if (mReleaseOnSystemReady == null) {
+ mReleaseOnSystemReady = new ArrayList<>();
+ }
+ mReleaseOnSystemReady.add(dstCodePath);
+ } else {
+ final ContentResolver resolver = mContext.getContentResolver();
+ F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath);
+ }
+ }
if (ret != PackageManager.INSTALL_SUCCEEDED) {
if (!dstCodePath.exists()) {
return null;
@@ -11524,8 +11651,31 @@
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return;
}
+ final List<String> names = new ArrayList<>();
+ final List<ProviderInfo> infos = new ArrayList<>();
+ final int callingUserId = UserHandle.getCallingUserId();
mComponentResolver.querySyncProviders(
- outNames, outInfo, mSafeMode, UserHandle.getCallingUserId());
+ names, infos, mSafeMode, callingUserId);
+ synchronized (mLock) {
+ for (int i = infos.size() - 1; i >= 0; i--) {
+ final ProviderInfo providerInfo = infos.get(i);
+ final PackageSetting ps = mSettings.getPackageLPr(providerInfo.packageName);
+ final ComponentName component =
+ new ComponentName(providerInfo.packageName, providerInfo.name);
+ if (!shouldFilterApplicationLocked(ps, Binder.getCallingUid(), component,
+ TYPE_PROVIDER, callingUserId)) {
+ continue;
+ }
+ infos.remove(i);
+ names.remove(i);
+ }
+ }
+ if (!names.isEmpty()) {
+ outNames.addAll(names);
+ }
+ if (!infos.isEmpty()) {
+ outInfo.addAll(infos);
+ }
}
@Override
@@ -12647,21 +12797,6 @@
return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags));
}
- /**
- * Ask the package manager to compile layouts in the given package.
- */
- @Override
- public boolean compileLayouts(String packageName) {
- AndroidPackage pkg;
- synchronized (mLock) {
- pkg = mPackages.get(packageName);
- if (pkg == null) {
- return false;
- }
- }
- return mViewCompiler.compileLayouts(pkg);
- }
-
/*package*/ boolean performDexOpt(DexoptOptions options) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return false;
@@ -12958,6 +13093,15 @@
@Override
public void dumpProfiles(String packageName) {
+ /* Only the shell, root, or the app user should be able to dump profiles. */
+ final int callingUid = Binder.getCallingUid();
+ final String[] callerPackageNames = getPackagesForUid(callingUid);
+ if (callingUid != Process.SHELL_UID
+ && callingUid != Process.ROOT_UID
+ && !ArrayUtils.contains(callerPackageNames, packageName)) {
+ throw new SecurityException("dumpProfiles");
+ }
+
AndroidPackage pkg;
synchronized (mLock) {
pkg = mPackages.get(packageName);
@@ -12965,13 +13109,6 @@
throw new IllegalArgumentException("Unknown package: " + packageName);
}
}
- /* Only the shell, root, or the app user should be able to dump profiles. */
- int callingUid = Binder.getCallingUid();
- if (callingUid != Process.SHELL_UID &&
- callingUid != Process.ROOT_UID &&
- callingUid != pkg.getUid()) {
- throw new SecurityException("dumpProfiles");
- }
synchronized (mInstallLock) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles");
@@ -16964,9 +17101,15 @@
return new ParceledListSlice<IntentFilter>(result) {
@Override
protected void writeElement(IntentFilter parcelable, Parcel dest, int callFlags) {
- // WatchedIntentFilter has final Parcelable methods, so redirect to the subclass
- ((ParsedIntentInfo) parcelable).writeIntentInfoToParcel(dest,
- callFlags);
+ parcelable.writeToParcel(dest, callFlags);
+ }
+
+ @Override
+ protected void writeParcelableCreator(IntentFilter parcelable, Parcel dest) {
+ // All Parcel#writeParcelableCreator does is serialize the class name to
+ // access via reflection to grab its CREATOR. This does that manually, pointing
+ // to the parent IntentFilter so that all of the subclass fields are ignored.
+ dest.writeString(IntentFilter.class.getName());
}
};
}
@@ -17097,10 +17240,7 @@
callerPackageName);
synchronized (mLock) {
PackageSetting ps = mSettings.getPackageLPr(packageName);
- if (ps == null) {
- throw new IllegalArgumentException("Unknown target package " + packageName);
- }
- if (shouldFilterApplicationLocked(
+ if (ps == null || shouldFilterApplicationLocked(
ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
throw new IllegalArgumentException("Unknown target package " + packageName);
}
@@ -17853,6 +17993,10 @@
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
mRet = args.copyApk();
}
+ if (mRet == PackageManager.INSTALL_SUCCEEDED) {
+ F2fsUtils.releaseCompressedBlocks(
+ mContext.getContentResolver(), new File(args.getCodePath()));
+ }
if (mParentInstallParams != null) {
mParentInstallParams.tryProcessInstallRequest(args, mRet);
} else {
@@ -17860,7 +18004,6 @@
processInstallRequestsAsync(
res.returnCode == PackageManager.INSTALL_SUCCEEDED,
Collections.singletonList(new InstallRequest(args, res)));
-
}
}
}
@@ -21342,6 +21485,8 @@
// for the uninstall-updates case and restricted profiles, remember the per-
// user handle installed state
int[] allUsers;
+ final int freezeUser;
+ final SparseArray<Pair<Integer, String>> enabledStateAndCallerPerUser;
/** enabled state of the uninstalled application */
synchronized (mLock) {
uninstalledPs = mSettings.getPackageLPr(packageName);
@@ -21386,16 +21531,23 @@
}
info.origUsers = uninstalledPs.queryInstalledUsers(allUsers, true);
- }
- final int freezeUser;
- if (isUpdatedSystemApp(uninstalledPs)
- && ((deleteFlags & PackageManager.DELETE_SYSTEM_APP) == 0)) {
- // We're downgrading a system app, which will apply to all users, so
- // freeze them all during the downgrade
- freezeUser = UserHandle.USER_ALL;
- } else {
- freezeUser = removeUser;
+ if (isUpdatedSystemApp(uninstalledPs)
+ && ((deleteFlags & PackageManager.DELETE_SYSTEM_APP) == 0)) {
+ // We're downgrading a system app, which will apply to all users, so
+ // freeze them all during the downgrade
+ freezeUser = UserHandle.USER_ALL;
+ enabledStateAndCallerPerUser = new SparseArray<>();
+ for (int i = 0; i < allUsers.length; i++) {
+ PackageUserState userState = uninstalledPs.readUserState(allUsers[i]);
+ Pair<Integer, String> enabledStateAndCaller =
+ new Pair<>(userState.enabled, userState.lastDisableAppCaller);
+ enabledStateAndCallerPerUser.put(allUsers[i], enabledStateAndCaller);
+ }
+ } else {
+ freezeUser = removeUser;
+ enabledStateAndCallerPerUser = null;
+ }
}
synchronized (mInstallLock) {
@@ -21464,6 +21616,19 @@
}
}
}
+ if (enabledStateAndCallerPerUser != null) {
+ synchronized (mLock) {
+ for (int i = 0; i < allUsers.length; i++) {
+ Pair<Integer, String> enabledStateAndCaller =
+ enabledStateAndCallerPerUser.get(allUsers[i]);
+ getPackageSetting(packageName)
+ .setEnabled(enabledStateAndCaller.first,
+ allUsers[i],
+ enabledStateAndCaller.second);
+ }
+ mSettings.writeAllUsersPackageRestrictionsLPr();
+ }
+ }
}
return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR;
@@ -23158,16 +23323,17 @@
if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
return;
}
+ final String[] callerPackageNames = getPackagesForUid(callingUid);
+ if (!ArrayUtils.contains(callerPackageNames, pkg)) {
+ throw new SecurityException("Calling uid " + callingUid
+ + " does not own package " + pkg);
+ }
final int callingUserId = UserHandle.getUserId(callingUid);
PackageInfo pi = getPackageInfo(pkg, 0, callingUserId);
if (pi == null) {
throw new IllegalArgumentException("Unknown package " + pkg + " on user "
+ callingUserId);
}
- if (!UserHandle.isSameApp(pi.applicationInfo.uid, callingUid)) {
- throw new SecurityException("Calling uid " + callingUid
- + " does not own package " + pkg);
- }
}
@Override
@@ -23983,6 +24149,13 @@
final int permission = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
+ if (!allowedByPermission
+ && !ArrayUtils.contains(getPackagesForUid(callingUid), packageName)) {
+ throw new SecurityException(
+ "Permission Denial: attempt to change stopped state from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + callingUid + ", package=" + packageName);
+ }
enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
true /* checkShell */, "stop package");
boolean shouldUnhibernate = false;
@@ -23993,8 +24166,7 @@
shouldUnhibernate = true;
}
if (!shouldFilterApplicationLocked(ps, callingUid, userId)
- && mSettings.setPackageStoppedStateLPw(this, packageName, stopped,
- allowedByPermission, callingUid, userId)) {
+ && mSettings.setPackageStoppedStateLPw(this, packageName, stopped, userId)) {
scheduleWritePackageRestrictionsLocked(userId);
}
}
@@ -24235,8 +24407,15 @@
public void systemReady() {
enforceSystemOrRoot("Only the system can claim the system is ready");
- mSystemReady = true;
final ContentResolver resolver = mContext.getContentResolver();
+ if (mReleaseOnSystemReady != null) {
+ for (int i = mReleaseOnSystemReady.size() - 1; i >= 0; --i) {
+ final File dstCodePath = mReleaseOnSystemReady.get(i);
+ F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath);
+ }
+ mReleaseOnSystemReady = null;
+ }
+ mSystemReady = true;
ContentObserver co = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
@@ -26326,7 +26505,7 @@
synchronized (mLock) {
scheduleWritePackageRestrictionsLocked(userId);
scheduleWritePackageListLocked(userId);
- mAppsFilter.onUsersChanged();
+ mAppsFilter.onUserCreated(userId);
}
}
@@ -26421,16 +26600,12 @@
}
synchronized(mLock) {
final AndroidPackage pkg = mPackages.get(packageName);
- if (pkg == null) {
+ if (pkg == null
+ || shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()),
+ Binder.getCallingUid(), UserHandle.getCallingUserId())) {
Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- final PackageSetting ps = getPackageSetting(pkg.getPackageName());
- if (shouldFilterApplicationLocked(
- ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
- Slog.w(TAG, "KeySet requested for filtered package: " + packageName);
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
final KeySetManagerService ksms = mSettings.getKeySetManagerService();
return new KeySet(ksms.getKeySetByAliasAndPackageNameLPr(packageName, alias));
}
@@ -26445,14 +26620,10 @@
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
final AndroidPackage pkg = mPackages.get(packageName);
- if (pkg == null) {
- Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- final PackageSetting ps = getPackageSetting(pkg.getPackageName());
- if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
- // filter and pretend the package doesn't exist
- Slog.w(TAG, "KeySet requested for filtered package: " + packageName
+ if (pkg == null
+ || shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()),
+ callingUid, callingUserId)) {
+ Slog.w(TAG, "KeySet requested for unknown package: " + packageName
+ ", uid:" + callingUid);
throw new IllegalArgumentException("Unknown package: " + packageName);
}
@@ -27892,7 +28063,7 @@
@Override
public List<String> getMimeGroup(String packageName, String mimeGroup) {
- return PackageManagerService.this.getMimeGroup(packageName, mimeGroup);
+ return PackageManagerService.this.getMimeGroupInternal(packageName, mimeGroup);
}
@Override
@@ -27982,8 +28153,8 @@
}
@Override
- public void deleteOatArtifactsOfPackage(String packageName) {
- PackageManagerService.this.deleteOatArtifactsOfPackage(packageName);
+ public long deleteOatArtifactsOfPackage(String packageName) {
+ return PackageManagerService.this.deleteOatArtifactsOfPackage(packageName);
}
@Override
@@ -28391,14 +28562,14 @@
}
}
- void deleteOatArtifactsOfPackage(String packageName) {
+ long deleteOatArtifactsOfPackage(String packageName) {
final AndroidPackage pkg;
final PackageSetting pkgSetting;
synchronized (mLock) {
pkg = mPackages.get(packageName);
pkgSetting = mSettings.getPackageLPr(packageName);
}
- mDexManager.deleteOptimizedFiles(ArtUtils.createArtPackageInfo(pkg, pkgSetting));
+ return mDexManager.deleteOptimizedFiles(ArtUtils.createArtPackageInfo(pkg, pkgSetting));
}
Set<String> getUnusedPackages(long downgradeTimeThresholdMillis) {
@@ -28518,9 +28689,11 @@
@Override
public void setMimeGroup(String packageName, String mimeGroup, List<String> mimeTypes) {
- boolean changed = mSettings.getPackageLPr(packageName)
- .setMimeGroup(mimeGroup, mimeTypes);
-
+ enforceOwnerRights(packageName, Binder.getCallingUid());
+ final boolean changed;
+ synchronized (mLock) {
+ changed = mSettings.getPackageLPr(packageName).setMimeGroup(mimeGroup, mimeTypes);
+ }
if (changed) {
applyMimeGroupChanges(packageName, mimeGroup);
}
@@ -28528,7 +28701,14 @@
@Override
public List<String> getMimeGroup(String packageName, String mimeGroup) {
- return mSettings.getPackageLPr(packageName).getMimeGroup(mimeGroup);
+ enforceOwnerRights(packageName, Binder.getCallingUid());
+ return getMimeGroupInternal(packageName, mimeGroup);
+ }
+
+ private List<String> getMimeGroupInternal(String packageName, String mimeGroup) {
+ synchronized (mLock) {
+ return mSettings.getPackageLPr(packageName).getMimeGroup(mimeGroup);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 49559f29..1aa80a9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -307,6 +307,8 @@
return runLogVisibility();
case "bypass-staged-installer-check":
return runBypassStagedInstallerCheck();
+ case "bypass-allowed-apex-update-check":
+ return runBypassAllowedApexUpdateCheck();
case "set-silent-updates-policy":
return runSetSilentUpdatesPolicy();
default: {
@@ -424,6 +426,20 @@
}
}
+ private int runBypassAllowedApexUpdateCheck() {
+ final PrintWriter pw = getOutPrintWriter();
+ try {
+ mInterface.getPackageInstaller()
+ .bypassNextAllowedApexUpdateCheck(Boolean.parseBoolean(getNextArg()));
+ return 0;
+ } catch (RemoteException e) {
+ pw.println("Failure ["
+ + e.getClass().getName() + " - "
+ + e.getMessage() + "]");
+ return -1;
+ }
+ }
+
private int uninstallSystemUpdates(String packageName) {
final PrintWriter pw = getOutPrintWriter();
boolean failedUninstalls = false;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 7aa1c3a..26aebbc 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4188,18 +4188,11 @@
}
boolean setPackageStoppedStateLPw(PackageManagerService pm, String packageName,
- boolean stopped, boolean allowedByPermission, int uid, int userId) {
- int appId = UserHandle.getAppId(uid);
+ boolean stopped, int userId) {
final PackageSetting pkgSetting = mPackages.get(packageName);
if (pkgSetting == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- if (!allowedByPermission && (appId != pkgSetting.appId)) {
- throw new SecurityException(
- "Permission Denial: attempt to change stopped state from pid="
- + Binder.getCallingPid()
- + ", uid=" + uid + ", package uid=" + pkgSetting.appId);
- }
if (DEBUG_STOPPED) {
if (stopped) {
RuntimeException e = new RuntimeException("here");
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 1bd9e5e..b4bd086 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -139,7 +139,7 @@
private static final String ATTR_BITMAP_PATH = "bitmap-path";
private static final String ATTR_ICON_URI = "icon-uri";
private static final String ATTR_LOCUS_ID = "locus-id";
- private static final String ATTR_SPLASH_SCREEN_THEME_ID = "splash-screen-theme-id";
+ private static final String ATTR_SPLASH_SCREEN_THEME_NAME = "splash-screen-theme-name";
private static final String ATTR_PERSON_NAME = "name";
private static final String ATTR_PERSON_URI = "uri";
@@ -1799,7 +1799,7 @@
ShortcutService.writeAttr(out, ATTR_TITLE, si.getTitle());
ShortcutService.writeAttr(out, ATTR_TITLE_RES_ID, si.getTitleResId());
ShortcutService.writeAttr(out, ATTR_TITLE_RES_NAME, si.getTitleResName());
- ShortcutService.writeAttr(out, ATTR_SPLASH_SCREEN_THEME_ID, si.getStartingThemeResId());
+ ShortcutService.writeAttr(out, ATTR_SPLASH_SCREEN_THEME_NAME, si.getStartingThemeResName());
ShortcutService.writeAttr(out, ATTR_TEXT, si.getText());
ShortcutService.writeAttr(out, ATTR_TEXT_RES_ID, si.getTextResId());
ShortcutService.writeAttr(out, ATTR_TEXT_RES_NAME, si.getTextResName());
@@ -2010,7 +2010,7 @@
String bitmapPath;
String iconUri;
final String locusIdString;
- int splashScreenThemeResId;
+ String splashScreenThemeResName;
int backupVersionCode;
ArraySet<String> categories = null;
ArrayList<Person> persons = new ArrayList<>();
@@ -2021,8 +2021,8 @@
title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE);
titleResId = ShortcutService.parseIntAttribute(parser, ATTR_TITLE_RES_ID);
titleResName = ShortcutService.parseStringAttribute(parser, ATTR_TITLE_RES_NAME);
- splashScreenThemeResId = ShortcutService.parseIntAttribute(parser,
- ATTR_SPLASH_SCREEN_THEME_ID);
+ splashScreenThemeResName = ShortcutService.parseStringAttribute(parser,
+ ATTR_SPLASH_SCREEN_THEME_NAME);
text = ShortcutService.parseStringAttribute(parser, ATTR_TEXT);
textResId = ShortcutService.parseIntAttribute(parser, ATTR_TEXT_RES_ID);
textResName = ShortcutService.parseStringAttribute(parser, ATTR_TEXT_RES_NAME);
@@ -2117,7 +2117,7 @@
rank, extras, lastChangedTimestamp, flags,
iconResId, iconResName, bitmapPath, iconUri,
disabledReason, persons.toArray(new Person[persons.size()]), locusId,
- splashScreenThemeResId);
+ splashScreenThemeResName);
}
private static Intent parseIntent(TypedXmlPullParser parser)
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index c06f01a..b86c50b 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -383,8 +383,11 @@
final int textResId = sa.getResourceId(R.styleable.Shortcut_shortcutLongLabel, 0);
final int disabledMessageResId = sa.getResourceId(
R.styleable.Shortcut_shortcutDisabledMessage, 0);
- final int splashScreenTheme = sa.getResourceId(
+ final int splashScreenThemeResId = sa.getResourceId(
R.styleable.Shortcut_splashScreenTheme, 0);
+ final String splashScreenThemeResName = splashScreenThemeResId != 0
+ ? service.mContext.getResources().getResourceName(splashScreenThemeResId)
+ : null;
if (TextUtils.isEmpty(id)) {
Log.w(TAG, "android:shortcutId must be provided. activity=" + activity);
@@ -407,7 +410,7 @@
rank,
iconResId,
enabled,
- splashScreenTheme);
+ splashScreenThemeResName);
} finally {
sa.recycle();
}
@@ -416,7 +419,7 @@
private static ShortcutInfo createShortcutFromManifest(ShortcutService service,
@UserIdInt int userId, String id, String packageName, ComponentName activityComponent,
int titleResId, int textResId, int disabledMessageResId,
- int rank, int iconResId, boolean enabled, int splashScreenTheme) {
+ int rank, int iconResId, boolean enabled, @Nullable String splashScreenThemeResName) {
final int flags =
(enabled ? ShortcutInfo.FLAG_MANIFEST : ShortcutInfo.FLAG_DISABLED)
@@ -456,7 +459,7 @@
disabledReason,
null /* persons */,
null /* locusId */,
- splashScreenTheme);
+ splashScreenThemeResName);
}
private static String parseCategory(ShortcutService service, AttributeSet attrs) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 5f10277..fcbf40e 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -3536,7 +3536,8 @@
}
@Override
- public int getShortcutStartingThemeResId(int launcherUserId,
+ @Nullable
+ public String getShortcutStartingThemeResName(int launcherUserId,
@NonNull String callingPackage, @NonNull String packageName,
@NonNull String shortcutId, int userId) {
Objects.requireNonNull(callingPackage, "callingPackage");
@@ -3553,11 +3554,11 @@
final ShortcutPackage p = getUserShortcutsLocked(userId)
.getPackageShortcutsIfExists(packageName);
if (p == null) {
- return 0;
+ return null;
}
final ShortcutInfo shortcutInfo = p.findShortcutById(shortcutId);
- return shortcutInfo != null ? shortcutInfo.getStartingThemeResId() : 0;
+ return shortcutInfo != null ? shortcutInfo.getStartingThemeResName() : null;
}
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 4a68b76..c842ff1 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -955,8 +955,7 @@
continue;
} else if (isApexSessionFailed(apexSession)) {
hasFailedApexSession = true;
- String errorMsg = "APEX activation failed. Check logcat messages from apexd "
- + "for more information.";
+ String errorMsg = "APEX activation failed. " + apexSession.errorMessage;
if (!TextUtils.isEmpty(apexSession.crashingNativeProcess)) {
prepareForLoggingApexdRevert(session, apexSession.crashingNativeProcess);
errorMsg = "Session reverted due to crashing native process: "
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index b7a069e..878eb92 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -13,6 +13,9 @@
"name": "CtsAppEnumerationTestCases"
},
{
+ "name": "AppEnumerationInternalTests"
+ },
+ {
"name": "CtsMatchFlagTestCases"
},
{
@@ -52,6 +55,14 @@
]
},
{
+ "name": "GtsContentTestCases",
+ "options": [
+ {
+ "include-filter": "com.google.android.content.gts"
+ }
+ ]
+ },
+ {
"name": "GtsSecurityHostTestCases",
"options": [
{
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e8897ca..d4feb3a 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3902,6 +3902,17 @@
isFirstBoot, isUpgrade, existingPackages);
}
+ @Override
+ public String[] getPreInstallableSystemPackages(@NonNull String userType) {
+ checkManageOrCreateUsersPermission("getPreInstallableSystemPackages");
+ final Set<String> installableSystemPackages =
+ mSystemPackageInstaller.getInstallablePackagesForUserType(userType);
+ if (installableSystemPackages == null) {
+ return null;
+ }
+ return installableSystemPackages.toArray(new String[installableSystemPackages.size()]);
+ }
+
private long getCreationTime() {
final long now = System.currentTimeMillis();
return (now > EPOCH_PLUS_30_YEARS) ? now : 0;
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index 83d4ce7..b26b694 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -22,6 +22,7 @@
import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK;
import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK;
+import android.os.SystemClock;
import android.util.Slog;
import android.util.jar.StrictJarFile;
@@ -288,7 +289,7 @@
ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_UNKNOWN),
COMPILATION_REASON_MAP.getOrDefault(compilationReason, ArtStatsLog.
ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_UNKNOWN),
- /*timestamp_millis=*/ 0L,
+ /*timestamp_millis=*/ SystemClock.uptimeMillis(),
ArtStatsLog.ART_DATUM_REPORTED__THREAD_TYPE__ART_THREAD_MAIN,
kind,
value,
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 32ba26c..5820489 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -1034,18 +1034,26 @@
/**
* Deletes all the optimizations files generated by ART.
+ * This is best effort, and the method will log but not throw errors
+ * for individual deletes
+ *
* @param packageInfo the package information.
+ * @return the number of freed bytes or -1 if there was an error in the process.
*/
- public void deleteOptimizedFiles(ArtPackageInfo packageInfo) {
+ public long deleteOptimizedFiles(ArtPackageInfo packageInfo) {
+ long freedBytes = 0;
+ boolean hadErrors = false;
for (String codePath : packageInfo.getCodePaths()) {
for (String isa : packageInfo.getInstructionSets()) {
try {
- mInstaller.deleteOdex(codePath, isa, packageInfo.getOatDir());
+ freedBytes += mInstaller.deleteOdex(codePath, isa, packageInfo.getOatDir());
} catch (InstallerException e) {
Log.e(TAG, "Failed deleting oat files for " + codePath, e);
+ hadErrors = true;
}
}
}
+ return hadErrors ? -1 : freedBytes;
}
public static class RegisterDexModuleResult {
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index bad7e5c..dab980a 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -641,7 +641,7 @@
// Cell Broadcast Receiver
grantSystemFixedPermissionsToSystemPackage(pm,
getDefaultSystemHandlerActivityPackage(pm, Intents.SMS_CB_RECEIVED_ACTION, userId),
- userId, SMS_PERMISSIONS);
+ userId, SMS_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS);
// Carrier Provisioning Service
grantPermissionsToSystemPackage(pm,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index a391dbc..38e9d3e 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -5720,10 +5720,8 @@
boolean fromDatasource, int attributedOp) {
// Now let's check the identity chain...
final int op = AppOpsManager.permissionToOpCode(permission);
- final int attributionChainId = (startDataDelivery)
- ? sAttributionChainIds.incrementAndGet()
- : AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
-
+ final int attributionChainId =
+ getAttributionChainId(startDataDelivery, attributionSource);
AttributionSource current = attributionSource;
AttributionSource next = null;
@@ -5879,9 +5877,8 @@
return PermissionChecker.PERMISSION_HARD_DENIED;
}
- final int attributionChainId = (startDataDelivery)
- ? sAttributionChainIds.incrementAndGet()
- : AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+ final int attributionChainId =
+ getAttributionChainId(startDataDelivery, attributionSource);
AttributionSource current = attributionSource;
AttributionSource next = null;
@@ -6064,6 +6061,21 @@
}
}
+ private static int getAttributionChainId(boolean startDataDelivery,
+ AttributionSource source) {
+ if (source == null || source.getNext() == null || !startDataDelivery) {
+ return AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+ }
+ int attributionChainId = sAttributionChainIds.incrementAndGet();
+
+ // handle overflow
+ if (attributionChainId < 0) {
+ attributionChainId = 0;
+ sAttributionChainIds.set(0);
+ }
+ return attributionChainId;
+ }
+
private static @Nullable String resolvePackageName(@NonNull Context context,
@NonNull AttributionSource attributionSource) {
if (attributionSource.getPackageName() != null) {
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
index 05879ec..fad0aef 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
@@ -28,6 +28,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Collectors;
/**
* For use by {@link PackageSetting} to maintain functionality that used to exist in
@@ -110,6 +111,10 @@
this.overrideSeInfo = other.overrideSeInfo;
}
+ public @NonNull List<SharedLibraryInfo> getNonNativeUsesLibraryInfos() {
+ return getUsesLibraryInfos().stream()
+ .filter((l) -> !l.isNative()).collect(Collectors.toList());
+ }
// Code below generated by codegen v1.0.14.
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index 94005b2..563acf7 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -33,28 +33,30 @@
import android.location.LocationManagerInternal;
import android.net.Uri;
import android.os.IBinder;
+import android.os.PackageTagsList;
import android.os.Process;
import android.os.UserHandle;
import android.service.voice.VoiceInteractionManagerInternal;
import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.function.DecFunction;
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexFunction;
import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.QuintConsumer;
import com.android.internal.util.function.QuintFunction;
import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.UndecFunction;
import com.android.server.LocalServices;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -67,11 +69,10 @@
"android:activity_recognition_allow_listed_tags";
private static final String ACTIVITY_RECOGNITION_TAGS_SEPARATOR = ";";
- private static ArraySet<String> sExpectedTags = new ArraySet<>(new String[] {
+ private static final ArraySet<String> sExpectedTags = new ArraySet<>(new String[] {
"awareness_provider", "activity_recognition_provider", "network_location_provider",
"network_location_calibration", "fused_location_provider", "geofencer_provider"});
-
@NonNull
private final Object mLock = new Object();
@@ -96,13 +97,22 @@
*/
@GuardedBy("mLock - writes only - see above")
@NonNull
- private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>> mLocationTags =
+ private final ConcurrentHashMap<Integer, PackageTagsList> mLocationTags =
new ConcurrentHashMap<>();
+ // location tags can vary per uid - but we merge all tags under an app id into the final data
+ // structure above
+ @GuardedBy("mLock")
+ private final SparseArray<PackageTagsList> mPerUidLocationTags = new SparseArray<>();
+
+ // activity recognition currently only grabs tags from the APK manifest. we know that the
+ // manifest is the same for all users, so there's no need to track variations in tags across
+ // different users. if that logic ever changes, this might need to behave more like location
+ // tags above.
@GuardedBy("mLock - writes only - see above")
@NonNull
- private final ConcurrentHashMap<Integer, ArrayMap<String, ArraySet<String>>>
- mActivityRecognitionTags = new ConcurrentHashMap<>();
+ private final ConcurrentHashMap<Integer, PackageTagsList> mActivityRecognitionTags =
+ new ConcurrentHashMap<>();
public AppOpsPolicy(@NonNull Context context) {
mContext = context;
@@ -112,13 +122,28 @@
final LocationManagerInternal locationManagerInternal = LocalServices.getService(
LocationManagerInternal.class);
- locationManagerInternal.setOnProviderLocationTagsChangeListener((providerTagInfo) -> {
- synchronized (mLock) {
- updateAllowListedTagsForPackageLocked(providerTagInfo.getUid(),
- providerTagInfo.getPackageName(), providerTagInfo.getTags(),
- mLocationTags);
- }
- });
+ locationManagerInternal.setLocationPackageTagsListener(
+ (uid, packageTagsList) -> {
+ synchronized (mLock) {
+ if (packageTagsList.isEmpty()) {
+ mPerUidLocationTags.remove(uid);
+ } else {
+ mPerUidLocationTags.set(uid, packageTagsList);
+ }
+
+ int appId = UserHandle.getAppId(uid);
+ PackageTagsList.Builder appIdTags = new PackageTagsList.Builder(1);
+ int size = mPerUidLocationTags.size();
+ for (int i = 0; i < size; i++) {
+ if (UserHandle.getAppId(mPerUidLocationTags.keyAt(i)) == appId) {
+ appIdTags.add(mPerUidLocationTags.valueAt(i));
+ }
+ }
+
+ updateAllowListedTagsForPackageLocked(appId, appIdTags.build(),
+ mLocationTags);
+ }
+ });
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
@@ -219,6 +244,14 @@
}
@Override
+ public void finishOperation(IBinder clientId, int code, int uid, String packageName,
+ String attributionTag,
+ @NonNull QuintConsumer<IBinder, Integer, Integer, String, String> superImpl) {
+ superImpl.accept(clientId, resolveDatasourceOp(code, uid, packageName, attributionTag),
+ resolveUid(code, uid), packageName, attributionTag);
+ }
+
+ @Override
public void finishProxyOperation(int code, @NonNull AttributionSource attributionSource,
boolean skipProxyOperation, @NonNull TriFunction<Integer, AttributionSource,
Boolean, Void> superImpl) {
@@ -306,95 +339,30 @@
}
final String tagsList = resolvedService.serviceInfo.metaData.getString(
ACTIVITY_RECOGNITION_TAGS);
- if (tagsList != null) {
- final String[] tags = tagsList.split(ACTIVITY_RECOGNITION_TAGS_SEPARATOR);
+ if (!TextUtils.isEmpty(tagsList)) {
+ PackageTagsList packageTagsList = new PackageTagsList.Builder(1).add(
+ resolvedService.serviceInfo.packageName,
+ Arrays.asList(tagsList.split(ACTIVITY_RECOGNITION_TAGS_SEPARATOR))).build();
synchronized (mLock) {
updateAllowListedTagsForPackageLocked(
- resolvedService.serviceInfo.applicationInfo.uid,
- resolvedService.serviceInfo.packageName, new ArraySet<>(tags),
+ UserHandle.getAppId(resolvedService.serviceInfo.applicationInfo.uid),
+ packageTagsList,
mActivityRecognitionTags);
}
}
}
- 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>> appIdTags = datastore.get(appId);
- if (appIdTags != null) {
- appIdTags = new ArrayMap<>(appIdTags);
- }
-
- ArraySet<String> packageTags = (appIdTags != null) ? appIdTags.get(packageName) : null;
- if (packageTags != null) {
- packageTags = new ArraySet<>(packageTags);
- }
-
- if (allowListedTags != null && !allowListedTags.isEmpty()) {
- if (packageTags != null) {
- packageTags.clear();
- packageTags.addAll(allowListedTags);
- } else {
- packageTags = new ArraySet<>(allowListedTags);
- }
- if (appIdTags == null) {
- appIdTags = new ArrayMap<>();
- }
-
- // Remove any invalid tags
- boolean nullRemoved = packageTags.remove(null);
- boolean nullStrRemoved = packageTags.remove("null");
- boolean emptyRemoved = packageTags.remove("");
- if (nullRemoved || nullStrRemoved || emptyRemoved) {
- Log.e(LOG_TAG, "Attempted to add invalid source attribution tag, removed "
- + "null: " + nullRemoved + " removed \"null\": " + nullStrRemoved
- + " removed empty string: " + emptyRemoved);
- }
-
- 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(appId);
- }
- }
+ private static void updateAllowListedTagsForPackageLocked(int appId,
+ PackageTagsList packageTagsList,
+ ConcurrentHashMap<Integer, PackageTagsList> datastore) {
+ datastore.put(appId, packageTagsList);
}
private static boolean isDatasourceAttributionTag(int uid, @NonNull String packageName,
- @NonNull String attributionTag, @NonNull Map<Integer, ArrayMap<String,
- ArraySet<String>>> mappedOps) {
+ @NonNull String attributionTag, @NonNull Map<Integer, PackageTagsList> mappedOps) {
// Only a single lookup from the underlying concurrent data structure
- 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)) {
- if (packageName.equals("com.google.android.gms")
- && !sExpectedTags.contains(attributionTag)) {
- Log.i("AppOpsDebugRemapping", packageName + " tag "
- + attributionTag + " in " + packageTags);
- }
- return true;
- }
- if (packageName.equals("com.google.android.gms")
- && sExpectedTags.contains(attributionTag)) {
- Log.i("AppOpsDebugRemapping", packageName + " tag " + attributionTag
- + " NOT in " + packageTags);
- }
- } else {
- if (packageName.equals("com.google.android.gms")) {
- Log.i("AppOpsDebugRemapping", "no package tags for uid " + uid
- + " package " + packageName);
- }
-
- }
- return false;
+ final PackageTagsList appIdTags = mappedOps.get(UserHandle.getAppId(uid));
+ return appIdTags != null && appIdTags.contains(packageName, attributionTag);
}
private static int resolveLocationOp(int code) {
diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
index 5b48abb..a5969a8 100644
--- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java
+++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
@@ -135,7 +135,10 @@
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
- context.registerReceiver(mBroadcastReceiver, filter);
+ // By default CLOSE_SYSTEM_DIALOGS broadcast is sent only for current user, which is user
+ // 10 on devices with headless system user enabled.
+ // In order to receive the broadcast, register the broadcast receiver with UserHandle.ALL.
+ context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
mHasTelephony =
context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index ed4a7bf..f72adb60 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -961,11 +961,13 @@
@Override
public boolean allocateSpaceForUpdate(String packageFile) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
if (!isUpdatableApexSupported()) {
Log.i(TAG, "Updatable Apex not supported, "
+ "allocateSpaceForUpdate does nothing.");
return true;
}
+ final long token = Binder.clearCallingIdentity();
try {
CompressedApexInfoList apexInfoList = getCompressedApexInfoList(packageFile);
ApexManager apexManager = ApexManager.getInstance();
@@ -975,6 +977,8 @@
e.rethrowAsRuntimeException();
} catch (IOException | UnsupportedOperationException e) {
Slog.e(TAG, "Failed to reserve space for compressed apex: ", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
return false;
}
diff --git a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
index e1e6195..f653e4b 100644
--- a/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
+++ b/services/core/java/com/android/server/stats/pull/ProcfsMemoryUtil.java
@@ -30,6 +30,9 @@
"RssAnon:",
"VmSwap:"
};
+ private static final String[] VMSTAT_KEYS = new String[] {
+ "oom_kill"
+ };
private ProcfsMemoryUtil() {}
@@ -99,4 +102,22 @@
public int anonRssInKilobytes;
public int swapInKilobytes;
}
+
+ /** Reads and parses selected entries of /proc/vmstat. */
+ @Nullable
+ static VmStat readVmStat() {
+ long[] vmstat = new long[VMSTAT_KEYS.length];
+ vmstat[0] = -1;
+ Process.readProcLines("/proc/vmstat", VMSTAT_KEYS, vmstat);
+ if (vmstat[0] == -1) {
+ return null;
+ }
+ VmStat result = new VmStat();
+ result.oomKillCount = (int) vmstat[0];
+ return result;
+ }
+
+ static final class VmStat {
+ public int oomKillCount;
+ }
}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index dc868b3..cd0ce2b 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -544,6 +544,8 @@
return pullProcessDmabufMemory(atomTag, data);
case FrameworkStatsLog.SYSTEM_MEMORY:
return pullSystemMemory(atomTag, data);
+ case FrameworkStatsLog.VMSTAT:
+ return pullVmStat(atomTag, data);
case FrameworkStatsLog.TEMPERATURE:
synchronized (mTemperatureLock) {
return pullTemperatureLocked(atomTag, data);
@@ -842,6 +844,7 @@
registerProcessSystemIonHeapSize();
registerSystemMemory();
registerProcessDmabufMemory();
+ registerVmStat();
registerTemperature();
registerCoolingDevice();
registerBinderCallsStats();
@@ -2273,6 +2276,27 @@
return StatsManager.PULL_SUCCESS;
}
+ private void registerVmStat() {
+ int tagId = FrameworkStatsLog.VMSTAT;
+ mStatsManager.setPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ DIRECT_EXECUTOR,
+ mStatsCallbackImpl
+ );
+ }
+
+ int pullVmStat(int atomTag, List<StatsEvent> pulledData) {
+ ProcfsMemoryUtil.VmStat vmStat = ProcfsMemoryUtil.readVmStat();
+ if (vmStat != null) {
+ pulledData.add(
+ FrameworkStatsLog.buildStatsEvent(
+ atomTag,
+ vmStat.oomKillCount));
+ }
+ return StatsManager.PULL_SUCCESS;
+ }
+
private void registerTemperature() {
int tagId = FrameworkStatsLog.TEMPERATURE;
mStatsManager.setPullAtomCallback(
diff --git a/services/core/java/com/android/server/tracing/TracingServiceProxy.java b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
index 8f22748..ff2f08b 100644
--- a/services/core/java/com/android/server/tracing/TracingServiceProxy.java
+++ b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
@@ -20,6 +20,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Binder;
import android.os.UserHandle;
import android.tracing.ITracingServiceProxy;
import android.util.Log;
@@ -30,6 +31,8 @@
* TracingServiceProxy is the system_server intermediary between the Perfetto tracing daemon and the
* system tracing app Traceur.
*
+ * Access to this service is restricted via SELinux. Normal apps do not have access.
+ *
* @hide
*/
public class TracingServiceProxy extends SystemService {
@@ -87,11 +90,15 @@
intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOPPED);
}
+ final long identity = Binder.clearCallingIdentity();
try {
mContext.startForegroundServiceAsUser(intent, UserHandle.SYSTEM);
} catch (RuntimeException e) {
Log.e(TAG, "Failed to notifyTraceSessionEnded", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
+
} catch (NameNotFoundException e) {
Log.e(TAG, "Failed to locate Traceur", e);
}
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index f7d6136..44a6d13 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -453,6 +453,10 @@
for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
}
+
+ // Update the mobile data state after updating the subscription snapshot as a change in
+ // subIds for a subGroup may affect the mobile data state.
+ handleMobileDataToggled();
}
private void handleMobileDataToggled() {
@@ -514,7 +518,11 @@
}
private String getLogPrefix() {
- return "[" + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) + "] ";
+ return "["
+ + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup)
+ + "-"
+ + System.identityHashCode(this)
+ + "] ";
}
private void logVdbg(String msg) {
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 4936970..c6f3f73 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -714,6 +714,18 @@
protected void onQuitting() {
logDbg("Quitting VcnGatewayConnection");
+ if (mNetworkAgent != null) {
+ logWtf("NetworkAgent was non-null in onQuitting");
+ mNetworkAgent.unregister();
+ mNetworkAgent = null;
+ }
+
+ if (mIkeSession != null) {
+ logWtf("IkeSession was non-null in onQuitting");
+ mIkeSession.kill();
+ mIkeSession = null;
+ }
+
// No need to call setInterfaceDown(); the IpSecInterface is being fully torn down.
if (mTunnelIface != null) {
mTunnelIface.close();
@@ -1863,6 +1875,7 @@
if (mUnderlying == null) {
logWtf("Underlying network was null in retry state");
+ teardownNetwork();
transitionTo(mDisconnectedState);
} else {
// Safe to blindly set up, as it is cancelled and cleared on exiting this state
@@ -1879,6 +1892,7 @@
// If new underlying is null, all networks were lost; go back to disconnected.
if (mUnderlying == null) {
+ teardownNetwork();
transitionTo(mDisconnectedState);
return;
} else if (oldUnderlying != null
@@ -2134,6 +2148,8 @@
+ LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup)
+ "-"
+ mConnectionConfig.getGatewayConnectionName()
+ + "-"
+ + System.identityHashCode(this)
+ "] ";
}
diff --git a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
index d439b94..1d8c64b 100644
--- a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
+++ b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java
@@ -22,6 +22,7 @@
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -59,6 +60,7 @@
convertStepsToRamps(segments);
int newRepeatIndex = addRampDownToZeroAmplitudeSegments(segments, repeatIndex);
newRepeatIndex = addRampDownToLoop(segments, newRepeatIndex);
+ newRepeatIndex = splitLongRampSegments(info, segments, newRepeatIndex);
return newRepeatIndex;
}
@@ -210,11 +212,70 @@
return repeatIndex;
}
+ /**
+ * Split {@link RampSegment} entries that have duration longer than {@link
+ * VibratorInfo#getPwlePrimitiveDurationMax()}.
+ */
+ private int splitLongRampSegments(VibratorInfo info, List<VibrationEffectSegment> segments,
+ int repeatIndex) {
+ int maxDuration = info.getPwlePrimitiveDurationMax();
+ if (maxDuration <= 0) {
+ // No limit set to PWLE primitive duration.
+ return repeatIndex;
+ }
+
+ int segmentCount = segments.size();
+ for (int i = 0; i < segmentCount; i++) {
+ if (!(segments.get(i) instanceof RampSegment)) {
+ continue;
+ }
+ RampSegment ramp = (RampSegment) segments.get(i);
+ int splits = ((int) ramp.getDuration() + maxDuration - 1) / maxDuration;
+ if (splits <= 1) {
+ continue;
+ }
+ segments.remove(i);
+ segments.addAll(i, splitRampSegment(ramp, splits));
+ int addedSegments = splits - 1;
+ if (repeatIndex > i) {
+ repeatIndex += addedSegments;
+ }
+ i += addedSegments;
+ segmentCount += addedSegments;
+ }
+
+ return repeatIndex;
+ }
+
private static RampSegment apply(StepSegment segment) {
return new RampSegment(segment.getAmplitude(), segment.getAmplitude(),
segment.getFrequency(), segment.getFrequency(), (int) segment.getDuration());
}
+ private static List<RampSegment> splitRampSegment(RampSegment ramp, int splits) {
+ List<RampSegment> ramps = new ArrayList<>(splits);
+ long splitDuration = ramp.getDuration() / splits;
+ float previousAmplitude = ramp.getStartAmplitude();
+ float previousFrequency = ramp.getStartFrequency();
+ long accumulatedDuration = 0;
+
+ for (int i = 1; i < splits; i++) {
+ accumulatedDuration += splitDuration;
+ RampSegment rampSplit = new RampSegment(
+ previousAmplitude, interpolateAmplitude(ramp, accumulatedDuration),
+ previousFrequency, interpolateFrequency(ramp, accumulatedDuration),
+ (int) splitDuration);
+ ramps.add(rampSplit);
+ previousAmplitude = rampSplit.getEndAmplitude();
+ previousFrequency = rampSplit.getEndFrequency();
+ }
+
+ ramps.add(new RampSegment(previousAmplitude, ramp.getEndAmplitude(), previousFrequency,
+ ramp.getEndFrequency(), (int) (ramp.getDuration() - accumulatedDuration)));
+
+ return ramps;
+ }
+
private static RampSegment createRampDown(float amplitude, float frequency, long duration) {
return new RampSegment(amplitude, /* endAmplitude= */ 0, frequency, frequency,
(int) duration);
@@ -244,4 +305,19 @@
}
return false;
}
+
+ private static float interpolateAmplitude(RampSegment ramp, long duration) {
+ return interpolate(ramp.getStartAmplitude(), ramp.getEndAmplitude(), duration,
+ ramp.getDuration());
+ }
+
+ private static float interpolateFrequency(RampSegment ramp, long duration) {
+ return interpolate(ramp.getStartFrequency(), ramp.getEndFrequency(), duration,
+ ramp.getDuration());
+ }
+
+ private static float interpolate(float start, float end, long duration, long totalDuration) {
+ float position = (float) duration / totalDuration;
+ return start + position * (end - start);
+ }
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 4a07c1a..885f0e4 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -19,7 +19,10 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IUidObserver;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.media.AudioManager;
@@ -61,6 +64,9 @@
private final SettingsObserver mSettingObserver;
@VisibleForTesting
final UidObserver mUidObserver;
+ @VisibleForTesting
+ final UserObserver mUserReceiver;
+
@GuardedBy("mLock")
private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>();
@@ -94,6 +100,7 @@
mContext = context;
mSettingObserver = new SettingsObserver(handler);
mUidObserver = new UidObserver();
+ mUserReceiver = new UserObserver();
VibrationEffect clickEffect = createEffectFromResource(
com.android.internal.R.array.config_virtualKeyVibePattern);
@@ -150,6 +157,7 @@
}
});
+ mContext.registerReceiver(mUserReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED));
registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES));
registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING));
registerSettingsObserver(Settings.Global.getUriFor(Settings.Global.APPLY_RAMPING_RINGER));
@@ -345,6 +353,7 @@
+ ", mLowPowerMode=" + mLowPowerMode
+ ", mZenMode=" + Settings.Global.zenModeToString(mZenMode)
+ ", mProcStatesCache=" + mUidObserver.mProcStatesCache
+ + ", mHapticChannelMaxVibrationAmplitude=" + getHapticChannelMaxVibrationAmplitude()
+ ", mHapticFeedbackIntensity="
+ intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_TOUCH))
+ ", mHapticFeedbackDefaultIntensity="
@@ -403,6 +412,12 @@
}
}
+ private float getHapticChannelMaxVibrationAmplitude() {
+ synchronized (mLock) {
+ return mVibrator == null ? Float.NaN : mVibrator.getHapticChannelMaximumAmplitude();
+ }
+ }
+
private int getSystemSetting(String settingName, int defaultValue) {
return Settings.System.getIntForUser(mContext.getContentResolver(),
settingName, defaultValue, UserHandle.USER_CURRENT);
@@ -457,6 +472,17 @@
}
}
+ /** Implementation of {@link BroadcastReceiver} to update settings on current user change. */
+ @VisibleForTesting
+ final class UserObserver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
+ updateSettings();
+ }
+ }
+ }
+
/** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */
@VisibleForTesting
final class UidObserver extends IUidObserver.Stub {
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 150fde9..45d5111 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -844,7 +844,12 @@
public List<Step> play() {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePrimitivesStep");
try {
- int segmentCount = effect.getSegments().size();
+ // Load the next PrimitiveSegments to create a single compose call to the vibrator,
+ // limited to the vibrator composition maximum size.
+ int limit = controller.getVibratorInfo().getCompositionSizeMax();
+ int segmentCount = limit > 0
+ ? Math.min(effect.getSegments().size(), segmentIndex + limit)
+ : effect.getSegments().size();
List<PrimitiveSegment> primitives = new ArrayList<>();
for (int i = segmentIndex; i < segmentCount; i++) {
VibrationEffectSegment segment = effect.getSegments().get(i);
@@ -896,7 +901,12 @@
public List<Step> play() {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePwleStep");
try {
- int segmentCount = effect.getSegments().size();
+ // Load the next RampSegments to create a single composePwle call to the vibrator,
+ // limited to the vibrator PWLE maximum size.
+ int limit = controller.getVibratorInfo().getPwleSizeMax();
+ int segmentCount = limit > 0
+ ? Math.min(effect.getSegments().size(), segmentIndex + limit)
+ : effect.getSegments().size();
List<RampSegment> pwles = new ArrayList<>();
for (int i = segmentIndex; i < segmentCount; i++) {
VibrationEffectSegment segment = effect.getSegments().get(i);
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 5dceac2d0..001d5c4 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -30,19 +30,24 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
import libcore.util.NativeAllocationRegistry;
/** Controls a single vibrator. */
final class VibratorController {
private static final String TAG = "VibratorController";
+ // TODO(b/167947076): load suggested range from config
+ private static final int SUGGESTED_FREQUENCY_SAFE_RANGE = 200;
private final Object mLock = new Object();
private final NativeWrapper mNativeWrapper;
- private final VibratorInfo mVibratorInfo;
+ private final VibratorInfo.Builder mVibratorInfoBuilder;
@GuardedBy("mLock")
+ private VibratorInfo mVibratorInfo;
+ @GuardedBy("mLock")
+ private boolean mVibratorInfoLoaded;
+ @GuardedBy("mLock")
private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners =
new RemoteCallbackList<>();
@GuardedBy("mLock")
@@ -66,10 +71,10 @@
NativeWrapper nativeWrapper) {
mNativeWrapper = nativeWrapper;
mNativeWrapper.init(vibratorId, listener);
- // TODO(b/167947076): load suggested range from config
- mVibratorInfo = mNativeWrapper.getInfo(/* suggestedFrequencyRange= */ 200);
- Preconditions.checkNotNull(mVibratorInfo, "Failed to retrieve data for vibrator %d",
- vibratorId);
+ mVibratorInfoBuilder = new VibratorInfo.Builder(vibratorId);
+ mVibratorInfoLoaded = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
+ mVibratorInfoBuilder);
+ mVibratorInfo = mVibratorInfoBuilder.build();
}
/** Register state listener for this vibrator. */
@@ -103,7 +108,15 @@
/** Return the {@link VibratorInfo} representing the vibrator controlled by this instance. */
public VibratorInfo getVibratorInfo() {
- return mVibratorInfo;
+ synchronized (mLock) {
+ if (!mVibratorInfoLoaded) {
+ // Try to load the vibrator metadata that has failed in the last attempt.
+ mVibratorInfoLoaded = mNativeWrapper.getInfo(SUGGESTED_FREQUENCY_SAFE_RANGE,
+ mVibratorInfoBuilder);
+ mVibratorInfo = mVibratorInfoBuilder.build();
+ }
+ return mVibratorInfo;
+ }
}
/**
@@ -361,7 +374,8 @@
private static native void alwaysOnDisable(long nativePtr, long id);
- private static native VibratorInfo getInfo(long nativePtr, float suggestedFrequencyRange);
+ private static native boolean getInfo(long nativePtr, float suggestedFrequencyRange,
+ VibratorInfo.Builder infoBuilder);
private long mNativePtr = 0;
@@ -428,9 +442,11 @@
alwaysOnDisable(mNativePtr, id);
}
- /** Return device vibrator metadata. */
- public VibratorInfo getInfo(float suggestedFrequencyRange) {
- return getInfo(mNativePtr, suggestedFrequencyRange);
+ /**
+ * Loads device vibrator metadata and returns true if all metadata was loaded successfully.
+ */
+ public boolean getInfo(float suggestedFrequencyRange, VibratorInfo.Builder infoBuilder) {
+ return getInfo(mNativePtr, suggestedFrequencyRange, infoBuilder);
}
}
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 2f0ed19..79706ea 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -723,6 +723,12 @@
* VibrationAttributes.USAGE_* values.
*/
private boolean shouldCancelVibration(VibrationAttributes attrs, int usageFilter) {
+ if (attrs.getUsage() == VibrationAttributes.USAGE_UNKNOWN) {
+ // Special case, usage UNKNOWN would match all filters. Instead it should only match if
+ // it's cancelling that usage specifically, or if cancelling all usages.
+ return usageFilter == VibrationAttributes.USAGE_UNKNOWN
+ || usageFilter == VibrationAttributes.USAGE_FILTER_MATCH_ALL;
+ }
return (usageFilter & attrs.getUsage()) == attrs.getUsage();
}
@@ -1535,7 +1541,7 @@
}
private int runCancel() {
- cancelVibrate(/* usageFilter= */ -1, mToken);
+ cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, mToken);
return 0;
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 53f1035..7713320 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -129,7 +129,9 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -403,20 +405,10 @@
needsExtraction = wallpaper.primaryColors == null;
}
- // Let's notify the current values, it's fine if it's null, it just means
- // that we don't know yet.
- notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId);
-
if (needsExtraction) {
extractColors(wallpaper);
- synchronized (mLock) {
- // Don't need to notify if nothing changed.
- if (wallpaper.primaryColors == null) {
- return;
- }
- }
- notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId);
}
+ notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId);
}
private static <T extends IInterface> boolean emptyCallbackList(RemoteCallbackList<T> list) {
@@ -3140,7 +3132,9 @@
}
}
- private void writeWallpaperAttributes(TypedXmlSerializer out, String tag,
+
+ @VisibleForTesting
+ void writeWallpaperAttributes(TypedXmlSerializer out, String tag,
WallpaperData wallpaper)
throws IllegalArgumentException, IllegalStateException, IOException {
if (DEBUG) {
@@ -3179,6 +3173,19 @@
out.attributeInt(null, "colorValue" + i, wc.toArgb());
}
}
+
+ int allColorsCount = wallpaper.primaryColors.getAllColors().size();
+ out.attributeInt(null, "allColorsCount", allColorsCount);
+ if (allColorsCount > 0) {
+ int index = 0;
+ for (Map.Entry<Integer, Integer> entry : wallpaper.primaryColors.getAllColors()
+ .entrySet()) {
+ out.attributeInt(null, "allColorsValue" + index, entry.getKey());
+ out.attributeInt(null, "allColorsPopulation" + index, entry.getValue());
+ index++;
+ }
+ }
+
out.attributeInt(null, "colorHints", wallpaper.primaryColors.getColorHints());
}
@@ -3410,7 +3417,8 @@
}
}
- private void parseWallpaperAttributes(TypedXmlPullParser parser, WallpaperData wallpaper,
+ @VisibleForTesting
+ void parseWallpaperAttributes(TypedXmlPullParser parser, WallpaperData wallpaper,
boolean keepDimensionHints) throws XmlPullParserException {
final int id = parser.getAttributeInt(null, "id", -1);
if (id != -1) {
@@ -3437,7 +3445,17 @@
wpData.mPadding.right = getAttributeInt(parser, "paddingRight", 0);
wpData.mPadding.bottom = getAttributeInt(parser, "paddingBottom", 0);
int colorsCount = getAttributeInt(parser, "colorsCount", 0);
- if (colorsCount > 0) {
+ int allColorsCount = getAttributeInt(parser, "allColorsCount", 0);
+ if (allColorsCount > 0) {
+ Map<Integer, Integer> allColors = new HashMap<>(allColorsCount);
+ for (int i = 0; i < allColorsCount; i++) {
+ int colorInt = getAttributeInt(parser, "allColorsValue" + i, 0);
+ int population = getAttributeInt(parser, "allColorsPopulation" + i, 0);
+ allColors.put(colorInt, population);
+ }
+ int colorHints = getAttributeInt(parser, "colorHints", 0);
+ wallpaper.primaryColors = new WallpaperColors(allColors, colorHints);
+ } else if (colorsCount > 0) {
Color primary = null, secondary = null, tertiary = null;
for (int i = 0; i < colorsCount; i++) {
Color color = Color.valueOf(getAttributeInt(parser, "colorValue" + i, 0));
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 71e31c3..a24319f 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -703,7 +703,7 @@
Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
+ " displayId: " + displayContent.getDisplayId());
}
- mMagnifedViewport.onRotationChanged(displayContent.getPendingTransaction());
+ mMagnifedViewport.onRotationChanged();
mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
}
@@ -858,7 +858,7 @@
private final RectF mTempRectF = new RectF();
- private final Point mTempPoint = new Point();
+ private final Point mScreenSize = new Point();
private final Matrix mTempMatrix = new Matrix();
@@ -887,8 +887,8 @@
if (mDisplayContext.getResources().getConfiguration().isScreenRound()) {
mCircularPath = new Path();
- mDisplay.getRealSize(mTempPoint);
- final int centerXY = mTempPoint.x / 2;
+ mDisplay.getRealSize(mScreenSize);
+ final int centerXY = mScreenSize.x / 2;
mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
} else {
mCircularPath = null;
@@ -917,9 +917,9 @@
}
void recomputeBounds() {
- mDisplay.getRealSize(mTempPoint);
- final int screenWidth = mTempPoint.x;
- final int screenHeight = mTempPoint.y;
+ mDisplay.getRealSize(mScreenSize);
+ final int screenWidth = mScreenSize.x;
+ final int screenHeight = mScreenSize.y;
mMagnificationRegion.set(0, 0, 0, 0);
final Region availableBounds = mTempRegion1;
@@ -1052,7 +1052,7 @@
|| windowType == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
}
- void onRotationChanged(SurfaceControl.Transaction t) {
+ void onRotationChanged() {
// If we are showing the magnification border, hide it immediately so
// the user does not see strange artifacts during rotation. The screenshot
// used for rotation already has the border. After the rotation is complete
@@ -1066,7 +1066,7 @@
mHandler.sendMessageDelayed(message, delay);
}
recomputeBounds();
- mWindow.updateSize(t);
+ mWindow.updateSize();
}
void setMagnifiedRegionBorderShown(boolean shown, boolean animate) {
@@ -1148,9 +1148,9 @@
/* ignore */
}
mSurfaceControl = surfaceControl;
- mDisplay.getRealSize(mTempPoint);
+ mDisplay.getRealSize(mScreenSize);
mBlastBufferQueue = new BLASTBufferQueue(SURFACE_TITLE, mSurfaceControl,
- mTempPoint.x, mTempPoint.y, PixelFormat.RGBA_8888);
+ mScreenSize.x, mScreenSize.y, PixelFormat.RGBA_8888);
final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
final int layer =
@@ -1224,10 +1224,11 @@
}
}
- void updateSize(SurfaceControl.Transaction t) {
+ void updateSize() {
synchronized (mService.mGlobalLock) {
- mDisplay.getRealSize(mTempPoint);
- t.setBufferSize(mSurfaceControl, mTempPoint.x, mTempPoint.y);
+ mDisplay.getRealSize(mScreenSize);
+ mBlastBufferQueue.update(mSurfaceControl, mScreenSize.x, mScreenSize.y,
+ PixelFormat.RGBA_8888);
invalidate(mDirtyRect);
}
}
@@ -1296,8 +1297,8 @@
pw.println(prefix
+ " mBounds= " + mBounds
+ " mDirtyRect= " + mDirtyRect
- + " mWidth= " + mSurfaceControl.getWidth()
- + " mHeight= " + mSurfaceControl.getHeight());
+ + " mWidth= " + mScreenSize.x
+ + " mHeight= " + mScreenSize.y);
}
private final class AnimationController extends Handler {
@@ -1541,6 +1542,52 @@
mEmbeddedDisplayIdList.add(displayId);
}
+ boolean shellRootIsAbove(WindowState windowState, ShellRoot shellRoot) {
+ int wsLayer = mService.mPolicy.getWindowLayerLw(windowState);
+ int shellLayer = mService.mPolicy.getWindowLayerFromTypeLw(shellRoot.getWindowType(),
+ true);
+ return shellLayer >= wsLayer;
+ }
+
+ int addShellRootsIfAbove(WindowState windowState, ArrayList<ShellRoot> shellRoots,
+ int shellRootIndex, List<WindowInfo> windows, Set<IBinder> addedWindows,
+ Region unaccountedSpace, boolean focusedWindowAdded) {
+ while (shellRootIndex < shellRoots.size()
+ && shellRootIsAbove(windowState, shellRoots.get(shellRootIndex))) {
+ ShellRoot shellRoot = shellRoots.get(shellRootIndex);
+ shellRootIndex++;
+ final WindowInfo info = shellRoot.getWindowInfo();
+ if (info == null) {
+ continue;
+ }
+
+ info.layer = addedWindows.size();
+ windows.add(info);
+ addedWindows.add(info.token);
+ unaccountedSpace.op(info.regionInScreen, unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
+ if (unaccountedSpace.isEmpty() && focusedWindowAdded) {
+ break;
+ }
+ }
+ return shellRootIndex;
+ }
+
+ private ArrayList<ShellRoot> getSortedShellRoots(
+ SparseArray<ShellRoot> originalShellRoots) {
+ ArrayList<ShellRoot> sortedShellRoots = new ArrayList<>(originalShellRoots.size());
+ for (int i = originalShellRoots.size() - 1; i >= 0; --i) {
+ sortedShellRoots.add(originalShellRoots.valueAt(i));
+ }
+
+ sortedShellRoots.sort((left, right) ->
+ mService.mPolicy.getWindowLayerFromTypeLw(right.getWindowType(), true)
+ - mService.mPolicy.getWindowLayerFromTypeLw(left.getWindowType(),
+ true));
+
+ return sortedShellRoots;
+ }
+
/**
* Check if windows have changed, and send them to the accessibility subsystem if they have.
*
@@ -1600,9 +1647,22 @@
final int visibleWindowCount = visibleWindows.size();
HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>();
+ ArrayList<ShellRoot> shellRoots = getSortedShellRoots(dc.mShellRoots);
+
// Iterate until we figure out what is touchable for the entire screen.
+ int shellRootIndex = 0;
for (int i = visibleWindowCount - 1; i >= 0; i--) {
final WindowState windowState = visibleWindows.valueAt(i);
+ int prevShellRootIndex = shellRootIndex;
+ shellRootIndex = addShellRootsIfAbove(windowState, shellRoots, shellRootIndex,
+ windows, addedWindows, unaccountedSpace, focusedWindowAdded);
+
+ // If a Shell Root was added, it could have accounted for all the space already.
+ if (shellRootIndex > prevShellRootIndex && unaccountedSpace.isEmpty()
+ && focusedWindowAdded) {
+ break;
+ }
+
final Region regionInScreen = new Region();
computeWindowRegionInScreen(windowState, regionInScreen);
@@ -1626,16 +1686,6 @@
}
}
- for (int i = dc.mShellRoots.size() - 1; i >= 0; --i) {
- final WindowInfo info = dc.mShellRoots.valueAt(i).getWindowInfo();
- if (info == null) {
- continue;
- }
- info.layer = addedWindows.size();
- windows.add(info);
- addedWindows.add(info.token);
- }
-
// Remove child/parent references to windows that were not added.
final int windowCount = windows.size();
for (int i = 0; i < windowCount; i++) {
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 14f6fb3..0f6a718 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -206,8 +206,8 @@
* observer to identify which callbacks belong to a launch event.
*/
final long mTransitionStartTimeNs;
- /** The device uptime in seconds when this transition info is created. */
- final int mCurrentTransitionDeviceUptime;
+ /** The device uptime in millis when this transition info is created. */
+ final long mTransitionDeviceUptimeMs;
/** The type can be cold (new process), warm (new activity), or hot (bring to front). */
final int mTransitionType;
/** Whether the process was already running when the transition started. */
@@ -277,8 +277,7 @@
mTransitionType = transitionType;
mProcessRunning = processRunning;
mProcessSwitch = processSwitch;
- mCurrentTransitionDeviceUptime =
- (int) TimeUnit.MILLISECONDS.toSeconds(launchingState.mCurrentUpTimeMs);
+ mTransitionDeviceUptimeMs = launchingState.mCurrentUpTimeMs;
setLatestLaunchedActivity(r);
launchingState.mAssociatedTransitionInfo = this;
if (options != null) {
@@ -610,13 +609,27 @@
return;
}
+ // If the launched activity is started from an existing active transition, it will be put
+ // into the transition info.
if (info != null && info.canCoalesce(launchedActivity)) {
- // If we are already in an existing transition on the same display, only update the
- // activity name, but not the other attributes.
+ if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunched consecutive launch");
- if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunched update launched activity");
+ final boolean crossPackage =
+ !info.mLastLaunchedActivity.packageName.equals(launchedActivity.packageName);
+ // The trace name uses package name so different packages should be separated.
+ if (crossPackage) {
+ stopLaunchTrace(info);
+ }
+
+ mLastTransitionInfo.remove(info.mLastLaunchedActivity);
// Coalesce multiple (trampoline) activities from a single sequence together.
info.setLatestLaunchedActivity(launchedActivity);
+ // Update the latest one so it can be found when reporting fully-drawn.
+ mLastTransitionInfo.put(launchedActivity, info);
+
+ if (crossPackage) {
+ startLaunchTrace(info);
+ }
return;
}
@@ -639,12 +652,24 @@
launchObserverNotifyIntentFailed();
}
if (launchedActivity.mDisplayContent.isSleeping()) {
- // It is unknown whether the activity can be drawn or not, e.g. ut depends on the
+ // It is unknown whether the activity can be drawn or not, e.g. it depends on the
// keyguard states and the attributes or flags set by the activity. If the activity
// keeps invisible in the grace period, the tracker will be cancelled so it won't get
// a very long launch time that takes unlocking as the end of launch.
scheduleCheckActivityToBeDrawn(launchedActivity, UNKNOWN_VISIBILITY_CHECK_DELAY_MS);
}
+
+ // If the previous transitions are no longer visible, abort them to avoid counting the
+ // launch time when resuming from back stack. E.g. launch 2 independent tasks in a short
+ // time, the transition info of the first task should not keep active until it becomes
+ // visible such as after the top task is finished.
+ for (int i = mTransitionInfoList.size() - 2; i >= 0; i--) {
+ final TransitionInfo prevInfo = mTransitionInfoList.get(i);
+ prevInfo.updatePendingDraw();
+ if (prevInfo.allDrawn()) {
+ abort(prevInfo, "nothing will be drawn");
+ }
+ }
}
/**
@@ -865,6 +890,7 @@
final Boolean isHibernating =
mLastHibernationStates.remove(info.mLastLaunchedActivity.packageName);
if (abort) {
+ mLastTransitionInfo.remove(info.mLastLaunchedActivity);
mSupervisor.stopWaitingForActivityVisible(info.mLastLaunchedActivity);
launchObserverNotifyActivityLaunchCancelled(info);
} else {
@@ -908,7 +934,7 @@
final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info);
if (info.isInterestingToLoggerAndObserver()) {
mLoggerHandler.post(() -> logAppTransition(
- info.mCurrentTransitionDeviceUptime, info.mCurrentTransitionDelayMs,
+ info.mTransitionDeviceUptimeMs, info.mCurrentTransitionDelayMs,
infoSnapshot, isHibernating));
}
mLoggerHandler.post(() -> logAppDisplayed(infoSnapshot));
@@ -920,7 +946,7 @@
}
// This gets called on another thread without holding the activity manager lock.
- private void logAppTransition(int currentTransitionDeviceUptime, int currentTransitionDelayMs,
+ private void logAppTransition(long transitionDeviceUptimeMs, int currentTransitionDelayMs,
TransitionInfoSnapshot info, boolean isHibernating) {
final LogMaker builder = new LogMaker(APP_TRANSITION);
builder.setPackageName(info.packageName);
@@ -937,7 +963,7 @@
}
builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL, isInstantApp ? 1 : 0);
builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS,
- currentTransitionDeviceUptime);
+ TimeUnit.MILLISECONDS.toSeconds(transitionDeviceUptimeMs));
builder.addTaggedData(APP_TRANSITION_DELAY_MS, currentTransitionDelayMs);
builder.setSubtype(info.reason);
if (info.startingWindowDelayMs != INVALID_DELAY) {
@@ -972,7 +998,7 @@
info.launchedActivityName,
info.launchedActivityLaunchedFromPackage,
isInstantApp,
- currentTransitionDeviceUptime * 1000,
+ transitionDeviceUptimeMs,
info.reason,
currentTransitionDelayMs,
info.startingWindowDelayMs,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 8cc6a42..0ad8782 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -38,6 +38,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -277,6 +278,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.storage.StorageManager;
+import android.service.contentcapture.ActivityEvent;
import android.service.dreams.DreamActivity;
import android.service.dreams.DreamManagerInternal;
import android.service.voice.IVoiceInteractionSession;
@@ -327,6 +329,7 @@
import com.android.server.LocalServices;
import com.android.server.am.AppTimeTracker;
import com.android.server.am.PendingIntentRecord;
+import com.android.server.contentcapture.ContentCaptureManagerInternal;
import com.android.server.display.color.ColorDisplayService;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.uri.NeededUriGrants;
@@ -647,6 +650,9 @@
private boolean mLastContainsDismissKeyguardWindow;
private boolean mLastContainsTurnScreenOnWindow;
+ /** Whether the IME is showing when transitioning away from this activity. */
+ boolean mLastImeShown;
+
/**
* A flag to determine if this AR is in the process of closing or entering PIP. This is needed
* to help AR know that the app is in the process of closing but hasn't yet started closing on
@@ -674,8 +680,6 @@
private boolean mInSizeCompatModeForBounds = false;
// Whether the aspect ratio restrictions applied to the activity bounds in applyAspectRatio().
- // TODO(b/182268157): Aspect ratio can also be applie in resolveFixedOrientationConfiguration
- // but that isn't reflected in this boolean.
private boolean mIsAspectRatioApplied = false;
// Bounds populated in resolveFixedOrientationConfiguration when this activity is letterboxed
@@ -1221,6 +1225,7 @@
TopResumedActivityChangeItem.obtain(onTop));
} catch (RemoteException e) {
// If process died, whatever.
+ Slog.w(TAG, "Failed to send top-resumed=" + onTop + " to " + this, e);
return false;
}
return true;
@@ -1784,17 +1789,19 @@
/**
* Evaluate the theme for a starting window.
+ * @param prev Previous activity which may have a starting window.
* @param originalTheme The original theme which read from activity or application.
* @param replaceTheme The replace theme which requested from starter.
* @return Resolved theme.
*/
- private int evaluateStartingWindowTheme(String pkg, int originalTheme, int replaceTheme) {
+ private int evaluateStartingWindowTheme(ActivityRecord prev, String pkg, int originalTheme,
+ int replaceTheme) {
// Skip if the package doesn't want a starting window.
- if (!validateStartingWindowTheme(pkg, originalTheme)) {
+ if (!validateStartingWindowTheme(prev, pkg, originalTheme)) {
return 0;
}
int selectedTheme = originalTheme;
- if (replaceTheme != 0 && validateStartingWindowTheme(pkg, replaceTheme)) {
+ if (replaceTheme != 0 && validateStartingWindowTheme(prev, pkg, replaceTheme)) {
// allow to replace theme
selectedTheme = replaceTheme;
}
@@ -1831,7 +1838,7 @@
return LAUNCH_SOURCE_TYPE_APPLICATION;
}
- private boolean validateStartingWindowTheme(String pkg, int theme) {
+ private boolean validateStartingWindowTheme(ActivityRecord prev, String pkg, int theme) {
// If this is a translucent window, then don't show a starting window -- the current
// effect (a full-screen opaque starting window that fades away to the real contents
// when it is ready) does not work for this.
@@ -1868,7 +1875,11 @@
return false;
}
if (windowDisableStarting && !launchedFromSystemSurface()) {
- return false;
+ // Check if previous activity can transfer the starting window to this activity.
+ return prev != null && prev.getActivityType() == ACTIVITY_TYPE_STANDARD
+ && prev.mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_IDLE
+ && (prev.mStartingData != null
+ || (prev.mStartingWindow != null && prev.mStartingSurface != null));
}
return true;
}
@@ -4593,7 +4604,7 @@
if (visible) {
displayContent.mOpeningApps.add(this);
mEnteringAnimation = true;
- } else {
+ } else if (mVisible) {
displayContent.mClosingApps.add(this);
mEnteringAnimation = false;
}
@@ -4721,6 +4732,15 @@
setClientVisible(visible);
}
+ if (!visible) {
+ final InsetsControlTarget imeInputTarget = mDisplayContent.getImeTarget(
+ DisplayContent.IME_TARGET_INPUT);
+ mLastImeShown = imeInputTarget != null && imeInputTarget.getWindow() != null
+ && imeInputTarget.getWindow().mActivityRecord == this
+ && mDisplayContent.mInputMethodWindow != null
+ && mDisplayContent.mInputMethodWindow.isVisible();
+ }
+
final DisplayContent displayContent = getDisplayContent();
if (!displayContent.mClosingApps.contains(this)
&& !displayContent.mOpeningApps.contains(this)) {
@@ -4860,6 +4880,12 @@
true /* activityChange */, true /* updateOomAdj */,
true /* addPendingTopUid */);
}
+ final ContentCaptureManagerInternal contentCaptureService =
+ LocalServices.getService(ContentCaptureManagerInternal.class);
+ if (contentCaptureService != null) {
+ contentCaptureService.notifyActivityEvent(mUserId, mActivityComponent,
+ ActivityEvent.TYPE_ACTIVITY_STARTED);
+ }
break;
case PAUSED:
mAtmService.updateBatteryStats(this, false);
@@ -5764,10 +5790,24 @@
}
void onStartingWindowDrawn() {
+ boolean wasTaskVisible = false;
if (task != null) {
mSplashScreenStyleEmpty = true;
+ wasTaskVisible = task.getHasBeenVisible();
task.setHasBeenVisible(true);
}
+
+ // The transition may not be executed if the starting process hasn't attached. But if the
+ // starting window is drawn, the transition can start earlier. Exclude finishing and bubble
+ // because it may be a trampoline.
+ if (!wasTaskVisible && mStartingData != null && !finishing && !mLaunchedFromBubble
+ && !mDisplayContent.mAppTransition.isReady()
+ && !mDisplayContent.mAppTransition.isRunning()) {
+ // The pending transition state will be cleared after the transition is started, so
+ // save the state for launching the client later (used by LaunchActivityItem).
+ mStartingData.mIsTransitionForward = mDisplayContent.isNextTransitionForward();
+ mDisplayContent.executeAppTransition();
+ }
}
/** Called when the windows associated app window container are drawn. */
@@ -6251,15 +6291,21 @@
mSplashScreenStyleEmpty = shouldUseEmptySplashScreen(sourceRecord);
- final int resolvedTheme = evaluateStartingWindowTheme(packageName, theme,
+ final int resolvedTheme = evaluateStartingWindowTheme(prev, packageName, theme,
splashScreenTheme);
+ final boolean activityCreated =
+ mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal();
+ // If this activity is just created and all activities below are finish, treat this
+ // scenario as warm launch.
+ final boolean newSingleActivity = !newTask && !activityCreated
+ && task.getActivity((r) -> !r.finishing && r != this) == null;
+
final boolean shown = addStartingWindow(packageName, resolvedTheme,
compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
- prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
- allowTaskSnapshot(),
- mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal(),
- mSplashScreenStyleEmpty);
+ prev != null ? prev.appToken : null,
+ newTask || newSingleActivity, taskSwitch, isProcessRunning(),
+ allowTaskSnapshot(), activityCreated, mSplashScreenStyleEmpty);
if (shown) {
mStartingWindowState = STARTING_WINDOW_SHOWN;
}
@@ -6480,6 +6526,11 @@
|| dc.mChangingContainers.contains(this));
}
+ boolean isTransitionForward() {
+ return (mStartingData != null && mStartingData.mIsTransitionForward)
+ || mDisplayContent.isNextTransitionForward();
+ }
+
private int getAnimationLayer() {
// The leash is parented to the animation layer. We need to preserve the z-order by using
// the prefix order index, but we boost if necessary.
@@ -7234,41 +7285,48 @@
}
final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
- final int parentWidth = parentBounds.width();
- final int parentHeight = parentBounds.height();
- float aspect = Math.max(parentWidth, parentHeight)
- / (float) Math.min(parentWidth, parentHeight);
+ final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds();
+ final Rect containingBounds = new Rect();
+ final Rect containingAppBounds = new Rect();
+ // Need to shrink the containing bounds into a square because the parent orientation does
+ // not match the activity requested orientation.
+ if (forcedOrientation == ORIENTATION_LANDSCAPE) {
+ // Shrink height to match width. Position height within app bounds.
+ final int bottom = Math.min(parentAppBounds.top + parentBounds.width(),
+ parentAppBounds.bottom);
+ containingBounds.set(parentBounds.left, parentAppBounds.top, parentBounds.right,
+ bottom);
+ containingAppBounds.set(parentAppBounds.left, parentAppBounds.top,
+ parentAppBounds.right, bottom);
+ } else {
+ // Shrink width to match height. Position width within app bounds.
+ final int right = Math.min(parentAppBounds.left + parentBounds.height(),
+ parentAppBounds.right);
+ containingBounds.set(parentAppBounds.left, parentBounds.top, right,
+ parentBounds.bottom);
+ containingAppBounds.set(parentAppBounds.left, parentAppBounds.top, right,
+ parentAppBounds.bottom);
+ }
+
+ Rect mTmpFullBounds = new Rect(resolvedBounds);
+ resolvedBounds.set(containingBounds);
// Override from config_fixedOrientationLetterboxAspectRatio or via ADB with
// set-fixed-orientation-letterbox-aspect-ratio.
final float letterboxAspectRatioOverride =
mWmService.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
- aspect = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
- ? letterboxAspectRatioOverride : aspect;
+ final float desiredAspectRatio =
+ letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
+ ? letterboxAspectRatioOverride : computeAspectRatio(parentBounds);
+ // Apply aspect ratio to resolved bounds
+ mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingAppBounds,
+ containingBounds, desiredAspectRatio, true);
- // Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in
- // order to use the extra available space.
- final float maxAspectRatio = info.getMaxAspectRatio();
- final float minAspectRatio = info.getMinAspectRatio();
- if (aspect > maxAspectRatio && maxAspectRatio != 0) {
- aspect = maxAspectRatio;
- } else if (aspect < minAspectRatio) {
- aspect = minAspectRatio;
- }
-
- // Store the current bounds to be able to revert to size compat mode values below if needed.
- Rect mTmpFullBounds = new Rect(resolvedBounds);
+ // Vertically center if orientation is landscape. Bounds will later be horizontally centered
+ // in {@link updateResolvedBoundsHorizontalPosition()} regardless of orientation.
if (forcedOrientation == ORIENTATION_LANDSCAPE) {
- final int height = (int) Math.rint(parentWidth / aspect);
- final int top = parentBounds.centerY() - height / 2;
- resolvedBounds.set(parentBounds.left, top, parentBounds.right, top + height);
- } else {
- final int width = (int) Math.rint(parentHeight / aspect);
- final Rect parentAppBounds = newParentConfig.windowConfiguration.getAppBounds();
- final int left = width <= parentAppBounds.width()
- // Avoid overlapping with the horizontal decor area when possible.
- ? parentAppBounds.left : parentBounds.centerX() - width / 2;
- resolvedBounds.set(left, parentBounds.top, left + width, parentBounds.bottom);
+ final int offsetY = parentBounds.centerY() - resolvedBounds.centerY();
+ resolvedBounds.offset(0, offsetY);
}
if (mCompatDisplayInsets != null) {
@@ -7310,8 +7368,6 @@
// then they should be aligned later in #updateResolvedBoundsHorizontalPosition().
if (!mTmpBounds.isEmpty()) {
resolvedBounds.set(mTmpBounds);
- // Exclude the horizontal decor area.
- resolvedBounds.left = parentAppBounds.left;
}
if (!resolvedBounds.isEmpty() && !resolvedBounds.equals(parentBounds)) {
// Compute the configuration based on the resolved bounds. If aspect ratio doesn't
@@ -7372,13 +7428,6 @@
mIsAspectRatioApplied =
applyAspectRatio(resolvedBounds, containingAppBounds, containingBounds);
}
- // If the bounds are restricted by fixed aspect ratio, the resolved bounds should be put in
- // the container app bounds. Otherwise the entire container bounds are available.
- final boolean fillContainer = resolvedBounds.equals(containingBounds);
- if (!fillContainer) {
- // The horizontal position should not cover insets.
- resolvedBounds.left = containingAppBounds.left;
- }
// Use resolvedBounds to compute other override configurations such as appBounds. The bounds
// are calculated in compat container space. The actual position on screen will be applied
@@ -7445,6 +7494,7 @@
// Align to top of parent (bounds) - this is a UX choice and exclude the horizontal decor
// if needed. Horizontal position is adjusted in updateResolvedBoundsHorizontalPosition.
// Above coordinates are in "@" space, now place "*" and "#" to screen space.
+ final boolean fillContainer = resolvedBounds.equals(containingBounds);
final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left;
final int screenPosY = containerBounds.top;
if (screenPosX != 0 || screenPosY != 0) {
@@ -7529,10 +7579,6 @@
if (getUid() == SYSTEM_UID) {
return false;
}
- // Do not sandbox to activity window bounds if the feature is disabled.
- if (mDisplayContent != null && !mDisplayContent.sandboxDisplayApis()) {
- return false;
- }
// Never apply sandboxing to an app that should be explicitly excluded from the config.
if (info != null && info.neverSandboxDisplayApis()) {
return false;
@@ -7685,6 +7731,12 @@
return true;
}
+ private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds,
+ Rect containingBounds) {
+ return applyAspectRatio(outBounds, containingAppBounds, containingBounds,
+ 0 /* desiredAspectRatio */, false /* fixedOrientationLetterboxed */);
+ }
+
/**
* Applies aspect ratio restrictions to outBounds. If no restrictions, then no change is
* made to outBounds.
@@ -7693,17 +7745,19 @@
*/
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds,
- Rect containingBounds) {
+ Rect containingBounds, float desiredAspectRatio, boolean fixedOrientationLetterboxed) {
final float maxAspectRatio = info.getMaxAspectRatio();
final Task rootTask = getRootTask();
final float minAspectRatio = info.getMinAspectRatio();
if (task == null || rootTask == null
- || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets())
- || (maxAspectRatio == 0 && minAspectRatio == 0)
+ || (inMultiWindowMode() && !shouldCreateCompatDisplayInsets()
+ && !fixedOrientationLetterboxed)
+ || (maxAspectRatio < 1 && minAspectRatio < 1 && desiredAspectRatio < 1)
|| isInVrUiMode(getConfiguration())) {
- // We don't enforce aspect ratio if the activity task is in multiwindow unless it
- // is in size-compat mode. We also don't set it if we are in VR mode.
+ // We don't enforce aspect ratio if the activity task is in multiwindow unless it is in
+ // size-compat mode or is letterboxed from fixed orientation. We also don't set it if we
+ // are in VR mode.
return false;
}
@@ -7711,20 +7765,30 @@
final int containingAppHeight = containingAppBounds.height();
final float containingRatio = computeAspectRatio(containingAppBounds);
+ if (desiredAspectRatio < 1) {
+ desiredAspectRatio = containingRatio;
+ }
+
+ if (maxAspectRatio >= 1 && desiredAspectRatio > maxAspectRatio) {
+ desiredAspectRatio = maxAspectRatio;
+ } else if (minAspectRatio >= 1 && desiredAspectRatio < minAspectRatio) {
+ desiredAspectRatio = minAspectRatio;
+ }
+
int activityWidth = containingAppWidth;
int activityHeight = containingAppHeight;
- if (containingRatio > maxAspectRatio && maxAspectRatio != 0) {
+ if (containingRatio > desiredAspectRatio) {
if (containingAppWidth < containingAppHeight) {
// Width is the shorter side, so we use that to figure-out what the max. height
// should be given the aspect ratio.
- activityHeight = (int) ((activityWidth * maxAspectRatio) + 0.5f);
+ activityHeight = (int) ((activityWidth * desiredAspectRatio) + 0.5f);
} else {
// Height is the shorter side, so we use that to figure-out what the max. width
// should be given the aspect ratio.
- activityWidth = (int) ((activityHeight * maxAspectRatio) + 0.5f);
+ activityWidth = (int) ((activityHeight * desiredAspectRatio) + 0.5f);
}
- } else if (containingRatio < minAspectRatio) {
+ } else if (containingRatio < desiredAspectRatio) {
boolean adjustWidth;
switch (getRequestedConfigurationOrientation()) {
case ORIENTATION_LANDSCAPE:
@@ -7752,9 +7816,9 @@
break;
}
if (adjustWidth) {
- activityWidth = (int) ((activityHeight / minAspectRatio) + 0.5f);
+ activityWidth = (int) ((activityHeight / desiredAspectRatio) + 0.5f);
} else {
- activityHeight = (int) ((activityWidth / minAspectRatio) + 0.5f);
+ activityHeight = (int) ((activityWidth / desiredAspectRatio) + 0.5f);
}
}
@@ -7778,6 +7842,13 @@
}
outBounds.set(containingBounds.left, containingBounds.top, right, bottom);
+ // If the bounds are restricted by fixed aspect ratio, then out bounds should be put in the
+ // container app bounds. Otherwise the entire container bounds are available.
+ if (!outBounds.equals(containingBounds)) {
+ // The horizontal position should not cover insets.
+ outBounds.left = containingAppBounds.left;
+ }
+
return true;
}
@@ -8085,8 +8156,7 @@
preserveWindow);
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
- lifecycleItem = ResumeActivityItem.obtain(
- mDisplayContent.isNextTransitionForward());
+ lifecycleItem = ResumeActivityItem.obtain(isTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 01ee3be..1759cde 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -280,11 +280,6 @@
public abstract boolean isRecentsComponentHomeActivity(int userId);
/**
- * Cancels any currently running recents animation.
- */
- public abstract void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition);
-
- /**
* Returns true if the app can close system dialogs. Otherwise it either throws a {@link
* SecurityException} or returns false with a logcat message depending on whether the app
* targets SDK level {@link android.os.Build.VERSION_CODES#S} or not.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5e75ceb..569c11b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -580,19 +580,28 @@
* windowing modes.
* 0: If it is a small screen (smallest width < {@link #mLargeScreenSmallestScreenWidthDp}),
* the device compares the activity min width/height with the min multi windowing modes
- * dimensions {@link #mMinPercentageMultiWindowSupportWidth} the device supports to
+ * dimensions {@link #mMinPercentageMultiWindowSupportHeight} the device supports to
* determine whether the activity can be shown in multi windowing modes
* 1: The device always compare the activity min width/height with the min multi windowing
- * modes dimensions {@link #mMinPercentageMultiWindowSupportWidth} the device supports to
+ * modes dimensions {@link #mMinPercentageMultiWindowSupportHeight} the device supports to
* determine whether it can be shown in multi windowing modes.
*/
int mRespectsActivityMinWidthHeightMultiWindow;
/**
- * This value is only used when the device checks activity min width/height to determine if it
+ * This value is only used when the device checks activity min height to determine if it
* can be shown in multi windowing modes.
- * If the activity min width/height is greater than this percentage of the display smallest
- * width, it will not be allowed to be shown in multi windowing modes.
+ * If the activity min height is greater than this percentage of the display height in portrait,
+ * it will not be allowed to be shown in multi windowing modes.
+ * The value should be between [0 - 1].
+ */
+ float mMinPercentageMultiWindowSupportHeight;
+
+ /**
+ * This value is only used when the device checks activity min width to determine if it
+ * can be shown in multi windowing modes.
+ * If the activity min width is greater than this percentage of the display width in landscape,
+ * it will not be allowed to be shown in multi windowing modes.
* The value should be between [0 - 1].
*/
float mMinPercentageMultiWindowSupportWidth;
@@ -840,6 +849,8 @@
com.android.internal.R.integer.config_supportsNonResizableMultiWindow);
final int respectsActivityMinWidthHeightMultiWindow = mContext.getResources().getInteger(
com.android.internal.R.integer.config_respectsActivityMinWidthHeightMultiWindow);
+ final float minPercentageMultiWindowSupportHeight = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_minPercentageMultiWindowSupportHeight);
final float minPercentageMultiWindowSupportWidth = mContext.getResources().getFloat(
com.android.internal.R.dimen.config_minPercentageMultiWindowSupportWidth);
final int largeScreenSmallestScreenWidthDp = mContext.getResources().getInteger(
@@ -860,6 +871,7 @@
mDevEnableNonResizableMultiWindow = devEnableNonResizableMultiWindow;
mSupportsNonResizableMultiWindow = supportsNonResizableMultiWindow;
mRespectsActivityMinWidthHeightMultiWindow = respectsActivityMinWidthHeightMultiWindow;
+ mMinPercentageMultiWindowSupportHeight = minPercentageMultiWindowSupportHeight;
mMinPercentageMultiWindowSupportWidth = minPercentageMultiWindowSupportWidth;
mLargeScreenSmallestScreenWidthDp = largeScreenSmallestScreenWidthDp;
final boolean multiWindowFormEnabled = freeformWindowManagement
@@ -4146,21 +4158,21 @@
/**
* Update the asset configuration and increase the assets sequence number.
- * @param processes the processes that needs to update the asset configuration, if none
- * updates the global configuration for all processes.
+ * @param processes the processes that needs to update the asset configuration
*/
- public void updateAssetConfiguration(List<WindowProcessController> processes) {
+ public void updateAssetConfiguration(List<WindowProcessController> processes,
+ boolean updateFrameworkRes) {
synchronized (mGlobalLock) {
final int assetSeq = increaseAssetConfigurationSeq();
- // Update the global configuration if the no target processes
- if (processes == null) {
+ if (updateFrameworkRes) {
Configuration newConfig = new Configuration();
newConfig.assetsSeq = assetSeq;
updateConfiguration(newConfig);
- return;
}
+ // Always update the override of every process so the asset sequence of the process is
+ // always greater than or equal to the global configuration.
for (int i = processes.size() - 1; i >= 0; i--) {
final WindowProcessController wpc = processes.get(i);
wpc.updateAssetConfiguration(assetSeq);
@@ -5256,11 +5268,6 @@
}
@Override
- public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) {
- ActivityTaskManagerService.this.cancelRecentsAnimation(restoreHomeRootTaskPosition);
- }
-
- @Override
public boolean checkCanCloseSystemDialogs(int pid, int uid, @Nullable String packageName) {
return ActivityTaskManagerService.this.checkCanCloseSystemDialogs(pid, uid,
packageName);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 7fe0f5b..d3d1c1c 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -836,7 +836,7 @@
final ClientTransaction clientTransaction = ClientTransaction.obtain(
proc.getThread(), r.appToken);
- final DisplayContent dc = r.mDisplayContent;
+ final boolean isTransitionForward = r.isTransitionForward();
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
@@ -845,7 +845,7 @@
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
r.getSavedState(), r.getPersistentSavedState(), results, newIntents,
- r.takeOptions(), dc.isNextTransitionForward(),
+ r.takeOptions(), isTransitionForward,
proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
r.createFixedRotationAdjustmentsIfNeeded(), r.shareableActivityToken,
r.getLaunchedFromBubble()));
@@ -853,7 +853,7 @@
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
- lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
+ lifecycleItem = ResumeActivityItem.obtain(isTransitionForward);
} else {
lifecycleItem = PauseActivityItem.obtain();
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index eaebb6f..4acadb2 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -623,7 +623,11 @@
siblings.add(current);
boolean canPromote = true;
- if (parent == null || !parent.canCreateRemoteAnimationTarget()) {
+ if (parent == null || !parent.canCreateRemoteAnimationTarget()
+ // We cannot promote the animation on Task's parent when the task is in
+ // clearing task in case the animating get stuck when performing the opening
+ // task that behind it.
+ || (current.asTask() != null && current.asTask().mInRemoveTask)) {
canPromote = false;
} else {
// In case a descendant of the parent belongs to the other group, we cannot promote
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index 0ec0142..2ea043a 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -75,10 +75,18 @@
* CompatModePackages#DOWNSCALED is the gatekeeper of all per-app buffer downscaling
* changes. Disabling this change will prevent the following scaling factors from working:
* CompatModePackages#DOWNSCALE_90
+ * CompatModePackages#DOWNSCALE_85
* CompatModePackages#DOWNSCALE_80
+ * CompatModePackages#DOWNSCALE_75
* CompatModePackages#DOWNSCALE_70
+ * CompatModePackages#DOWNSCALE_65
* CompatModePackages#DOWNSCALE_60
+ * CompatModePackages#DOWNSCALE_55
* CompatModePackages#DOWNSCALE_50
+ * CompatModePackages#DOWNSCALE_45
+ * CompatModePackages#DOWNSCALE_40
+ * CompatModePackages#DOWNSCALE_35
+ * CompatModePackages#DOWNSCALE_30
*
* If CompatModePackages#DOWNSCALED is enabled for an app package, then the app will be forcibly
* resized to the highest enabled scaling factor e.g. 80% if both 80% and 70% were enabled.
@@ -100,6 +108,16 @@
/**
* With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_85 for a package will force the app to assume it's
+ * running on a display with 85% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long DOWNSCALE_85 = 189969734L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
* CompatModePackages#DOWNSCALE_80 for a package will force the app to assume it's
* running on a display with 80% the vertical and horizontal resolution of the real display.
*/
@@ -110,6 +128,16 @@
/**
* With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_75 for a package will force the app to assume it's
+ * running on a display with 75% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long DOWNSCALE_75 = 189969779L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
* CompatModePackages#DOWNSCALE_70 for a package will force the app to assume it's
* running on a display with 70% the vertical and horizontal resolution of the real display.
*/
@@ -120,6 +148,16 @@
/**
* With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_65 for a package will force the app to assume it's
+ * running on a display with 65% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long DOWNSCALE_65 = 189969744L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
* CompatModePackages#DOWNSCALE_60 for a package will force the app to assume it's
* running on a display with 60% the vertical and horizontal resolution of the real display.
*/
@@ -130,6 +168,16 @@
/**
* With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_55 for a package will force the app to assume it's
+ * running on a display with 55% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long DOWNSCALE_55 = 189970036L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
* CompatModePackages#DOWNSCALE_50 for a package will force the app to assume it's
* running on a display with 50% vertical and horizontal resolution of the real display.
*/
@@ -139,6 +187,46 @@
public static final long DOWNSCALE_50 = 176926741L;
/**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_45 for a package will force the app to assume it's
+ * running on a display with 45% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long DOWNSCALE_45 = 189969782L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_40 for a package will force the app to assume it's
+ * running on a display with 40% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long DOWNSCALE_40 = 189970038L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_35 for a package will force the app to assume it's
+ * running on a display with 35% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long DOWNSCALE_35 = 189969749L;
+
+ /**
+ * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id
+ * CompatModePackages#DOWNSCALE_30 for a package will force the app to assume it's
+ * running on a display with 30% the vertical and horizontal resolution of the real display.
+ */
+ @ChangeId
+ @Disabled
+ @Overridable
+ public static final long DOWNSCALE_30 = 189970040L;
+
+ /**
* On Android TV applications that target pre-S are not expecting to receive a Window larger
* than 1080p, so if needed we are downscaling their Windows to 1080p.
* However, applications that target S and greater release version are expected to be able to
@@ -291,18 +379,42 @@
if (CompatChanges.isChangeEnabled(DOWNSCALE_90, packageName, userHandle)) {
return 1f / 0.9f;
}
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_85, packageName, userHandle)) {
+ return 1f / 0.85f;
+ }
if (CompatChanges.isChangeEnabled(DOWNSCALE_80, packageName, userHandle)) {
return 1f / 0.8f;
}
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_75, packageName, userHandle)) {
+ return 1f / 0.75f;
+ }
if (CompatChanges.isChangeEnabled(DOWNSCALE_70, packageName, userHandle)) {
return 1f / 0.7f;
}
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_65, packageName, userHandle)) {
+ return 1f / 0.65f;
+ }
if (CompatChanges.isChangeEnabled(DOWNSCALE_60, packageName, userHandle)) {
return 1f / 0.6f;
}
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_55, packageName, userHandle)) {
+ return 1f / 0.55f;
+ }
if (CompatChanges.isChangeEnabled(DOWNSCALE_50, packageName, userHandle)) {
return 1f / 0.5f;
}
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_45, packageName, userHandle)) {
+ return 1f / 0.45f;
+ }
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_40, packageName, userHandle)) {
+ return 1f / 0.4f;
+ }
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_35, packageName, userHandle)) {
+ return 1f / 0.35f;
+ }
+ if (CompatChanges.isChangeEnabled(DOWNSCALE_30, packageName, userHandle)) {
+ return 1f / 0.3f;
+ }
}
if (mService.mHasLeanbackFeature) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 81992d8..3dbc517 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -63,6 +63,8 @@
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
@@ -208,6 +210,7 @@
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
+import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManager.DisplayImePolicy;
@@ -358,13 +361,6 @@
boolean mIsSizeForced = false;
/**
- * Overridden display size and metrics to activity window bounds. Set via
- * "adb shell wm set-sandbox-display-apis". Default to true, since only disable for debugging.
- * @see WindowManagerService#setSandboxDisplayApis(int, boolean)
- */
- private boolean mSandboxDisplayApis = true;
-
- /**
* Overridden display density for current user. Initialized with {@link #mInitialDisplayDensity}
* but can be set from Settings or via shell command "adb shell wm density".
* @see WindowManagerService#setForcedDisplayDensityForUser(int, int, int)
@@ -1556,9 +1552,7 @@
// to cover the activity configuration change.
return false;
}
- if ((r.mStartingData != null && r.mStartingData.hasImeSurface())
- || (mInsetsStateController.getImeSourceProvider()
- .getSource().getVisibleFrame() != null)) {
+ if (r.attachedToProcess() && mayImeShowOnLaunchingActivity(r)) {
// 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;
@@ -1614,6 +1608,24 @@
return true;
}
+ /** Returns {@code true} if the IME is possible to show on the launching activity. */
+ private boolean mayImeShowOnLaunchingActivity(@NonNull ActivityRecord r) {
+ final WindowState win = r.findMainWindow();
+ if (win == null) {
+ return false;
+ }
+ // See InputMethodManagerService#shouldRestoreImeVisibility that we expecting the IME
+ // should be hidden when the window set the hidden softInputMode.
+ final int softInputMode = win.mAttrs.softInputMode;
+ switch (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) {
+ case SOFT_INPUT_STATE_ALWAYS_HIDDEN:
+ case SOFT_INPUT_STATE_HIDDEN:
+ return false;
+ }
+ return r.mLastImeShown && mInputMethodWindow != null && mInputMethodWindow.mHasSurface
+ && mInputMethodWindow.mViewVisibility == View.VISIBLE;
+ }
+
/** Returns {@code true} if the top activity is transformed with the new rotation of display. */
boolean hasTopFixedRotationLaunchingApp() {
return mFixedRotationLaunchingApp != null
@@ -3821,7 +3833,7 @@
// 4. Update the IME control target to apply any inset change and animation.
// 5. Reparent the IME container surface to either the input target app, or the IME window
// parent.
- updateImeControlTarget();
+ updateImeControlTarget(true /* forceUpdateImeParent */);
}
@VisibleForTesting
@@ -3953,12 +3965,17 @@
}
void updateImeControlTarget() {
+ updateImeControlTarget(false /* forceUpdateImeParent */);
+ }
+
+ void updateImeControlTarget(boolean forceUpdateImeParent) {
InsetsControlTarget prevImeControlTarget = mImeControlTarget;
mImeControlTarget = computeImeControlTarget();
mInsetsStateController.onImeControlTargetChanged(mImeControlTarget);
- // Update Ime parent when IME insets leash created, which is the best time that default
- // IME visibility has been settled down after IME control target changed.
- if (prevImeControlTarget != mImeControlTarget) {
+ // Update Ime parent when IME insets leash created or the new IME layering target might
+ // updated from setImeLayeringTarget, which is the best time that default IME visibility
+ // has been settled down after IME control target changed.
+ if (prevImeControlTarget != mImeControlTarget || forceUpdateImeParent) {
updateImeParent();
}
@@ -5475,6 +5492,14 @@
}
boolean updateDisplayOverrideConfigurationLocked() {
+ // Preemptively cancel the running recents animation -- SysUI can't currently handle this
+ // case properly since the signals it receives all happen post-change
+ final RecentsAnimationController recentsAnimationController =
+ mWmService.getRecentsAnimationController();
+ if (recentsAnimationController != null) {
+ recentsAnimationController.cancelAnimationForDisplayChange();
+ }
+
Configuration values = new Configuration();
computeScreenConfiguration(values);
@@ -5810,21 +5835,6 @@
return true;
}
- /**
- * Sets if Display APIs should be sandboxed to the activity window bounds.
- */
- void setSandboxDisplayApis(boolean sandboxDisplayApis) {
- mSandboxDisplayApis = sandboxDisplayApis;
- }
-
- /**
- * Returns {@code true} is Display APIs should be sandboxed to the activity window bounds,
- * {@code false} otherwise. Default to true, unless set for debugging purposes.
- */
- boolean sandboxDisplayApis() {
- return mSandboxDisplayApis;
- }
-
/** The entry for proceeding to handle {@link #mFixedRotationLaunchingApp}. */
class FixedRotationTransitionListener extends WindowManagerInternal.AppTransitionListener {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 977df93..97c19ab 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1771,7 +1771,7 @@
}
// Voice interaction overrides both top fullscreen and top docked.
- if (affectsSystemUi && attrs.type == TYPE_VOICE_INTERACTION) {
+ if (affectsSystemUi && attrs.type == TYPE_VOICE_INTERACTION && attrs.isFullscreen()) {
if (mTopFullscreenOpaqueWindowState == null) {
mTopFullscreenOpaqueWindowState = win;
if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index c8f2777..73d6cec 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -480,6 +480,16 @@
return false;
}
+ // Preemptively cancel the running recents animation -- SysUI can't currently handle this
+ // case properly since the signals it receives all happen post-change. We do this earlier
+ // in the rotation flow, since DisplayContent.updateDisplayOverrideConfigurationLocked seems
+ // to happen too late.
+ final RecentsAnimationController recentsAnimationController =
+ mService.getRecentsAnimationController();
+ if (recentsAnimationController != null) {
+ recentsAnimationController.cancelAnimationForDisplayChange();
+ }
+
final Transition t = (useShellTransitions
&& !mService.mAtmService.getTransitionController().isCollecting())
? mService.mAtmService.getTransitionController().createTransition(TRANSIT_CHANGE)
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 6f2f698..8c781a1 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -394,10 +394,23 @@
/**
* Called when the current input focus changes.
*/
- private void updateInputFocusRequest() {
+ private void updateInputFocusRequest(InputConsumerImpl recentsAnimationInputConsumer) {
final WindowState focus = mDisplayContent.mCurrentFocus;
- final IBinder focusToken = focus != null ? focus.mInputChannelToken : null;
+ // Request focus for the recents animation input consumer if an input consumer should
+ // be applied for the window.
+ if (recentsAnimationInputConsumer != null && focus != null) {
+ final RecentsAnimationController recentsAnimationController =
+ mService.getRecentsAnimationController();
+ final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null
+ && recentsAnimationController.shouldApplyInputConsumer(focus.mActivityRecord);
+ if (shouldApplyRecentsInputConsumer) {
+ requestFocus(recentsAnimationInputConsumer.mWindowHandle.token,
+ recentsAnimationInputConsumer.mName);
+ return;
+ }
+ }
+ final IBinder focusToken = focus != null ? focus.mInputChannelToken : null;
if (focusToken == null) {
mInputFocus = null;
return;
@@ -474,8 +487,6 @@
boolean mInDrag;
- private boolean mRecentsAnimationFocusOverride;
-
private void updateInputWindows(boolean inDrag) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
@@ -491,16 +502,8 @@
mInDrag = inDrag;
resetInputConsumers(mInputTransaction);
- mRecentsAnimationFocusOverride = false;
mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */);
-
- if (mRecentsAnimationFocusOverride) {
- requestFocus(mRecentsAnimationInputConsumer.mWindowHandle.token,
- mRecentsAnimationInputConsumer.mName);
- } else {
- updateInputFocusRequest();
- }
-
+ updateInputFocusRequest(mRecentsAnimationInputConsumer);
if (!mUpdateInputWindowsImmediately) {
mDisplayContent.getPendingTransaction().merge(mInputTransaction);
@@ -538,7 +541,6 @@
mRecentsAnimationInputConsumer.mWindowHandle)) {
mRecentsAnimationInputConsumer.show(mInputTransaction, w.mActivityRecord);
mAddRecentsAnimationInputConsumerHandle = false;
- mRecentsAnimationFocusOverride = true;
}
}
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 0112f79..f8238c1 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -48,6 +48,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
+import android.view.Display;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -163,16 +164,27 @@
aodShowing ? 1 : 0,
mKeyguardGoingAway ? 1 : 0,
"setKeyguardShown");
+
+ // Update the task snapshot if the screen will not be turned off. To make sure that the
+ // unlocking animation can animate consistent content. The conditions are:
+ // - Either AOD or keyguard changes to be showing. So if the states change individually,
+ // the later one can be skipped to avoid taking snapshot again. While it still accepts
+ // if both of them change to show at the same time.
+ // - Keyguard was not going away. Because if it was, the closing transition is able to
+ // handle the snapshot.
+ // - The display state is ON. Because if AOD is not on or pulsing, the display state will
+ // be OFF or DOZE (the path of screen off may have handled it).
+ if (((aodShowing ^ keyguardShowing) || (aodShowing && aodChanged && keyguardChanged))
+ && !mKeyguardGoingAway && Display.isOnState(
+ mRootWindowContainer.getDefaultDisplay().getDisplayInfo().state)) {
+ mWindowManager.mTaskSnapshotController.snapshotForSleeping(DEFAULT_DISPLAY);
+ }
+
mKeyguardShowing = keyguardShowing;
mAodShowing = aodShowing;
if (aodChanged) {
// Ensure the new state takes effect.
mWindowManager.mWindowPlacerLocked.performSurfacePlacement();
- // If the device can enter AOD and keyguard at the same time, the screen will not be
- // turned off, so the snapshot needs to be refreshed when these states are changed.
- if (aodShowing && keyguardShowing && keyguardChanged) {
- mWindowManager.mTaskSnapshotController.snapshotForSleeping(DEFAULT_DISPLAY);
- }
}
if (keyguardChanged) {
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index eb7087c..7174e68 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -21,6 +21,7 @@
import android.graphics.Color;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -104,20 +105,12 @@
* com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and
* the framework implementation will be used to determine the aspect ratio.
*/
+ @VisibleForTesting
void setFixedOrientationLetterboxAspectRatio(float aspectRatio) {
mFixedOrientationLetterboxAspectRatio = aspectRatio;
}
/**
- * Resets the aspect ratio of letterbox for fixed orientation to {@link
- * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}.
- */
- void resetFixedOrientationLetterboxAspectRatio() {
- mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio);
- }
-
- /**
* Gets the aspect ratio of letterbox for fixed orientation.
*/
float getFixedOrientationLetterboxAspectRatio() {
@@ -125,25 +118,6 @@
}
/**
- * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0,
- * both it and a value of {@link
- * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and
- * and corners of the activity won't be rounded.
- */
- void setLetterboxActivityCornersRadius(int cornersRadius) {
- mLetterboxActivityCornersRadius = cornersRadius;
- }
-
- /**
- * Resets corners raidus for activities presented in the letterbox mode to {@link
- * com.android.internal.R.integer.config_letterboxActivityCornersRadius}.
- */
- void resetLetterboxActivityCornersRadius() {
- mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_letterboxActivityCornersRadius);
- }
-
- /**
* Whether corners of letterboxed activities are rounded.
*/
boolean isLetterboxActivityCornersRounded() {
@@ -166,25 +140,6 @@
return mLetterboxBackgroundColor;
}
-
- /**
- * Sets color of letterbox background which is used when {@link
- * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
- * fallback for other backfround types.
- */
- void setLetterboxBackgroundColor(Color color) {
- mLetterboxBackgroundColor = color;
- }
-
- /**
- * Resets color of letterbox background to {@link
- * com.android.internal.R.color.config_letterboxBackgroundColor}.
- */
- void resetLetterboxBackgroundColor() {
- mLetterboxBackgroundColor = Color.valueOf(mContext.getResources().getColor(
- com.android.internal.R.color.config_letterboxBackgroundColor));
- }
-
/**
* Gets {@link LetterboxBackgroundType} specified in {@link
* com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command.
@@ -194,19 +149,6 @@
return mLetterboxBackgroundType;
}
- /** Sets letterbox background type. */
- void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) {
- mLetterboxBackgroundType = backgroundType;
- }
-
- /**
- * Resets cletterbox background type to {@link
- * com.android.internal.R.integer.config_letterboxBackgroundType}.
- */
- void resetLetterboxBackgroundType() {
- mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext);
- }
-
/** Returns a string representing the given {@link LetterboxBackgroundType}. */
static String letterboxBackgroundTypeToString(
@LetterboxBackgroundType int backgroundType) {
@@ -236,27 +178,6 @@
}
/**
- * Overrides alpha of a black scrim shown over wallpaper for {@link
- * #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link mLetterboxBackgroundType}.
- *
- * <p>If given value is < 0 or >= 1, both it and a value of {@link
- * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha} are ignored
- * and 0.0 (transparent) is instead.
- */
- void setLetterboxBackgroundWallpaperDarkScrimAlpha(float alpha) {
- mLetterboxBackgroundWallpaperDarkScrimAlpha = alpha;
- }
-
- /**
- * Resets alpha of a black scrim shown over wallpaper letterbox background to {@link
- * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha}.
- */
- void resetLetterboxBackgroundWallpaperDarkScrimAlpha() {
- mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
- }
-
- /**
* Gets alpha of a black scrim shown over wallpaper letterbox background.
*/
float getLetterboxBackgroundWallpaperDarkScrimAlpha() {
@@ -264,28 +185,6 @@
}
/**
- * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in
- * {@link mLetterboxBackgroundType}.
- *
- * <p> If given value <= 0, both it and a value of {@link
- * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius} are ignored
- * and 0 is used instead.
- */
- void setLetterboxBackgroundWallpaperBlurRadius(int radius) {
- mLetterboxBackgroundWallpaperBlurRadius = radius;
- }
-
- /**
- * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
- * mLetterboxBackgroundType} to {@link
- * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius}.
- */
- void resetLetterboxBackgroundWallpaperBlurRadius() {
- mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius);
- }
-
- /**
* Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
* mLetterboxBackgroundType}.
*/
@@ -312,17 +211,9 @@
* com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier} are ignored and
* central position (0.5) is used.
*/
+ @VisibleForTesting
void setLetterboxHorizontalPositionMultiplier(float multiplier) {
mLetterboxHorizontalPositionMultiplier = multiplier;
}
- /**
- * Resets horizontal position of a center of the letterboxed app window to {@link
- * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}.
- */
- void resetLetterboxHorizontalPositionMultiplier() {
- mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat(
- com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier);
- }
-
}
diff --git a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
index b1e12b6..d230936 100644
--- a/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/NonAppWindowAnimationAdapter.java
@@ -99,8 +99,10 @@
final WindowManagerPolicy policy = service.mPolicy;
service.mRoot.forAllWindows(nonAppWindow -> {
+ // Animation on the IME window is controlled via Insets.
if (nonAppWindow.mActivityRecord == null && policy.canBeHiddenByKeyguardLw(nonAppWindow)
- && nonAppWindow.wouldBeVisibleIfPolicyIgnored() && !nonAppWindow.isVisible()) {
+ && nonAppWindow.wouldBeVisibleIfPolicyIgnored() && !nonAppWindow.isVisible()
+ && nonAppWindow != service.mRoot.getCurrentInputMethodWindow()) {
final NonAppWindowAnimationAdapter nonAppAdapter = new NonAppWindowAnimationAdapter(
nonAppWindow, durationHint, statusBarTransitionDelay);
adaptersOut.add(nonAppAdapter);
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 5362771..710dd552 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -36,8 +36,10 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.WindowConfiguration;
+import android.graphics.GraphicBuffer;
import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
import android.os.Binder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
@@ -54,6 +56,7 @@
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
+import android.view.SurfaceSession;
import android.view.WindowInsets.Type;
import android.window.PictureInPictureSurfaceTransaction;
import android.window.TaskSnapshot;
@@ -150,6 +153,8 @@
private boolean mCancelOnNextTransitionStart;
// Whether to take a screenshot when handling a deferred cancel
private boolean mCancelDeferredWithScreenshot;
+ // The reorder mode to apply after the cleanupScreenshot() callback
+ private int mPendingCancelWithScreenshotReorderMode = REORDER_MOVE_TO_ORIGINAL_POSITION;
@VisibleForTesting
boolean mIsAddingTaskToTargets;
@@ -159,12 +164,6 @@
private ActivityRecord mNavBarAttachedApp;
/**
- * Animates the screenshot of task that used to be controlled by RecentsAnimation.
- * @see {@link #setCancelOnNextTransitionStart}
- */
- SurfaceAnimator mRecentScreenshotAnimator;
-
- /**
* An app transition listener to cancel the recents animation only after the app transition
* starts or is canceled.
*/
@@ -221,7 +220,7 @@
final ArraySet<Task> tasks = Sets.newArraySet(task);
snapshotController.snapshotTasks(tasks);
snapshotController.addSkipClosingAppSnapshotTasks(tasks);
- return snapshotController.getSnapshot(taskId, 0 /* userId */,
+ return snapshotController.getSnapshot(taskId, task.mUserId,
false /* restoreFromDisk */, false /* isLowResolution */);
}
}
@@ -353,18 +352,20 @@
@Override
public void cleanupScreenshot() {
- synchronized (mService.mGlobalLock) {
- if (mRecentScreenshotAnimator != null) {
- mRecentScreenshotAnimator.cancelAnimation();
- mRecentScreenshotAnimator = null;
- }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // Note, the callback will handle its own synchronization, do not lock on WM lock
+ // prior to calling the callback
+ continueDeferredCancelAnimation();
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
@Override
public void setWillFinishToHome(boolean willFinishToHome) {
synchronized (mService.getWindowManagerLock()) {
- mWillFinishToHome = willFinishToHome;
+ RecentsAnimationController.this.setWillFinishToHome(willFinishToHome);
}
}
@@ -513,15 +514,14 @@
|| config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
}
-
@VisibleForTesting
- AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) {
+ TaskAnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) {
return addAnimation(task, isRecentTaskInvisible, false /* hidden */,
null /* finishedCallback */);
}
@VisibleForTesting
- AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible, boolean hidden,
+ TaskAnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible, boolean hidden,
OnAnimationFinishedCallback finishedCallback) {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addAnimation(%s)", task.getName());
final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task,
@@ -537,9 +537,7 @@
void removeAnimation(TaskAnimationAdapter taskAdapter) {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
"removeAnimation(%d)", taskAdapter.mTask.mTaskId);
- taskAdapter.mTask.setCanAffectSystemUiFlags(true);
- taskAdapter.mCapturedFinishCallback.onAnimationFinished(taskAdapter.mLastAnimationType,
- taskAdapter);
+ taskAdapter.onRemove();
mPendingAnimations.remove(taskAdapter);
}
@@ -816,6 +814,32 @@
cancelAnimation(REORDER_KEEP_IN_PLACE, screenshot, "rootTaskOrderChanged");
}
+ /**
+ * Cancels the running animation when starting home, providing a snapshot for the runner to
+ * properly handle the cancellation. This call uses the provided hint to determine how to
+ * finish the animation.
+ */
+ public void cancelAnimationForHomeStart() {
+ if (mCanceled) {
+ return;
+ }
+ cancelAnimation(mWillFinishToHome ? REORDER_MOVE_TO_TOP : REORDER_KEEP_IN_PLACE,
+ true /* screenshot */, "cancelAnimationForHomeStart");
+ }
+
+ /**
+ * Cancels the running animation when there is a display change, providing a snapshot for the
+ * runner to properly handle the cancellation. This call uses the provided hint to determine
+ * how to finish the animation.
+ */
+ public void cancelAnimationForDisplayChange() {
+ if (mCanceled) {
+ return;
+ }
+ cancelAnimation(mWillFinishToHome ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION,
+ true /* screenshot */, "cancelAnimationForDisplayChange");
+ }
+
private void cancelAnimation(@ReorderMode int reorderMode, boolean screenshot, String reason) {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "cancelAnimation(): reason=%s", reason);
synchronized (mService.getWindowManagerLock()) {
@@ -826,17 +850,23 @@
mService.mH.removeCallbacks(mFailsafeRunnable);
mCanceled = true;
- if (screenshot) {
+ if (screenshot && !mPendingAnimations.isEmpty()) {
+ final TaskAnimationAdapter adapter = mPendingAnimations.get(0);
+ final Task task = adapter.mTask;
// Screen shot previous task when next task starts transition and notify the runner.
// We will actually finish the animation once the runner calls cleanUpScreenshot().
- final Task task = mPendingAnimations.get(0).mTask;
- final TaskSnapshot taskSnapshot = screenshotRecentTask(task, reorderMode);
+ final TaskSnapshot taskSnapshot = screenshotRecentTask(task);
+ mPendingCancelWithScreenshotReorderMode = reorderMode;
try {
mRunner.onAnimationCanceled(taskSnapshot);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to cancel recents animation", e);
}
- if (taskSnapshot == null) {
+ if (taskSnapshot != null) {
+ // Defer until the runner calls back to cleanupScreenshot()
+ adapter.setSnapshotOverlay(taskSnapshot);
+ } else {
+ // Do a normal cancel since we couldn't screenshot
mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
}
} else {
@@ -853,6 +883,17 @@
}
}
+ @VisibleForTesting
+ void continueDeferredCancelAnimation() {
+ mCallbacks.onAnimationFinished(mPendingCancelWithScreenshotReorderMode,
+ false /* sendUserLeaveHint */);
+ }
+
+ @VisibleForTesting
+ void setWillFinishToHome(boolean willFinishToHome) {
+ mWillFinishToHome = willFinishToHome;
+ }
+
/**
* Cancel recents animation when the next app transition starts.
* <p>
@@ -894,28 +935,13 @@
return mRequestDeferCancelUntilNextTransition && mCancelDeferredWithScreenshot;
}
- TaskSnapshot screenshotRecentTask(Task task, @ReorderMode int reorderMode) {
+ TaskSnapshot screenshotRecentTask(Task task) {
final TaskSnapshotController snapshotController = mService.mTaskSnapshotController;
final ArraySet<Task> tasks = Sets.newArraySet(task);
snapshotController.snapshotTasks(tasks);
snapshotController.addSkipClosingAppSnapshotTasks(tasks);
- final TaskSnapshot taskSnapshot = snapshotController.getSnapshot(task.mTaskId,
- task.mUserId, false /* restoreFromDisk */, false /* isLowResolution */);
- if (taskSnapshot == null) {
- return null;
- }
-
- final TaskScreenshotAnimatable animatable = new TaskScreenshotAnimatable(mService.mSurfaceControlFactory, task,
- new SurfaceControl.ScreenshotHardwareBuffer(taskSnapshot.getHardwareBuffer(),
- taskSnapshot.getColorSpace(), false /* containsSecureLayers */));
- mRecentScreenshotAnimator = new SurfaceAnimator(
- animatable,
- (type, anim) -> {
- ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "mRecentScreenshotAnimator finish");
- mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
- }, mService);
- mRecentScreenshotAnimator.transferAnimation(task.mSurfaceAnimator);
- return taskSnapshot;
+ return snapshotController.getSnapshot(task.mTaskId, task.mUserId,
+ false /* restoreFromDisk */, false /* isLowResolution */);
}
void cleanupAnimation(@ReorderMode int reorderMode) {
@@ -954,12 +980,6 @@
mRunner = null;
mCanceled = true;
- // Make sure previous animator has cleaned-up.
- if (mRecentScreenshotAnimator != null) {
- mRecentScreenshotAnimator.cancelAnimation();
- mRecentScreenshotAnimator = null;
- }
-
// Restore IME icon only when moving the original app task to front from recents, in case
// IME icon may missing if the moving task has already been the current focused task.
if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION && !mIsAddingTaskToTargets) {
@@ -1140,6 +1160,8 @@
private PictureInPictureSurfaceTransaction mFinishTransaction;
// An overlay used to mask the content as an app goes into PIP
private SurfaceControl mFinishOverlay;
+ // An overlay used for canceling the animation with a screenshot
+ private SurfaceControl mSnapshotOverlay;
TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) {
mTask = task;
@@ -1174,10 +1196,47 @@
return mTarget;
}
- void onCleanup() {
- if (mFinishTransaction != null) {
- final Transaction pendingTransaction = mTask.getPendingTransaction();
+ void setSnapshotOverlay(TaskSnapshot snapshot) {
+ // Create a surface control for the snapshot and reparent it to the leash
+ final HardwareBuffer buffer = snapshot.getHardwareBuffer();
+ if (buffer == null) {
+ return;
+ }
+ final SurfaceSession session = new SurfaceSession();
+ mSnapshotOverlay = mService.mSurfaceControlFactory.apply(session)
+ .setName("RecentTaskScreenshotSurface")
+ .setCallsite("TaskAnimationAdapter.setSnapshotOverlay")
+ .setFormat(buffer.getFormat())
+ .setParent(mCapturedLeash)
+ .setBLASTLayer()
+ .build();
+
+ final float scale = 1.0f * mTask.getBounds().width() / buffer.getWidth();
+ mTask.getPendingTransaction()
+ .setBuffer(mSnapshotOverlay, GraphicBuffer.createFromHardwareBuffer(buffer))
+ .setColorSpace(mSnapshotOverlay, snapshot.getColorSpace())
+ .setLayer(mSnapshotOverlay, Integer.MAX_VALUE)
+ .setMatrix(mSnapshotOverlay, scale, 0, 0, scale)
+ .show(mSnapshotOverlay)
+ .apply();
+ }
+
+ void onRemove() {
+ if (mSnapshotOverlay != null) {
+ // Clean up the snapshot overlay if necessary
+ mTask.getPendingTransaction()
+ .remove(mSnapshotOverlay)
+ .apply();
+ mSnapshotOverlay = null;
+ }
+ mTask.setCanAffectSystemUiFlags(true);
+ mCapturedFinishCallback.onAnimationFinished(mLastAnimationType, this);
+ }
+
+ void onCleanup() {
+ final Transaction pendingTransaction = mTask.getPendingTransaction();
+ if (mFinishTransaction != null) {
// Reparent the overlay
if (mFinishOverlay != null) {
pendingTransaction.reparent(mFinishOverlay, mTask.mSurfaceControl);
@@ -1205,10 +1264,15 @@
} else if (!mTask.isAttached()) {
// Apply the task's pending transaction in case it is detached and its transaction
// is not reachable.
- mTask.getPendingTransaction().apply();
+ pendingTransaction.apply();
}
}
+ @VisibleForTesting
+ public SurfaceControl getSnapshotOverlay() {
+ return mSnapshotOverlay;
+ }
+
@Override
public boolean getShowWallpaper() {
return false;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ea80b8b..6242b97 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
@@ -220,6 +221,9 @@
// transaction from the global transaction.
private final SurfaceControl.Transaction mDisplayTransaction;
+ // The tag for the token to put root tasks on the displays to sleep.
+ private static final String DISPLAY_OFF_SLEEP_TOKEN_TAG = "Display-off";
+
/** The token acquirer to put root tasks on the displays to sleep */
final ActivityTaskManagerInternal.SleepTokenAcquirer mDisplayOffTokenAcquirer;
@@ -449,7 +453,7 @@
mService = service.mAtmService;
mTaskSupervisor = mService.mTaskSupervisor;
mTaskSupervisor.mRootWindowContainer = this;
- mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirerImpl("Display-off");
+ mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirerImpl(DISPLAY_OFF_SLEEP_TOKEN_TAG);
}
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
@@ -1525,7 +1529,9 @@
// Updates the extra information of the intent.
if (fromHomeKey) {
homeIntent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, true);
- mWindowManager.cancelRecentsAnimation(REORDER_KEEP_IN_PLACE, "startHomeActivity");
+ if (mWindowManager.getRecentsAnimationController() != null) {
+ mWindowManager.getRecentsAnimationController().cancelAnimationForHomeStart();
+ }
}
homeIntent.putExtra(WindowManagerPolicy.EXTRA_START_REASON, reason);
@@ -2653,12 +2659,14 @@
Slog.d(TAG, "Remove non-exist sleep token: " + token + " from " + Debug.getCallers(6));
}
mSleepTokens.remove(token.mHashKey);
-
final DisplayContent display = getDisplayContent(token.mDisplayId);
if (display != null) {
display.mAllSleepTokens.remove(token);
if (display.mAllSleepTokens.isEmpty()) {
mService.updateSleepIfNeededLocked();
+ if (token.mTag.equals(DISPLAY_OFF_SLEEP_TOKEN_TAG)) {
+ display.mSkipAppTransitionAnimation = true;
+ }
}
}
}
@@ -3383,6 +3391,15 @@
*/
void lockAllProfileTasks(@UserIdInt int userId) {
forAllLeafTasks(task -> {
+ final ActivityRecord top = task.topRunningActivity();
+ if (top != null && !top.finishing
+ && ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER.equals(top.intent.getAction())
+ && top.packageName.equals(
+ mService.getSysUiServiceComponentLocked().getPackageName())) {
+ // Do nothing since the task is already secure by sysui.
+ return;
+ }
+
if (task.getActivity(activity -> !activity.finishing && activity.mUserId == userId)
!= null) {
mService.getTaskChangeNotificationController().notifyTaskProfileLocked(
diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java
index b56e76d..be6a5d2 100644
--- a/services/core/java/com/android/server/wm/ShellRoot.java
+++ b/services/core/java/com/android/server/wm/ShellRoot.java
@@ -50,6 +50,7 @@
private SurfaceControl mSurfaceControl = null;
private IWindow mAccessibilityWindow;
private IBinder.DeathRecipient mAccessibilityWindowDeath;
+ private int mWindowType;
ShellRoot(@NonNull IWindow client, @NonNull DisplayContent dc,
@WindowManager.ShellRootLayer final int shellRootLayer) {
@@ -64,19 +65,18 @@
return;
}
mClient = client;
- int windowType;
switch (shellRootLayer) {
case SHELL_ROOT_LAYER_DIVIDER:
- windowType = TYPE_DOCK_DIVIDER;
+ mWindowType = TYPE_DOCK_DIVIDER;
break;
case SHELL_ROOT_LAYER_PIP:
- windowType = TYPE_APPLICATION_OVERLAY;
+ mWindowType = TYPE_APPLICATION_OVERLAY;
break;
default:
throw new IllegalArgumentException(shellRootLayer
+ " is not an acceptable shell root layer.");
}
- mToken = new WindowToken.Builder(dc.mWmService, client.asBinder(), windowType)
+ mToken = new WindowToken.Builder(dc.mWmService, client.asBinder(), mWindowType)
.setDisplayContent(dc)
.setPersistOnEmpty(true)
.setOwnerCanManageAppTokens(true)
@@ -89,6 +89,10 @@
mToken.getPendingTransaction().show(mSurfaceControl);
}
+ int getWindowType() {
+ return mWindowType;
+ }
+
void clear() {
if (mClient != null) {
mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index 59de43a..3f9c93b 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -26,6 +26,12 @@
protected final WindowManagerService mService;
protected final int mTypeParams;
+ /**
+ * Tell whether the launching activity should use
+ * {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION}.
+ */
+ boolean mIsTransitionForward;
+
protected StartingData(WindowManagerService service, int typeParams) {
mService = service;
mTypeParams = typeParams;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7af8d8b..325f10f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -974,10 +974,7 @@
}
void removeIfPossible(String reason) {
- final boolean isRootTask = isRootTask();
- if (!isRootTask) {
- mAtmService.getLockTaskController().clearLockedTask(this);
- }
+ mAtmService.getLockTaskController().clearLockedTask(this);
if (shouldDeferRemoval()) {
if (DEBUG_ROOT_TASK) Slog.i(TAG,
"removeTask:" + reason + " deferring removing taskId=" + mTaskId);
@@ -6689,24 +6686,26 @@
}
}
- // TODO(185200798): Persist theme name instead of theme if
- int splashScreenThemeResId = options != null
- ? options.getSplashScreenThemeResId() : 0;
-
- // User can override the splashscreen theme. The theme name is used to persist
- // the setting, so if no theme is set in the ActivityOptions, we check if has
- // been persisted here.
- if (splashScreenThemeResId == 0) {
+ // Find the splash screen theme. User can override the persisted theme by
+ // ActivityOptions.
+ String splashScreenThemeResName = options != null
+ ? options.getSplashScreenThemeResName() : null;
+ if (splashScreenThemeResName == null || splashScreenThemeResName.isEmpty()) {
try {
- String themeName = mAtmService.getPackageManager()
+ splashScreenThemeResName = mAtmService.getPackageManager()
.getSplashScreenTheme(r.packageName, r.mUserId);
- if (themeName != null) {
- Context packageContext = mAtmService.mContext
- .createPackageContext(r.packageName, 0);
- splashScreenThemeResId = packageContext.getResources()
- .getIdentifier(themeName, null, null);
- }
- } catch (RemoteException | PackageManager.NameNotFoundException
+ } catch (RemoteException ignore) {
+ // Just use the default theme
+ }
+ }
+ int splashScreenThemeResId = 0;
+ if (splashScreenThemeResName != null && !splashScreenThemeResName.isEmpty()) {
+ try {
+ final Context packageContext = mAtmService.mContext
+ .createPackageContext(r.packageName, 0);
+ splashScreenThemeResId = packageContext.getResources()
+ .getIdentifier(splashScreenThemeResName, null, null);
+ } catch (PackageManager.NameNotFoundException
| Resources.NotFoundException ignore) {
// Just use the default theme
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 90d40f3..d450dbf 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -31,6 +31,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
@@ -44,6 +45,7 @@
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.WindowConfiguration;
+import android.content.res.Configuration;
import android.os.UserHandle;
import android.util.IntArray;
import android.util.Slog;
@@ -1705,10 +1707,18 @@
return true;
}
// Check if the request min width/height is supported in multi window.
- final int maxSupportMinDimensions = (int) (mAtmService.mMinPercentageMultiWindowSupportWidth
- * getConfiguration().smallestScreenWidthDp
- * mDisplayContent.getDisplayMetrics().density);
- return minWidth <= maxSupportMinDimensions && minHeight <= maxSupportMinDimensions;
+ final Configuration config = getConfiguration();
+ final int orientation = config.orientation;
+ if (orientation == ORIENTATION_LANDSCAPE) {
+ final int maxSupportMinWidth = (int) (mAtmService.mMinPercentageMultiWindowSupportWidth
+ * config.screenWidthDp * mDisplayContent.getDisplayMetrics().density);
+ return minWidth <= maxSupportMinWidth;
+ } else {
+ final int maxSupportMinHeight =
+ (int) (mAtmService.mMinPercentageMultiWindowSupportHeight
+ * config.screenHeightDp * mDisplayContent.getDisplayMetrics().density);
+ return minHeight <= maxSupportMinHeight;
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
deleted file mode 100644
index 7e992ac..0000000
--- a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.wm;
-
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
-
-import android.graphics.GraphicBuffer;
-import android.hardware.HardwareBuffer;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-
-import com.android.internal.protolog.common.ProtoLog;
-
-import java.util.function.Function;
-
-/**
- * Class used by {@link RecentsAnimationController} to create a surface control with taking
- * screenshot of task when canceling recents animation.
- *
- * @see {@link RecentsAnimationController#setCancelOnNextTransitionStart}
- */
-class TaskScreenshotAnimatable implements SurfaceAnimator.Animatable {
- private static final String TAG = "TaskScreenshotAnim";
- private Task mTask;
- private SurfaceControl mSurfaceControl;
- private int mWidth;
- private int mHeight;
-
- TaskScreenshotAnimatable(Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory,
- Task task, SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer) {
- HardwareBuffer buffer = screenshotBuffer == null
- ? null : screenshotBuffer.getHardwareBuffer();
- mTask = task;
- mWidth = (buffer != null) ? buffer.getWidth() : 1;
- mHeight = (buffer != null) ? buffer.getHeight() : 1;
- ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
- "Creating TaskScreenshotAnimatable: task: %s width: %d height: %d",
- task, mWidth, mHeight);
- mSurfaceControl = surfaceControlFactory.apply(new SurfaceSession())
- .setName("RecentTaskScreenshotSurface")
- .setBLASTLayer()
- .setCallsite("TaskScreenshotAnimatable")
- .build();
- if (buffer != null) {
- GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(buffer);
- getPendingTransaction().setBuffer(mSurfaceControl, graphicBuffer);
- getPendingTransaction().setColorSpace(mSurfaceControl,
- screenshotBuffer.getColorSpace());
- final float scale = 1.0f * mTask.getBounds().width() / mWidth;
- getPendingTransaction().setMatrix(mSurfaceControl, scale, 0, 0, scale);
- }
- getPendingTransaction().show(mSurfaceControl);
- }
-
- @Override
- public SurfaceControl.Transaction getPendingTransaction() {
- return mTask.getPendingTransaction();
- }
-
- @Override
- public void commitPendingTransaction() {
- mTask.commitPendingTransaction();
- }
-
- @Override
- public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
- t.setLayer(leash, 1);
- }
-
- @Override
- public void onAnimationLeashLost(SurfaceControl.Transaction t) {
- if (mSurfaceControl != null) {
- t.remove(mSurfaceControl);
- mSurfaceControl = null;
- }
- }
-
- @Override
- public SurfaceControl.Builder makeAnimationLeash() {
- return mTask.makeAnimationLeash();
- }
-
- @Override
- public SurfaceControl getAnimationLeashParent() {
- return mTask.getAnimationLeashParent();
- }
-
- @Override
- public SurfaceControl getSurfaceControl() {
- return mSurfaceControl;
- }
-
- @Override
- public SurfaceControl getParentSurfaceControl() {
- return mTask.mSurfaceControl;
- }
-
- @Override
- public int getSurfaceWidth() {
- return mWidth;
- }
-
- @Override
- public int getSurfaceHeight() {
- return mHeight;
- }
-}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 9ffb2b1..0000f95 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -443,7 +443,8 @@
} else {
excludeLayers = new SurfaceControl[0];
}
- builder.setHasImeSurface(!excludeIme && imeWindow != null && imeWindow.isDrawn());
+ builder.setHasImeSurface(!excludeIme && imeWindow != null && imeWindow.isVisible());
+
final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
SurfaceControl.captureLayersExcluding(
task.getSurfaceControl(), mTmpRect, scaleFraction,
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6d51849..147260b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -34,6 +34,7 @@
import static android.content.pm.PackageManager.FEATURE_PC;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
+import static android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myPid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -2463,6 +2464,17 @@
configChanged = displayContent.updateOrientation();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ final DisplayInfo rotatedDisplayInfo =
+ win.mToken.getFixedRotationTransformDisplayInfo();
+ if (rotatedDisplayInfo != null) {
+ outSurfaceControl.setTransformHint(rotatedDisplayInfo.rotation);
+ } else {
+ // We have to update the transform hint of display here, but we need to get if from
+ // SurfaceFlinger, so set it as rotation of display for most cases, then
+ // SurfaceFlinger would still update the transform hint of display in next frame.
+ outSurfaceControl.setTransformHint(displayContent.getDisplayInfo().rotation);
+ }
+
if (toBeDisplayed && win.mIsWallpaper) {
displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */);
}
@@ -2534,12 +2546,13 @@
// We will leave the critical section before returning the leash to the client,
// so we need to copy the leash to prevent others release the one that we are
// about to return.
- // TODO: We will have an extra copy if the client is not local.
- // For now, we rely on GC to release it.
- // Maybe we can modify InsetsSourceControl.writeToParcel so it can release
- // the extra leash as soon as possible.
- outControls[i] = controls[i] != null
- ? new InsetsSourceControl(controls[i]) : null;
+ if (controls[i] != null) {
+ // This source control is an extra copy if the client is not local. By setting
+ // PARCELABLE_WRITE_RETURN_VALUE, the leash will be released at the end of
+ // SurfaceControl.writeToParcel.
+ outControls[i] = new InsetsSourceControl(controls[i]);
+ outControls[i].setParcelableFlags(PARCELABLE_WRITE_RETURN_VALUE);
+ }
}
}
}
@@ -5391,25 +5404,6 @@
}
}
- void setSandboxDisplayApis(int displayId, boolean sandboxDisplayApis) {
- if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS);
- }
-
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
- if (displayContent != null) {
- displayContent.setSandboxDisplayApis(sandboxDisplayApis);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
/** The global settings only apply to default display. */
private boolean applyForcedPropertiesForDefaultDisplay() {
boolean changed = false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index d5965494..a94fd07 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -19,12 +19,6 @@
import static android.os.Build.IS_USER;
import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
-import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
-
-import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.ParcelFileDescriptor;
@@ -42,7 +36,6 @@
import com.android.internal.protolog.ProtoLogImpl;
import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
import java.io.IOException;
import java.io.PrintWriter;
@@ -65,12 +58,10 @@
// Internal service impl -- must perform security checks before touching.
private final WindowManagerService mInternal;
- private final LetterboxConfiguration mLetterboxConfiguration;
public WindowManagerShellCommand(WindowManagerService service) {
mInterface = service;
mInternal = service;
- mLetterboxConfiguration = service.mLetterboxConfiguration;
}
@Override
@@ -122,14 +113,6 @@
return runGetIgnoreOrientationRequest(pw);
case "dump-visible-window-views":
return runDumpVisibleWindowViews(pw);
- case "set-letterbox-style":
- return runSetLetterboxStyle(pw);
- case "get-letterbox-style":
- return runGetLetterboxStyle(pw);
- case "reset-letterbox-style":
- return runResetLetterboxStyle(pw);
- case "set-sandbox-display-apis":
- return runSandboxDisplayApis(pw);
case "set-multi-window-config":
return runSetMultiWindowConfig();
case "get-multi-window-config":
@@ -348,37 +331,6 @@
return 0;
}
- /**
- * Override display size and metrics to reflect the DisplayArea of the calling activity.
- */
- private int runSandboxDisplayApis(PrintWriter pw) throws RemoteException {
- int displayId = Display.DEFAULT_DISPLAY;
- String arg = getNextArgRequired();
- if ("-d".equals(arg)) {
- displayId = Integer.parseInt(getNextArgRequired());
- arg = getNextArgRequired();
- }
-
- final boolean sandboxDisplayApis;
- switch (arg) {
- case "true":
- case "1":
- sandboxDisplayApis = true;
- break;
- case "false":
- case "0":
- sandboxDisplayApis = false;
- break;
- default:
- getErrPrintWriter().println("Error: expecting true, 1, false, 0, but we "
- + "get " + arg);
- return -1;
- }
-
- mInternal.setSandboxDisplayApis(displayId, sandboxDisplayApis);
- return 0;
- }
-
private int runDismissKeyguard(PrintWriter pw) throws RemoteException {
mInterface.dismissKeyguard(null /* callback */, null /* message */);
return 0;
@@ -596,231 +548,6 @@
return 0;
}
- private int runSetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException {
- final float aspectRatio;
- try {
- String arg = getNextArgRequired();
- aspectRatio = Float.parseFloat(arg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: bad aspect ratio format " + e);
- return -1;
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: 'reset' or aspect ratio should be provided as an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(aspectRatio);
- }
- return 0;
- }
-
- private int runSetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException {
- final int cornersRadius;
- try {
- String arg = getNextArgRequired();
- cornersRadius = Integer.parseInt(arg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: bad corners radius format " + e);
- return -1;
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: 'reset' or corners radius should be provided as an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxActivityCornersRadius(cornersRadius);
- }
- return 0;
- }
-
- private int runSetLetterboxBackgroundType(PrintWriter pw) throws RemoteException {
- @LetterboxBackgroundType final int backgroundType;
- try {
- String arg = getNextArgRequired();
- switch (arg) {
- case "solid_color":
- backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR;
- break;
- case "app_color_background":
- backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
- break;
- case "app_color_background_floating":
- backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
- break;
- case "wallpaper":
- backgroundType = LETTERBOX_BACKGROUND_WALLPAPER;
- break;
- default:
- getErrPrintWriter().println(
- "Error: 'reset', 'solid_color', 'app_color_background' or "
- + "'wallpaper' should be provided as an argument");
- return -1;
- }
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: 'reset', 'solid_color', 'app_color_background' or "
- + "'wallpaper' should be provided as an argument" + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxBackgroundType(backgroundType);
- }
- return 0;
- }
-
- private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException {
- final Color color;
- try {
- String arg = getNextArgRequired();
- color = Color.valueOf(Color.parseColor(arg));
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: 'reset' or color in #RRGGBB format should be provided as "
- + "an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxBackgroundColor(color);
- }
- return 0;
- }
-
- private int runSetLetterboxBackgroundWallpaperBlurRadius(PrintWriter pw)
- throws RemoteException {
- final int radius;
- try {
- String arg = getNextArgRequired();
- radius = Integer.parseInt(arg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: blur radius format " + e);
- return -1;
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: 'reset' or blur radius should be provided as an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxBackgroundWallpaperBlurRadius(radius);
- }
- return 0;
- }
-
- private int runSetLetterboxBackgroundWallpaperDarkScrimAlpha(PrintWriter pw)
- throws RemoteException {
- final float alpha;
- try {
- String arg = getNextArgRequired();
- alpha = Float.parseFloat(arg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: bad alpha format " + e);
- return -1;
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: 'reset' or alpha should be provided as an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxBackgroundWallpaperDarkScrimAlpha(alpha);
- }
- return 0;
- }
-
- private int runSeLetterboxHorizontalPositionMultiplier(PrintWriter pw) throws RemoteException {
- final float multiplier;
- try {
- String arg = getNextArgRequired();
- multiplier = Float.parseFloat(arg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: bad multiplier format " + e);
- return -1;
- } catch (IllegalArgumentException e) {
- getErrPrintWriter().println(
- "Error: 'reset' or multiplier should be provided as an argument " + e);
- return -1;
- }
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(multiplier);
- }
- return 0;
- }
-
- private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException {
- if (peekNextArg() == null) {
- getErrPrintWriter().println("Error: No arguments provided.");
- }
- while (peekNextArg() != null) {
- String arg = getNextArg();
- switch (arg) {
- case "--aspectRatio":
- runSetFixedOrientationLetterboxAspectRatio(pw);
- break;
- case "--cornerRadius":
- runSetLetterboxActivityCornersRadius(pw);
- break;
- case "--backgroundType":
- runSetLetterboxBackgroundType(pw);
- break;
- case "--backgroundColor":
- runSetLetterboxBackgroundColor(pw);
- break;
- case "--wallpaperBlurRadius":
- runSetLetterboxBackgroundWallpaperBlurRadius(pw);
- break;
- case "--wallpaperDarkScrimAlpha":
- runSetLetterboxBackgroundWallpaperDarkScrimAlpha(pw);
- break;
- case "--horizontalPositionMultiplier":
- runSeLetterboxHorizontalPositionMultiplier(pw);
- break;
- default:
- getErrPrintWriter().println(
- "Error: Unrecognized letterbox style option: " + arg);
- return -1;
- }
- }
- return 0;
- }
-
- private int runResetLetterboxStyle(PrintWriter pw) throws RemoteException {
- if (peekNextArg() == null) {
- resetLetterboxStyle();
- }
- synchronized (mInternal.mGlobalLock) {
- while (peekNextArg() != null) {
- String arg = getNextArg();
- switch (arg) {
- case "aspectRatio":
- mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
- break;
- case "cornerRadius":
- mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
- break;
- case "backgroundType":
- mLetterboxConfiguration.resetLetterboxBackgroundType();
- break;
- case "backgroundColor":
- mLetterboxConfiguration.resetLetterboxBackgroundColor();
- break;
- case "wallpaperBlurRadius":
- mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
- break;
- case "wallpaperDarkScrimAlpha":
- mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
- break;
- case "horizontalPositionMultiplier":
- mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
- break;
- default:
- getErrPrintWriter().println(
- "Error: Unrecognized letterbox style option: " + arg);
- return -1;
- }
- }
- }
- return 0;
- }
-
private int runSetMultiWindowConfig() {
if (peekNextArg() == null) {
getErrPrintWriter().println("Error: No arguments provided.");
@@ -895,40 +622,6 @@
return 0;
}
- private void resetLetterboxStyle() {
- synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio();
- mLetterboxConfiguration.resetLetterboxActivityCornersRadius();
- mLetterboxConfiguration.resetLetterboxBackgroundType();
- mLetterboxConfiguration.resetLetterboxBackgroundColor();
- mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius();
- mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
- mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
- }
- }
-
- private int runGetLetterboxStyle(PrintWriter pw) throws RemoteException {
- synchronized (mInternal.mGlobalLock) {
- pw.println("Corner radius: "
- + mLetterboxConfiguration.getLetterboxActivityCornersRadius());
- pw.println("Horizontal position multiplier: "
- + mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier());
- pw.println("Aspect ratio: "
- + mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio());
-
- pw.println("Background type: "
- + LetterboxConfiguration.letterboxBackgroundTypeToString(
- mLetterboxConfiguration.getLetterboxBackgroundType()));
- pw.println(" Background color: " + Integer.toHexString(
- mLetterboxConfiguration.getLetterboxBackgroundColor().toArgb()));
- pw.println(" Wallpaper blur radius: "
- + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius());
- pw.println(" Wallpaper dark scrim alpha: "
- + mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha());
- }
- return 0;
- }
-
private int runReset(PrintWriter pw) throws RemoteException {
int displayId = getDisplayId(getNextArg());
@@ -953,12 +646,6 @@
// set-ignore-orientation-request
mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */);
- // set-letterbox-style
- resetLetterboxStyle();
-
- // set-sandbox-display-apis
- mInternal.setSandboxDisplayApis(displayId, /* sandboxDisplayApis= */ true);
-
// set-multi-window-config
runResetMultiWindowConfig();
@@ -993,12 +680,7 @@
pw.println(" set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]");
pw.println(" get-ignore-orientation-request [-d DISPLAY_ID] ");
pw.println(" If app requested orientation should be ignored.");
- pw.println(" set-sandbox-display-apis [true|1|false|0]");
- pw.println(" Sets override of Display APIs getRealSize / getRealMetrics to reflect ");
- pw.println(" DisplayArea of the activity, or the window bounds if in letterbox or");
- pw.println(" Size Compat Mode.");
- printLetterboxHelp(pw);
printMultiWindowConfigHelp(pw);
pw.println(" reset [-d DISPLAY_ID]");
@@ -1011,49 +693,6 @@
}
}
- private void printLetterboxHelp(PrintWriter pw) {
- pw.println(" set-letterbox-style");
- pw.println(" Sets letterbox style using the following options:");
- pw.println(" --aspectRatio aspectRatio");
- pw.println(" Aspect ratio of letterbox for fixed orientation. If aspectRatio <= "
- + LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO);
- pw.println(" both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will");
- pw.println(" be ignored and framework implementation will determine aspect ratio.");
- pw.println(" --cornerRadius radius");
- pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,");
- pw.println(" both it and R.integer.config_letterboxActivityCornersRadius will be");
- pw.println(" ignored and corners of the activity won't be rounded.");
- pw.println(" --backgroundType [reset|solid_color|app_color_background");
- pw.println(" |app_color_background_floating|wallpaper]");
- pw.println(" Type of background used in the letterbox mode.");
- pw.println(" --backgroundColor color");
- pw.println(" Color of letterbox which is be used when letterbox background type");
- pw.println(" is 'solid-color'. Use (set)get-letterbox-style to check and control");
- pw.println(" letterbox background type. See Color#parseColor for allowed color");
- pw.println(" formats (#RRGGBB and some colors by name, e.g. magenta or olive).");
- pw.println(" --wallpaperBlurRadius radius");
- pw.println(" Blur radius for 'wallpaper' letterbox background. If radius <= 0");
- pw.println(" both it and R.dimen.config_letterboxBackgroundWallpaperBlurRadius");
- pw.println(" are ignored and 0 is used.");
- pw.println(" --wallpaperDarkScrimAlpha alpha");
- pw.println(" Alpha of a black translucent scrim shown over 'wallpaper'");
- pw.println(" letterbox background. If alpha < 0 or >= 1 both it and");
- pw.println(" R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha are ignored");
- pw.println(" and 0.0 (transparent) is used instead.");
- pw.println(" --horizontalPositionMultiplier multiplier");
- pw.println(" Horizontal position of app window center. If multiplier < 0 or > 1,");
- pw.println(" both it and R.dimen.config_letterboxHorizontalPositionMultiplier");
- pw.println(" are ignored and central position (0.5) is used.");
- pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType");
- pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha");
- pw.println(" |horizontalPositionMultiplier]");
- pw.println(" Resets overrides to default values for specified properties separated");
- pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'.");
- pw.println(" If no arguments provided, all values will be reset.");
- pw.println(" get-letterbox-style");
- pw.println(" Prints letterbox style configuration.");
- }
-
private void printMultiWindowConfigHelp(PrintWriter pw) {
pw.println(" set-multi-window-config");
pw.println(" Sets options to determine if activity should be shown in multi window:");
diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
index 6a4767a..0ded8fb 100644
--- a/services/core/java/com/android/server/wm/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -68,7 +68,7 @@
private static final String KEY_ROTATION_MEMORIZATION_TIMEOUT =
"rotation_memorization_timeout_millis";
private static final long DEFAULT_ROTATION_RESOLVER_TIMEOUT_MILLIS = 700L;
- private static final long DEFAULT_ROTATION_MEMORIZATION_TIMEOUT_MILLIS = 300_000L; // 5 minutes
+ private static final long DEFAULT_ROTATION_MEMORIZATION_TIMEOUT_MILLIS = 10_000L; // 10 seconds
private Handler mHandler;
private SensorManager mSensorManager;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 1a468d9..bba50a7 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2568,6 +2568,7 @@
private void setupWindowForRemoveOnExit() {
mRemoveOnExit = true;
setDisplayLayoutNeeded();
+ getDisplayContent().getDisplayPolicy().removeWindowLw(this);
// Request a focus update as this window's input channel is already gone. Otherwise
// we could have no focused window in input manager.
final boolean focusChanged = mWmService.updateFocusedWindowLocked(
@@ -5512,7 +5513,19 @@
mSurfacePlacementNeeded = false;
transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
mSurfacePosition);
- mSurfacePosition.offset(mXOffset, mYOffset);
+
+ if (mWallpaperScale != 1f) {
+ DisplayInfo displayInfo = getDisplayInfo();
+ Matrix matrix = mTmpMatrix;
+ matrix.setTranslate(mXOffset, mYOffset);
+ matrix.postScale(mWallpaperScale, mWallpaperScale, displayInfo.logicalWidth / 2f,
+ displayInfo.logicalHeight / 2f);
+ matrix.getValues(mTmpMatrixArray);
+ mSurfacePosition.offset(Math.round(mTmpMatrixArray[Matrix.MTRANS_X]),
+ Math.round(mTmpMatrixArray[Matrix.MTRANS_Y]));
+ } else {
+ mSurfacePosition.offset(mXOffset, mYOffset);
+ }
// Freeze position while we're unrotated, so the surface remains at the position it was
// prior to the rotation.
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 9a6e444..8094196 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -16,12 +16,6 @@
package com.android.server.wm;
-import static android.graphics.Matrix.MSCALE_X;
-import static android.graphics.Matrix.MSCALE_Y;
-import static android.graphics.Matrix.MSKEW_X;
-import static android.graphics.Matrix.MSKEW_Y;
-import static android.graphics.Matrix.MTRANS_X;
-import static android.graphics.Matrix.MTRANS_Y;
import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -54,14 +48,12 @@
import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
import android.content.Context;
-import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Debug;
import android.os.Trace;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import android.view.DisplayInfo;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -260,7 +252,10 @@
}
if (postDrawTransaction != null) {
- if (mLastHidden) {
+ // If there is no surface, the last draw was for the previous surface. We don't want to
+ // wait until the new surface is shown and instead just apply the transaction right
+ // away.
+ if (mLastHidden && mDrawState != NO_SURFACE) {
mPostDrawTransaction.merge(postDrawTransaction);
layoutNeeded = true;
} else {
@@ -683,8 +678,9 @@
}
// We don't apply animation for application main window here since this window type
- // should be controlled by AppWindowToken in general.
- if (mAttrType != TYPE_BASE_APPLICATION) {
+ // should be controlled by ActivityRecord in general. Wallpaper is also excluded because
+ // WallpaperController should handle it.
+ if (mAttrType != TYPE_BASE_APPLICATION && !mIsWallpaper) {
applyAnimationLocked(transit, true);
}
@@ -829,6 +825,10 @@
}
void destroySurface(SurfaceControl.Transaction t) {
+ // Since the SurfaceControl is getting torn down, it's safe to just clean up any
+ // pending transactions that were in mPostDrawTransaction, as well.
+ t.merge(mPostDrawTransaction);
+
try {
if (mSurfaceController != null) {
mSurfaceController.destroy(t);
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e13ef99..a94ad4a 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -997,10 +997,7 @@
}
void NativeInputManager::displayRemoved(JNIEnv* env, int32_t displayId) {
- // Set an empty list to remove all handles from the specific display.
- std::vector<sp<InputWindowHandle>> windowHandles;
- mInputManager->getDispatcher()->setInputWindows({{displayId, windowHandles}});
- mInputManager->getDispatcher()->setFocusedApplication(displayId, nullptr);
+ mInputManager->getDispatcher()->displayRemoved(displayId);
}
void NativeInputManager::setFocusedApplication(JNIEnv* env, int32_t displayId,
diff --git a/services/core/jni/com_android_server_sensor_SensorService.cpp b/services/core/jni/com_android_server_sensor_SensorService.cpp
index d0f56e6..63b7dfb 100644
--- a/services/core/jni/com_android_server_sensor_SensorService.cpp
+++ b/services/core/jni/com_android_server_sensor_SensorService.cpp
@@ -32,6 +32,7 @@
namespace android {
+static JavaVM* sJvm = nullptr;
static jmethodID sMethodIdOnProximityActive;
class NativeSensorService {
@@ -93,8 +94,8 @@
}
void NativeSensorService::ProximityActiveListenerDelegate::onProximityActive(bool isActive) {
- AndroidRuntime::getJNIEnv()->CallVoidMethod(mListener, sMethodIdOnProximityActive,
- static_cast<jboolean>(isActive));
+ auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
+ jniEnv->CallVoidMethod(mListener, sMethodIdOnProximityActive, static_cast<jboolean>(isActive));
}
static jlong startSensorServiceNative(JNIEnv* env, jclass, jobject listener) {
@@ -128,7 +129,8 @@
};
-int register_android_server_sensor_SensorService(JNIEnv* env) {
+int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env) {
+ sJvm = vm;
jclass listenerClass = FindClassOrDie(env, PROXIMITY_ACTIVE_CLASS);
sMethodIdOnProximityActive = GetMethodIDOrDie(env, listenerClass, "onProximityActive", "(Z)V");
return jniRegisterNativeMethods(env, "com/android/server/sensors/SensorService", methods,
diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
index 698e3f7..9029fe7 100644
--- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp
+++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp
@@ -41,8 +41,18 @@
static jmethodID sMethodIdOnComplete;
static jclass sFrequencyMappingClass;
static jmethodID sFrequencyMappingCtor;
-static jclass sVibratorInfoClass;
-static jmethodID sVibratorInfoCtor;
+static struct {
+ jmethodID setCapabilities;
+ jmethodID setSupportedEffects;
+ jmethodID setSupportedBraking;
+ jmethodID setPwlePrimitiveDurationMax;
+ jmethodID setPwleSizeMax;
+ jmethodID setSupportedPrimitive;
+ jmethodID setPrimitiveDelayMax;
+ jmethodID setCompositionSizeMax;
+ jmethodID setQFactor;
+ jmethodID setFrequencyMapping;
+} sVibratorInfoBuilderClassInfo;
static struct {
jfieldID id;
jfieldID scale;
@@ -352,68 +362,88 @@
wrapper->halCall<void>(alwaysOnDisableFn, "alwaysOnDisable");
}
-static jobject vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,
- jfloat suggestedSafeRange) {
+static jboolean vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,
+ jfloat suggestedSafeRange, jobject vibratorInfoBuilder) {
VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr);
if (wrapper == nullptr) {
ALOGE("vibratorGetInfo failed because native wrapper was not initialized");
- return nullptr;
+ return JNI_FALSE;
}
vibrator::Info info = wrapper->getVibratorInfo();
- jlong capabilities =
- static_cast<jlong>(info.capabilities.valueOr(vibrator::Capabilities::NONE));
- jfloat minFrequency = static_cast<jfloat>(info.minFrequency.valueOr(NAN));
- jfloat resonantFrequency = static_cast<jfloat>(info.resonantFrequency.valueOr(NAN));
- jfloat frequencyResolution = static_cast<jfloat>(info.frequencyResolution.valueOr(NAN));
- jfloat qFactor = static_cast<jfloat>(info.qFactor.valueOr(NAN));
- jintArray supportedEffects = nullptr;
- jintArray supportedBraking = nullptr;
- jintArray supportedPrimitives = nullptr;
- jintArray primitiveDurations = nullptr;
- jfloatArray maxAmplitudes = nullptr;
-
+ if (info.capabilities.isOk()) {
+ env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setCapabilities,
+ static_cast<jlong>(info.capabilities.value()));
+ }
if (info.supportedEffects.isOk()) {
std::vector<aidl::Effect> effects = info.supportedEffects.value();
- supportedEffects = env->NewIntArray(effects.size());
+ jintArray supportedEffects = env->NewIntArray(effects.size());
env->SetIntArrayRegion(supportedEffects, 0, effects.size(),
reinterpret_cast<jint*>(effects.data()));
+ env->CallObjectMethod(vibratorInfoBuilder,
+ sVibratorInfoBuilderClassInfo.setSupportedEffects, supportedEffects);
}
if (info.supportedBraking.isOk()) {
std::vector<aidl::Braking> braking = info.supportedBraking.value();
- supportedBraking = env->NewIntArray(braking.size());
+ jintArray supportedBraking = env->NewIntArray(braking.size());
env->SetIntArrayRegion(supportedBraking, 0, braking.size(),
reinterpret_cast<jint*>(braking.data()));
+ env->CallObjectMethod(vibratorInfoBuilder,
+ sVibratorInfoBuilderClassInfo.setSupportedBraking, supportedBraking);
+ }
+ if (info.pwlePrimitiveDurationMax.isOk()) {
+ env->CallObjectMethod(vibratorInfoBuilder,
+ sVibratorInfoBuilderClassInfo.setPwlePrimitiveDurationMax,
+ static_cast<jint>(info.pwlePrimitiveDurationMax.value().count()));
+ }
+ if (info.pwleSizeMax.isOk()) {
+ // Use (pwleMaxSize - 1) to account for a possible extra braking segment added by the
+ // vibratorPerformPwleEffect method.
+ env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setPwleSizeMax,
+ static_cast<jint>(info.pwleSizeMax.value() - 1));
}
if (info.supportedPrimitives.isOk()) {
- std::vector<aidl::CompositePrimitive> primitives = info.supportedPrimitives.value();
- supportedPrimitives = env->NewIntArray(primitives.size());
- env->SetIntArrayRegion(supportedPrimitives, 0, primitives.size(),
- reinterpret_cast<jint*>(primitives.data()));
- }
- if (info.primitiveDurations.isOk()) {
- std::vector<int32_t> durations;
- for (auto duration : info.primitiveDurations.value()) {
- durations.push_back(duration.count());
+ auto durations = info.primitiveDurations.valueOr({});
+ for (auto& primitive : info.supportedPrimitives.value()) {
+ auto primitiveIdx = static_cast<size_t>(primitive);
+ auto duration = durations.size() > primitiveIdx ? durations[primitiveIdx].count() : 0;
+ env->CallObjectMethod(vibratorInfoBuilder,
+ sVibratorInfoBuilderClassInfo.setSupportedPrimitive,
+ static_cast<jint>(primitive), static_cast<jint>(duration));
}
- primitiveDurations = env->NewIntArray(durations.size());
- env->SetIntArrayRegion(primitiveDurations, 0, durations.size(),
- reinterpret_cast<jint*>(durations.data()));
}
+ if (info.primitiveDelayMax.isOk()) {
+ env->CallObjectMethod(vibratorInfoBuilder,
+ sVibratorInfoBuilderClassInfo.setPrimitiveDelayMax,
+ static_cast<jint>(info.primitiveDelayMax.value().count()));
+ }
+ if (info.compositionSizeMax.isOk()) {
+ env->CallObjectMethod(vibratorInfoBuilder,
+ sVibratorInfoBuilderClassInfo.setCompositionSizeMax,
+ static_cast<jint>(info.compositionSizeMax.value()));
+ }
+ if (info.qFactor.isOk()) {
+ env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setQFactor,
+ static_cast<jfloat>(info.qFactor.value()));
+ }
+
+ jfloat minFrequency = static_cast<jfloat>(info.minFrequency.valueOr(NAN));
+ jfloat resonantFrequency = static_cast<jfloat>(info.resonantFrequency.valueOr(NAN));
+ jfloat frequencyResolution = static_cast<jfloat>(info.frequencyResolution.valueOr(NAN));
+ jfloatArray maxAmplitudes = nullptr;
if (info.maxAmplitudes.isOk()) {
std::vector<float> amplitudes = info.maxAmplitudes.value();
maxAmplitudes = env->NewFloatArray(amplitudes.size());
env->SetFloatArrayRegion(maxAmplitudes, 0, amplitudes.size(),
reinterpret_cast<jfloat*>(amplitudes.data()));
}
-
jobject frequencyMapping = env->NewObject(sFrequencyMappingClass, sFrequencyMappingCtor,
minFrequency, resonantFrequency, frequencyResolution,
suggestedSafeRange, maxAmplitudes);
+ env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setFrequencyMapping,
+ frequencyMapping);
- return env->NewObject(sVibratorInfoClass, sVibratorInfoCtor, wrapper->getVibratorId(),
- capabilities, supportedEffects, supportedBraking, supportedPrimitives,
- primitiveDurations, qFactor, frequencyMapping);
+ return info.checkAndLogFailure("vibratorGetInfo") ? JNI_FALSE : JNI_TRUE;
}
static const JNINativeMethod method_table[] = {
@@ -433,7 +463,7 @@
{"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl},
{"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable},
{"alwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
- {"getInfo", "(JF)Landroid/os/VibratorInfo;", (void*)vibratorGetInfo},
+ {"getInfo", "(JFLandroid/os/VibratorInfo$Builder;)Z", (void*)vibratorGetInfo},
};
int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env) {
@@ -459,11 +489,38 @@
sFrequencyMappingClass = static_cast<jclass>(env->NewGlobalRef(frequencyMappingClass));
sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFFF[F)V");
- jclass vibratorInfoClass = FindClassOrDie(env, "android/os/VibratorInfo");
- sVibratorInfoClass = (jclass)env->NewGlobalRef(vibratorInfoClass);
- sVibratorInfoCtor =
- GetMethodIDOrDie(env, sVibratorInfoClass, "<init>",
- "(IJ[I[I[I[IFLandroid/os/VibratorInfo$FrequencyMapping;)V");
+ jclass vibratorInfoBuilderClass = FindClassOrDie(env, "android/os/VibratorInfo$Builder");
+ sVibratorInfoBuilderClassInfo.setCapabilities =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setCapabilities",
+ "(J)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setSupportedEffects =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setSupportedEffects",
+ "([I)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setSupportedBraking =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setSupportedBraking",
+ "([I)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setPwlePrimitiveDurationMax =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setPwlePrimitiveDurationMax",
+ "(I)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setPwleSizeMax =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setPwleSizeMax",
+ "(I)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setSupportedPrimitive =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setSupportedPrimitive",
+ "(II)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setPrimitiveDelayMax =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setPrimitiveDelayMax",
+ "(I)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setCompositionSizeMax =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setCompositionSizeMax",
+ "(I)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setQFactor =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setQFactor",
+ "(F)Landroid/os/VibratorInfo$Builder;");
+ sVibratorInfoBuilderClassInfo.setFrequencyMapping =
+ GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setFrequencyMapping",
+ "(Landroid/os/VibratorInfo$FrequencyMapping;)"
+ "Landroid/os/VibratorInfo$Builder;");
return jniRegisterNativeMethods(env,
"com/android/server/vibrator/VibratorController$NativeWrapper",
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index b8961d5..ff61abc 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -62,7 +62,7 @@
int register_android_server_FaceService(JNIEnv* env);
int register_android_server_GpuService(JNIEnv* env);
int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env);
-int register_android_server_sensor_SensorService(JNIEnv* env);
+int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env);
};
using namespace android;
@@ -118,6 +118,6 @@
register_android_server_FaceService(env);
register_android_server_GpuService(env);
register_android_server_stats_pull_StatsPullAtomService(env);
- register_android_server_sensor_SensorService(env);
+ register_android_server_sensor_SensorService(vm, env);
return JNI_VERSION_1_4;
}
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index c6dfe9d..82aaa61 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -77,6 +77,10 @@
<xs:annotation name="final"/>
</xs:element>
<xs:element name="timing" type="hbmTiming" minOccurs="1" maxOccurs="1"/>
+ <xs:element type="refreshRateRange" name="refreshRate" minOccurs="0" maxOccurs="1">
+ <xs:annotation name="nullable"/>
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:all>
<xs:attribute name="enabled" type="xs:boolean" use="optional"/>
</xs:complexType>
@@ -129,14 +133,30 @@
<xs:complexType name="sensorDetails">
<xs:sequence>
<xs:element type="xs:string" name="type" minOccurs="0" maxOccurs="1">
- <xs:annotation name="nullable" />
+ <xs:annotation name="nullable"/>
<xs:annotation name="final"/>
</xs:element>
<xs:element type="xs:string" name="name" minOccurs="0" maxOccurs="1">
- <xs:annotation name="nullable" />
+ <xs:annotation name="nullable"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element type="refreshRateRange" name="refreshRate" minOccurs="0" maxOccurs="1">
+ <xs:annotation name="nullable"/>
<xs:annotation name="final"/>
</xs:element>
</xs:sequence>
</xs:complexType>
+ <xs:complexType name="refreshRateRange">
+ <xs:sequence>
+ <xs:element type="xs:nonNegativeInteger" name="minimum" minOccurs="1" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element type="xs:nonNegativeInteger" name="maximum" minOccurs="1" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+
+
</xs:schema>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 7c2436d..6e2e362 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -44,10 +44,12 @@
ctor public HighBrightnessMode();
method public boolean getEnabled();
method @NonNull public final java.math.BigDecimal getMinimumLux_all();
+ method @Nullable public final com.android.server.display.config.RefreshRateRange getRefreshRate_all();
method public com.android.server.display.config.HbmTiming getTiming_all();
method @NonNull public final java.math.BigDecimal getTransitionPoint_all();
method public void setEnabled(boolean);
method public final void setMinimumLux_all(@NonNull java.math.BigDecimal);
+ method public final void setRefreshRate_all(@Nullable com.android.server.display.config.RefreshRateRange);
method public void setTiming_all(com.android.server.display.config.HbmTiming);
method public final void setTransitionPoint_all(@NonNull java.math.BigDecimal);
}
@@ -65,11 +67,21 @@
method public final void setValue(@NonNull java.math.BigDecimal);
}
+ public class RefreshRateRange {
+ ctor public RefreshRateRange();
+ method public final java.math.BigInteger getMaximum();
+ method public final java.math.BigInteger getMinimum();
+ method public final void setMaximum(java.math.BigInteger);
+ method public final void setMinimum(java.math.BigInteger);
+ }
+
public class SensorDetails {
ctor public SensorDetails();
method @Nullable public final String getName();
+ method @Nullable public final com.android.server.display.config.RefreshRateRange getRefreshRate();
method @Nullable public final String getType();
method public final void setName(@Nullable String);
+ method public final void setRefreshRate(@Nullable com.android.server.display.config.RefreshRateRange);
method public final void setType(@Nullable String);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index fa24e52..78ad59f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -169,6 +169,7 @@
import android.app.admin.DevicePolicyManager.PasswordComplexity;
import android.app.admin.DevicePolicyManager.PersonalAppsSuspensionReason;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DevicePolicyManagerLiteInternal;
import android.app.admin.DevicePolicySafetyChecker;
import android.app.admin.DeviceStateCache;
import android.app.admin.FactoryResetProtectionPolicy;
@@ -1748,6 +1749,8 @@
mTransferOwnershipMetadataManager = mInjector.newTransferOwnershipMetadataManager();
mBugreportCollectionManager = new RemoteBugreportManager(this, mInjector);
+ // "Lite" interface is available even when the device doesn't have the feature
+ LocalServices.addService(DevicePolicyManagerLiteInternal.class, mLocalService);
if (!mHasFeature) {
// Skip the rest of the initialization
mSetupContentObserver = null;
@@ -12612,7 +12615,8 @@
}
@VisibleForTesting
- final class LocalService extends DevicePolicyManagerInternal {
+ final class LocalService extends DevicePolicyManagerInternal
+ implements DevicePolicyManagerLiteInternal {
private List<OnCrossProfileWidgetProvidersChangeListener> mWidgetProviderListeners;
@Override
@@ -14313,7 +14317,7 @@
}
@Override
- public boolean isAffiliatedUser() {
+ public boolean isCallingUserAffiliated() {
if (!mHasFeature) {
return false;
}
@@ -14323,6 +14327,17 @@
}
}
+ @Override
+ public boolean isAffiliatedUser(@UserIdInt int userId) {
+ if (!mHasFeature) {
+ return false;
+ }
+ final CallerIdentity caller = getCallerIdentity();
+ Preconditions.checkCallAuthorization(hasCrossUsersPermission(caller, userId));
+
+ return isUserAffiliatedWithDeviceLocked(userId);
+ }
+
private boolean isUserAffiliatedWithDeviceLocked(@UserIdInt int userId) {
if (!mOwners.hasDeviceOwner()) {
return false;
@@ -17550,10 +17565,15 @@
@Override
public boolean isUsbDataSignalingEnabled(String packageName) {
+ final CallerIdentity caller = getCallerIdentity(packageName);
synchronized (getLockObject()) {
- final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(
- getCallerIdentity(packageName));
- return admin.mUsbDataSignalingEnabled;
+ // If the caller is an admin, return the policy set by itself. Otherwise
+ // return the device-wide policy.
+ if (isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)) {
+ return getProfileOwnerOrDeviceOwnerLocked(caller).mUsbDataSignalingEnabled;
+ } else {
+ return isUsbDataSignalingEnabledInternalLocked();
+ }
}
}
@@ -17563,12 +17583,16 @@
Preconditions.checkCallAuthorization(isSystemUid(caller));
synchronized (getLockObject()) {
- final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
- UserHandle.USER_SYSTEM);
- return admin == null || admin.mUsbDataSignalingEnabled;
+ return isUsbDataSignalingEnabledInternalLocked();
}
}
+ private boolean isUsbDataSignalingEnabledInternalLocked() {
+ final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
+ UserHandle.USER_SYSTEM);
+ return admin == null || admin.mUsbDataSignalingEnabled;
+ }
+
@Override
public boolean canUsbDataSignalingBeDisabled() {
return mInjector.binderWithCleanCallingIdentity(() ->
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
index 86437a2..1cbc634 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
@@ -21,7 +21,7 @@
import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
import android.app.admin.DevicePolicyManager.OperationSafetyReason;
-import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DevicePolicyManagerLiteInternal;
import android.app.admin.DevicePolicySafetyChecker;
import android.os.Handler;
import android.os.Looper;
@@ -80,8 +80,8 @@
+ ", should be " + operationToString(mOperation));
}
String reasonName = operationSafetyReasonToString(reason);
- DevicePolicyManagerInternal dpmi = LocalServices
- .getService(DevicePolicyManagerInternal.class);
+ DevicePolicyManagerLiteInternal dpmi = LocalServices
+ .getService(DevicePolicyManagerLiteInternal.class);
Slog.i(TAG, "notifying " + reasonName + " is UNSAFE");
dpmi.notifyUnsafeOperationStateChanged(this, reason, /* isSafe= */ false);
diff --git a/services/net/Android.bp b/services/net/Android.bp
index f92db86..a822257 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -37,15 +37,7 @@
name: "services.net-module-wifi",
srcs: [
":framework-services-net-module-wifi-shared-srcs",
- ":net-module-utils-srcs",
":net-utils-services-common-srcs",
- "java/android/net/ip/IpClientCallbacks.java",
- "java/android/net/ip/IpClientManager.java",
- "java/android/net/ip/IpClientUtil.java",
- "java/android/net/util/KeepalivePacketDataUtil.java",
- "java/android/net/util/NetworkConstants.java",
- "java/android/net/IpMemoryStore.java",
- "java/android/net/NetworkMonitorManager.java",
],
sdk_version: "module_current",
min_sdk_version: "30",
@@ -59,7 +51,6 @@
// All the classes in netd_aidl_interface must be jarjar so they do not conflict with the
// classes generated by netd_aidl_interfaces-platform-java above.
"netd_aidl_interface-V3-java",
- "netlink-client",
"networkstack-client",
"modules-utils-build_system",
],
@@ -89,11 +80,7 @@
filegroup {
name: "services-connectivity-shared-srcs",
srcs: [
- // TODO: move to networkstack-client
- "java/android/net/IpMemoryStore.java",
- "java/android/net/NetworkMonitorManager.java",
// TODO: move to libs/net
- "java/android/net/util/KeepalivePacketDataUtil.java",
"java/android/net/util/NetworkConstants.java",
],
}
diff --git a/services/net/java/android/net/IpMemoryStore.java b/services/net/java/android/net/IpMemoryStore.java
deleted file mode 100644
index 8df2e0d..0000000
--- a/services/net/java/android/net/IpMemoryStore.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.annotation.NonNull;
-import android.content.Context;
-import android.net.networkstack.ModuleNetworkStackClient;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.function.Consumer;
-
-/**
- * Manager class used to communicate with the ip memory store service in the network stack,
- * which is running in a separate module.
- * @hide
-*/
-public class IpMemoryStore extends IpMemoryStoreClient {
- private static final String TAG = IpMemoryStore.class.getSimpleName();
- @NonNull private final CompletableFuture<IIpMemoryStore> mService;
- @NonNull private final AtomicReference<CompletableFuture<IIpMemoryStore>> mTailNode;
-
- public IpMemoryStore(@NonNull final Context context) {
- super(context);
- mService = new CompletableFuture<>();
- mTailNode = new AtomicReference<CompletableFuture<IIpMemoryStore>>(mService);
- getModuleNetworkStackClient(context).fetchIpMemoryStore(
- new IIpMemoryStoreCallbacks.Stub() {
- @Override
- public void onIpMemoryStoreFetched(@NonNull final IIpMemoryStore memoryStore) {
- mService.complete(memoryStore);
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
-
- @Override
- public String getInterfaceHash() {
- return this.HASH;
- }
- });
- }
-
- /*
- * If the IpMemoryStore is ready, this function will run the request synchronously.
- * Otherwise, it will enqueue the requests for execution immediately after the
- * service becomes ready. The requests are guaranteed to be executed in the order
- * they are sumbitted.
- */
- @Override
- protected void runWhenServiceReady(Consumer<IIpMemoryStore> cb) throws ExecutionException {
- mTailNode.getAndUpdate(future -> future.handle((store, exception) -> {
- if (exception != null) {
- // this should never happens since we also catch the exception below
- Log.wtf(TAG, "Error fetching IpMemoryStore", exception);
- return store;
- }
-
- try {
- cb.accept(store);
- } catch (Exception e) {
- Log.wtf(TAG, "Exception occured: " + e.getMessage());
- }
- return store;
- }));
- }
-
- @VisibleForTesting
- protected ModuleNetworkStackClient getModuleNetworkStackClient(Context context) {
- return ModuleNetworkStackClient.getInstance(context);
- }
-
- /** Gets an instance of the memory store */
- @NonNull
- public static IpMemoryStore getMemoryStore(final Context context) {
- return new IpMemoryStore(context);
- }
-}
diff --git a/services/net/java/android/net/NetworkMonitorManager.java b/services/net/java/android/net/NetworkMonitorManager.java
deleted file mode 100644
index 0f66981..0000000
--- a/services/net/java/android/net/NetworkMonitorManager.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.annotation.Hide;
-import android.annotation.NonNull;
-import android.os.Binder;
-import android.os.RemoteException;
-import android.util.Log;
-
-/**
- * A convenience wrapper for INetworkMonitor.
- *
- * Wraps INetworkMonitor calls, making them a bit more friendly to use. Currently handles:
- * - Clearing calling identity
- * - Ignoring RemoteExceptions
- * - Converting to stable parcelables
- *
- * By design, all methods on INetworkMonitor are asynchronous oneway IPCs and are thus void. All the
- * wrapper methods in this class return a boolean that callers can use to determine whether
- * RemoteException was thrown.
- */
-@Hide
-public class NetworkMonitorManager {
-
- @NonNull private final INetworkMonitor mNetworkMonitor;
- @NonNull private final String mTag;
-
- public NetworkMonitorManager(@NonNull INetworkMonitor networkMonitorManager,
- @NonNull String tag) {
- mNetworkMonitor = networkMonitorManager;
- mTag = tag;
- }
-
- public NetworkMonitorManager(@NonNull INetworkMonitor networkMonitorManager) {
- this(networkMonitorManager, NetworkMonitorManager.class.getSimpleName());
- }
-
- private void log(String s, Throwable e) {
- Log.e(mTag, s, e);
- }
-
- // CHECKSTYLE:OFF Generated code
-
- public boolean start() {
- final long token = Binder.clearCallingIdentity();
- try {
- mNetworkMonitor.start();
- return true;
- } catch (RemoteException e) {
- log("Error in start", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- public boolean launchCaptivePortalApp() {
- final long token = Binder.clearCallingIdentity();
- try {
- mNetworkMonitor.launchCaptivePortalApp();
- return true;
- } catch (RemoteException e) {
- log("Error in launchCaptivePortalApp", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- public boolean notifyCaptivePortalAppFinished(int response) {
- final long token = Binder.clearCallingIdentity();
- try {
- mNetworkMonitor.notifyCaptivePortalAppFinished(response);
- return true;
- } catch (RemoteException e) {
- log("Error in notifyCaptivePortalAppFinished", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- public boolean setAcceptPartialConnectivity() {
- final long token = Binder.clearCallingIdentity();
- try {
- mNetworkMonitor.setAcceptPartialConnectivity();
- return true;
- } catch (RemoteException e) {
- log("Error in setAcceptPartialConnectivity", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- public boolean forceReevaluation(int uid) {
- final long token = Binder.clearCallingIdentity();
- try {
- mNetworkMonitor.forceReevaluation(uid);
- return true;
- } catch (RemoteException e) {
- log("Error in forceReevaluation", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- public boolean notifyPrivateDnsChanged(PrivateDnsConfigParcel config) {
- final long token = Binder.clearCallingIdentity();
- try {
- mNetworkMonitor.notifyPrivateDnsChanged(config);
- return true;
- } catch (RemoteException e) {
- log("Error in notifyPrivateDnsChanged", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- public boolean notifyDnsResponse(int returnCode) {
- final long token = Binder.clearCallingIdentity();
- try {
- mNetworkMonitor.notifyDnsResponse(returnCode);
- return true;
- } catch (RemoteException e) {
- log("Error in notifyDnsResponse", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- public boolean notifyNetworkConnected(LinkProperties lp, NetworkCapabilities nc) {
- final long token = Binder.clearCallingIdentity();
- try {
- mNetworkMonitor.notifyNetworkConnected(lp, nc);
- return true;
- } catch (RemoteException e) {
- log("Error in notifyNetworkConnected", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- public boolean notifyNetworkDisconnected() {
- final long token = Binder.clearCallingIdentity();
- try {
- mNetworkMonitor.notifyNetworkDisconnected();
- return true;
- } catch (RemoteException e) {
- log("Error in notifyNetworkDisconnected", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- public boolean notifyLinkPropertiesChanged(LinkProperties lp) {
- final long token = Binder.clearCallingIdentity();
- try {
- mNetworkMonitor.notifyLinkPropertiesChanged(lp);
- return true;
- } catch (RemoteException e) {
- log("Error in notifyLinkPropertiesChanged", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- public boolean notifyNetworkCapabilitiesChanged(NetworkCapabilities nc) {
- final long token = Binder.clearCallingIdentity();
- try {
- mNetworkMonitor.notifyNetworkCapabilitiesChanged(nc);
- return true;
- } catch (RemoteException e) {
- log("Error in notifyNetworkCapabilitiesChanged", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- // CHECKSTYLE:ON Generated code
-}
diff --git a/services/net/java/android/net/ip/IpClientCallbacks.java b/services/net/java/android/net/ip/IpClientCallbacks.java
deleted file mode 100644
index b17fcaa..0000000
--- a/services/net/java/android/net/ip/IpClientCallbacks.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import android.net.DhcpResultsParcelable;
-import android.net.Layer2PacketParcelable;
-import android.net.LinkProperties;
-
-import java.util.List;
-
-/**
- * Callbacks for handling IpClient events.
- *
- * This is a convenience class to allow clients not to override all methods of IIpClientCallbacks,
- * and avoid unparceling arguments.
- * These methods are called asynchronously on a Binder thread, as IpClient lives in a different
- * process.
- * @hide
- */
-public class IpClientCallbacks {
-
- /**
- * Callback called upon IpClient creation.
- *
- * @param ipClient The Binder token to communicate with IpClient.
- */
- public void onIpClientCreated(IIpClient ipClient) {}
-
- /**
- * Callback called prior to DHCP discovery/renewal.
- *
- * <p>In order to receive onPreDhcpAction(), call #withPreDhcpAction() when constructing a
- * ProvisioningConfiguration.
- *
- * <p>Implementations of onPreDhcpAction() must call IpClient#completedPreDhcpAction() to
- * indicate that DHCP is clear to proceed.
- */
- public void onPreDhcpAction() {}
-
- /**
- * Callback called after DHCP discovery/renewal.
- */
- public void onPostDhcpAction() {}
-
- /**
- * Callback called when new DHCP results are available.
- *
- * <p>This is purely advisory and not an indication of provisioning success or failure. This is
- * only here for callers that want to expose DHCPv4 results to other APIs
- * (e.g., WifiInfo#setInetAddress).
- *
- * <p>DHCPv4 or static IPv4 configuration failure or success can be determined by whether or not
- * the passed-in DhcpResults object is null.
- */
- public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) {
- // In general callbacks would not use a parcelable directly (DhcpResultsParcelable), and
- // would use a wrapper instead, because of the lack of safety of stable parcelables. But
- // there are already two classes in the tree for DHCP information: DhcpInfo and DhcpResults,
- // and neither of them exposes an appropriate API (they are bags of mutable fields and can't
- // be changed because they are public API and @UnsupportedAppUsage, being no better than the
- // stable parcelable). Adding a third class would cost more than the gain considering that
- // the only client of this callback is WiFi, which will end up converting the results to
- // DhcpInfo anyway.
- }
-
- /**
- * Indicates that provisioning was successful.
- */
- public void onProvisioningSuccess(LinkProperties newLp) {}
-
- /**
- * Indicates that provisioning failed.
- */
- public void onProvisioningFailure(LinkProperties newLp) {}
-
- /**
- * Invoked on LinkProperties changes.
- */
- public void onLinkPropertiesChange(LinkProperties newLp) {}
-
- /**Called when the internal IpReachabilityMonitor (if enabled) has
- * detected the loss of a critical number of required neighbors.
- */
- public void onReachabilityLost(String logMsg) {}
-
- /**
- * Called when the IpClient state machine terminates.
- */
- public void onQuit() {}
-
- /**
- * Called to indicate that a new APF program must be installed to filter incoming packets.
- */
- public void installPacketFilter(byte[] filter) {}
-
- /**
- * Called to indicate that the APF Program & data buffer must be read asynchronously from the
- * wifi driver.
- *
- * <p>Due to Wifi HAL limitations, the current implementation only supports dumping the entire
- * buffer. In response to this request, the driver returns the data buffer asynchronously
- * by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
- */
- public void startReadPacketFilter() {}
-
- /**
- * If multicast filtering cannot be accomplished with APF, this function will be called to
- * actuate multicast filtering using another means.
- */
- public void setFallbackMulticastFilter(boolean enabled) {}
-
- /**
- * Enabled/disable Neighbor Discover offload functionality. This is called, for example,
- * whenever 464xlat is being started or stopped.
- */
- public void setNeighborDiscoveryOffload(boolean enable) {}
-
- /**
- * Invoked on starting preconnection process.
- */
- public void onPreconnectionStart(List<Layer2PacketParcelable> packets) {}
-}
diff --git a/services/net/java/android/net/ip/IpClientManager.java b/services/net/java/android/net/ip/IpClientManager.java
deleted file mode 100644
index b45405f..0000000
--- a/services/net/java/android/net/ip/IpClientManager.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import android.annotation.Hide;
-import android.annotation.NonNull;
-import android.net.NattKeepalivePacketData;
-import android.net.ProxyInfo;
-import android.net.TcpKeepalivePacketData;
-import android.net.TcpKeepalivePacketDataParcelable;
-import android.net.shared.Layer2Information;
-import android.net.shared.ProvisioningConfiguration;
-import android.net.util.KeepalivePacketDataUtil;
-import android.os.Binder;
-import android.os.RemoteException;
-import android.util.Log;
-
-/**
- * A convenience wrapper for IpClient.
- *
- * Wraps IIpClient calls, making them a bit more friendly to use. Currently handles:
- * - Clearing calling identity
- * - Ignoring RemoteExceptions
- * - Converting to stable parcelables
- *
- * By design, all methods on IIpClient are asynchronous oneway IPCs and are thus void. All the
- * wrapper methods in this class return a boolean that callers can use to determine whether
- * RemoteException was thrown.
- */
-@Hide
-public class IpClientManager {
- @NonNull private final IIpClient mIpClient;
- @NonNull private final String mTag;
-
- public IpClientManager(@NonNull IIpClient ipClient, @NonNull String tag) {
- mIpClient = ipClient;
- mTag = tag;
- }
-
- public IpClientManager(@NonNull IIpClient ipClient) {
- this(ipClient, IpClientManager.class.getSimpleName());
- }
-
- private void log(String s, Throwable e) {
- Log.e(mTag, s, e);
- }
-
- /**
- * For clients using {@link ProvisioningConfiguration.Builder#withPreDhcpAction()}, must be
- * called after {@link IIpClientCallbacks#onPreDhcpAction} to indicate that DHCP is clear to
- * proceed.
- */
- public boolean completedPreDhcpAction() {
- final long token = Binder.clearCallingIdentity();
- try {
- mIpClient.completedPreDhcpAction();
- return true;
- } catch (RemoteException e) {
- log("Error completing PreDhcpAction", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Confirm the provisioning configuration.
- */
- public boolean confirmConfiguration() {
- final long token = Binder.clearCallingIdentity();
- try {
- mIpClient.confirmConfiguration();
- return true;
- } catch (RemoteException e) {
- log("Error confirming IpClient configuration", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Indicate that packet filter read is complete.
- */
- public boolean readPacketFilterComplete(byte[] data) {
- final long token = Binder.clearCallingIdentity();
- try {
- mIpClient.readPacketFilterComplete(data);
- return true;
- } catch (RemoteException e) {
- log("Error notifying IpClient of packet filter read", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Shut down this IpClient instance altogether.
- */
- public boolean shutdown() {
- final long token = Binder.clearCallingIdentity();
- try {
- mIpClient.shutdown();
- return true;
- } catch (RemoteException e) {
- log("Error shutting down IpClient", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Start provisioning with the provided parameters.
- */
- public boolean startProvisioning(ProvisioningConfiguration prov) {
- final long token = Binder.clearCallingIdentity();
- try {
- mIpClient.startProvisioning(prov.toStableParcelable());
- return true;
- } catch (RemoteException e) {
- log("Error starting IpClient provisioning", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Stop this IpClient.
- *
- * <p>This does not shut down the StateMachine itself, which is handled by {@link #shutdown()}.
- */
- public boolean stop() {
- final long token = Binder.clearCallingIdentity();
- try {
- mIpClient.stop();
- return true;
- } catch (RemoteException e) {
- log("Error stopping IpClient", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Set the TCP buffer sizes to use.
- *
- * This may be called, repeatedly, at any time before or after a call to
- * #startProvisioning(). The setting is cleared upon calling #stop().
- */
- public boolean setTcpBufferSizes(String tcpBufferSizes) {
- final long token = Binder.clearCallingIdentity();
- try {
- mIpClient.setTcpBufferSizes(tcpBufferSizes);
- return true;
- } catch (RemoteException e) {
- log("Error setting IpClient TCP buffer sizes", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Set the HTTP Proxy configuration to use.
- *
- * This may be called, repeatedly, at any time before or after a call to
- * #startProvisioning(). The setting is cleared upon calling #stop().
- */
- public boolean setHttpProxy(ProxyInfo proxyInfo) {
- final long token = Binder.clearCallingIdentity();
- try {
- mIpClient.setHttpProxy(proxyInfo);
- return true;
- } catch (RemoteException e) {
- log("Error setting IpClient proxy", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Enable or disable the multicast filter. Attempts to use APF to accomplish the filtering,
- * if not, Callback.setFallbackMulticastFilter() is called.
- */
- public boolean setMulticastFilter(boolean enabled) {
- final long token = Binder.clearCallingIdentity();
- try {
- mIpClient.setMulticastFilter(enabled);
- return true;
- } catch (RemoteException e) {
- log("Error setting multicast filter", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Add a TCP keepalive packet filter before setting up keepalive offload.
- */
- public boolean addKeepalivePacketFilter(int slot, TcpKeepalivePacketData pkt) {
- return addKeepalivePacketFilter(slot, KeepalivePacketDataUtil.toStableParcelable(pkt));
- }
-
- /**
- * Add a TCP keepalive packet filter before setting up keepalive offload.
- * @deprecated This method is for use on pre-S platforms where TcpKeepalivePacketData is not
- * system API. On newer platforms use
- * addKeepalivePacketFilter(int, TcpKeepalivePacketData) instead.
- */
- @Deprecated
- public boolean addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt) {
- final long token = Binder.clearCallingIdentity();
- try {
- mIpClient.addKeepalivePacketFilter(slot, pkt);
- return true;
- } catch (RemoteException e) {
- log("Error adding Keepalive Packet Filter ", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Add a NAT-T keepalive packet filter before setting up keepalive offload.
- */
- public boolean addKeepalivePacketFilter(int slot, NattKeepalivePacketData pkt) {
- final long token = Binder.clearCallingIdentity();
- try {
- mIpClient.addNattKeepalivePacketFilter(
- slot, KeepalivePacketDataUtil.toStableParcelable(pkt));
- return true;
- } catch (RemoteException e) {
- log("Error adding NAT-T Keepalive Packet Filter ", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Remove a keepalive packet filter after stopping keepalive offload.
- */
- public boolean removeKeepalivePacketFilter(int slot) {
- final long token = Binder.clearCallingIdentity();
- try {
- mIpClient.removeKeepalivePacketFilter(slot);
- return true;
- } catch (RemoteException e) {
- log("Error removing Keepalive Packet Filter ", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Set the L2 key and group hint for storing info into the memory store.
- */
- public boolean setL2KeyAndGroupHint(String l2Key, String groupHint) {
- final long token = Binder.clearCallingIdentity();
- try {
- mIpClient.setL2KeyAndGroupHint(l2Key, groupHint);
- return true;
- } catch (RemoteException e) {
- log("Failed setL2KeyAndGroupHint", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Notify IpClient that preconnection is complete and that the link is ready for use.
- * The success parameter indicates whether the packets passed in by 'onPreconnectionStart'
- * were successfully sent to the network or not.
- */
- public boolean notifyPreconnectionComplete(boolean success) {
- final long token = Binder.clearCallingIdentity();
- try {
- mIpClient.notifyPreconnectionComplete(success);
- return true;
- } catch (RemoteException e) {
- log("Error notifying IpClient Preconnection completed", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- /**
- * Update the bssid, L2 key and group hint layer2 information.
- */
- public boolean updateLayer2Information(Layer2Information info) {
- final long token = Binder.clearCallingIdentity();
- try {
- mIpClient.updateLayer2Information(info.toStableParcelable());
- return true;
- } catch (RemoteException e) {
- log("Error updating layer2 information", e);
- return false;
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-}
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
deleted file mode 100644
index 426614e..0000000
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.ip;
-
-import android.content.Context;
-import android.net.DhcpResultsParcelable;
-import android.net.Layer2PacketParcelable;
-import android.net.LinkProperties;
-import android.net.networkstack.ModuleNetworkStackClient;
-import android.os.ConditionVariable;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.List;
-
-
-/**
- * Utilities and wrappers to simplify communication with IpClient, which lives in the NetworkStack
- * process.
- *
- * @hide
- */
-public class IpClientUtil {
- // TODO: remove with its callers
- public static final String DUMP_ARG = "ipclient";
-
- /**
- * Subclass of {@link IpClientCallbacks} allowing clients to block until provisioning is
- * complete with {@link WaitForProvisioningCallbacks#waitForProvisioning()}.
- */
- public static class WaitForProvisioningCallbacks extends IpClientCallbacks {
- private final ConditionVariable mCV = new ConditionVariable();
- private LinkProperties mCallbackLinkProperties;
-
- /**
- * Block until either {@link #onProvisioningSuccess(LinkProperties)} or
- * {@link #onProvisioningFailure(LinkProperties)} is called.
- */
- public LinkProperties waitForProvisioning() {
- mCV.block();
- return mCallbackLinkProperties;
- }
-
- @Override
- public void onProvisioningSuccess(LinkProperties newLp) {
- mCallbackLinkProperties = newLp;
- mCV.open();
- }
-
- @Override
- public void onProvisioningFailure(LinkProperties newLp) {
- mCallbackLinkProperties = null;
- mCV.open();
- }
- }
-
- /**
- * Create a new IpClient.
- *
- * <p>This is a convenience method to allow clients to use {@link IpClientCallbacks} instead of
- * {@link IIpClientCallbacks}.
- * @see {@link ModuleNetworkStackClient#makeIpClient(String, IIpClientCallbacks)}
- */
- public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) {
- ModuleNetworkStackClient.getInstance(context)
- .makeIpClient(ifName, new IpClientCallbacksProxy(callback));
- }
-
- /**
- * Wrapper to relay calls from {@link IIpClientCallbacks} to {@link IpClientCallbacks}.
- */
- private static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub {
- protected final IpClientCallbacks mCb;
-
- /**
- * Create a new IpClientCallbacksProxy.
- */
- public IpClientCallbacksProxy(IpClientCallbacks cb) {
- mCb = cb;
- }
-
- @Override
- public void onIpClientCreated(IIpClient ipClient) {
- mCb.onIpClientCreated(ipClient);
- }
-
- @Override
- public void onPreDhcpAction() {
- mCb.onPreDhcpAction();
- }
-
- @Override
- public void onPostDhcpAction() {
- mCb.onPostDhcpAction();
- }
-
- // This is purely advisory and not an indication of provisioning
- // success or failure. This is only here for callers that want to
- // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
- // DHCPv4 or static IPv4 configuration failure or success can be
- // determined by whether or not the passed-in DhcpResults object is
- // null or not.
- @Override
- public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) {
- mCb.onNewDhcpResults(dhcpResults);
- }
-
- @Override
- public void onProvisioningSuccess(LinkProperties newLp) {
- mCb.onProvisioningSuccess(newLp);
- }
- @Override
- public void onProvisioningFailure(LinkProperties newLp) {
- mCb.onProvisioningFailure(newLp);
- }
-
- // Invoked on LinkProperties changes.
- @Override
- public void onLinkPropertiesChange(LinkProperties newLp) {
- mCb.onLinkPropertiesChange(newLp);
- }
-
- // Called when the internal IpReachabilityMonitor (if enabled) has
- // detected the loss of a critical number of required neighbors.
- @Override
- public void onReachabilityLost(String logMsg) {
- mCb.onReachabilityLost(logMsg);
- }
-
- // Called when the IpClient state machine terminates.
- @Override
- public void onQuit() {
- mCb.onQuit();
- }
-
- // Install an APF program to filter incoming packets.
- @Override
- public void installPacketFilter(byte[] filter) {
- mCb.installPacketFilter(filter);
- }
-
- // Asynchronously read back the APF program & data buffer from the wifi driver.
- // Due to Wifi HAL limitations, the current implementation only supports dumping the entire
- // buffer. In response to this request, the driver returns the data buffer asynchronously
- // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
- @Override
- public void startReadPacketFilter() {
- mCb.startReadPacketFilter();
- }
-
- // If multicast filtering cannot be accomplished with APF, this function will be called to
- // actuate multicast filtering using another means.
- @Override
- public void setFallbackMulticastFilter(boolean enabled) {
- mCb.setFallbackMulticastFilter(enabled);
- }
-
- // Enabled/disable Neighbor Discover offload functionality. This is
- // called, for example, whenever 464xlat is being started or stopped.
- @Override
- public void setNeighborDiscoveryOffload(boolean enable) {
- mCb.setNeighborDiscoveryOffload(enable);
- }
-
- // Invoked on starting preconnection process.
- @Override
- public void onPreconnectionStart(List<Layer2PacketParcelable> packets) {
- mCb.onPreconnectionStart(packets);
- }
-
- @Override
- public int getInterfaceVersion() {
- return this.VERSION;
- }
-
- @Override
- public String getInterfaceHash() {
- return this.HASH;
- }
- }
-
- /**
- * Dump logs for the specified IpClient.
- * TODO: remove callers and delete
- */
- public static void dumpIpClient(
- IIpClient connector, FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("IpClient logs have moved to dumpsys network_stack");
- }
-}
diff --git a/services/net/java/android/net/util/DhcpResultsCompatUtil.java b/services/net/java/android/net/util/DhcpResultsCompatUtil.java
deleted file mode 100644
index fce0834..0000000
--- a/services/net/java/android/net/util/DhcpResultsCompatUtil.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import static android.net.shared.IpConfigurationParcelableUtil.unparcelAddress;
-
-import android.annotation.Nullable;
-import android.net.DhcpResults;
-import android.net.DhcpResultsParcelable;
-
-import java.net.Inet4Address;
-
-/**
- * Compatibility utility for code that still uses DhcpResults.
- *
- * TODO: remove this class when all usages of DhcpResults (including Wifi in AOSP) are removed.
- */
-public class DhcpResultsCompatUtil {
-
- /**
- * Convert a DhcpResultsParcelable to DhcpResults.
- *
- * contract {
- * returns(null) implies p == null
- * returnsNotNull() implies p != null
- * }
- */
- @Nullable
- public static DhcpResults fromStableParcelable(@Nullable DhcpResultsParcelable p) {
- if (p == null) return null;
- final DhcpResults results = new DhcpResults(p.baseConfiguration);
- results.leaseDuration = p.leaseDuration;
- results.mtu = p.mtu;
- results.serverAddress = (Inet4Address) unparcelAddress(p.serverAddress);
- results.vendorInfo = p.vendorInfo;
- results.serverHostName = p.serverHostName;
- results.captivePortalApiUrl = p.captivePortalApiUrl;
- return results;
- }
-}
diff --git a/services/net/java/android/net/util/KeepalivePacketDataUtil.java b/services/net/java/android/net/util/KeepalivePacketDataUtil.java
deleted file mode 100644
index 5666985..0000000
--- a/services/net/java/android/net/util/KeepalivePacketDataUtil.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.util;
-
-import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.InvalidPacketException;
-import android.net.KeepalivePacketData;
-import android.net.NattKeepalivePacketData;
-import android.net.NattKeepalivePacketDataParcelable;
-import android.net.TcpKeepalivePacketData;
-import android.net.TcpKeepalivePacketDataParcelable;
-import android.os.Build;
-import android.system.OsConstants;
-import android.util.Log;
-
-import com.android.net.module.util.IpUtils;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-/**
- * Utility class to convert to/from keepalive data parcelables.
- *
- * TODO: move to networkstack-client library when it is moved to frameworks/libs/net.
- * This class cannot go into other shared libraries as it depends on NetworkStack AIDLs.
- * @hide
- */
-public final class KeepalivePacketDataUtil {
- private static final int IPV4_HEADER_LENGTH = 20;
- private static final int IPV6_HEADER_LENGTH = 40;
- private static final int TCP_HEADER_LENGTH = 20;
-
- private static final String TAG = KeepalivePacketDataUtil.class.getSimpleName();
-
- /**
- * Convert a NattKeepalivePacketData to a NattKeepalivePacketDataParcelable.
- */
- @NonNull
- public static NattKeepalivePacketDataParcelable toStableParcelable(
- @NonNull NattKeepalivePacketData pkt) {
- final NattKeepalivePacketDataParcelable parcel = new NattKeepalivePacketDataParcelable();
- final InetAddress srcAddress = pkt.getSrcAddress();
- final InetAddress dstAddress = pkt.getDstAddress();
- parcel.srcAddress = srcAddress.getAddress();
- parcel.srcPort = pkt.getSrcPort();
- parcel.dstAddress = dstAddress.getAddress();
- parcel.dstPort = pkt.getDstPort();
- return parcel;
- }
-
- /**
- * Convert a TcpKeepalivePacketData to a TcpKeepalivePacketDataParcelable.
- */
- @NonNull
- public static TcpKeepalivePacketDataParcelable toStableParcelable(
- @NonNull TcpKeepalivePacketData pkt) {
- final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable();
- final InetAddress srcAddress = pkt.getSrcAddress();
- final InetAddress dstAddress = pkt.getDstAddress();
- parcel.srcAddress = srcAddress.getAddress();
- parcel.srcPort = pkt.getSrcPort();
- parcel.dstAddress = dstAddress.getAddress();
- parcel.dstPort = pkt.getDstPort();
- parcel.seq = pkt.getTcpSeq();
- parcel.ack = pkt.getTcpAck();
- parcel.rcvWnd = pkt.getTcpWindow();
- parcel.rcvWndScale = pkt.getTcpWindowScale();
- parcel.tos = pkt.getIpTos();
- parcel.ttl = pkt.getIpTtl();
- return parcel;
- }
-
- /**
- * Factory method to create tcp keepalive packet structure.
- * @hide
- */
- public static TcpKeepalivePacketData fromStableParcelable(
- TcpKeepalivePacketDataParcelable tcpDetails) throws InvalidPacketException {
- final byte[] packet;
- try {
- if ((tcpDetails.srcAddress != null) && (tcpDetails.dstAddress != null)
- && (tcpDetails.srcAddress.length == 4 /* V4 IP length */)
- && (tcpDetails.dstAddress.length == 4 /* V4 IP length */)) {
- packet = buildV4Packet(tcpDetails);
- } else {
- // TODO: support ipv6
- throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
- }
- return new TcpKeepalivePacketData(
- InetAddress.getByAddress(tcpDetails.srcAddress),
- tcpDetails.srcPort,
- InetAddress.getByAddress(tcpDetails.dstAddress),
- tcpDetails.dstPort,
- packet,
- tcpDetails.seq, tcpDetails.ack, tcpDetails.rcvWnd, tcpDetails.rcvWndScale,
- tcpDetails.tos, tcpDetails.ttl);
- } catch (UnknownHostException e) {
- throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
- }
-
- }
-
- /**
- * Build ipv4 tcp keepalive packet, not including the link-layer header.
- */
- // TODO : if this code is ever moved to the network stack, factorize constants with the ones
- // over there.
- private static byte[] buildV4Packet(TcpKeepalivePacketDataParcelable tcpDetails) {
- final int length = IPV4_HEADER_LENGTH + TCP_HEADER_LENGTH;
- ByteBuffer buf = ByteBuffer.allocate(length);
- buf.order(ByteOrder.BIG_ENDIAN);
- buf.put((byte) 0x45); // IP version and IHL
- buf.put((byte) tcpDetails.tos); // TOS
- buf.putShort((short) length);
- buf.putInt(0x00004000); // ID, flags=DF, offset
- buf.put((byte) tcpDetails.ttl); // TTL
- buf.put((byte) OsConstants.IPPROTO_TCP);
- final int ipChecksumOffset = buf.position();
- buf.putShort((short) 0); // IP checksum
- buf.put(tcpDetails.srcAddress);
- buf.put(tcpDetails.dstAddress);
- buf.putShort((short) tcpDetails.srcPort);
- buf.putShort((short) tcpDetails.dstPort);
- buf.putInt(tcpDetails.seq); // Sequence Number
- buf.putInt(tcpDetails.ack); // ACK
- buf.putShort((short) 0x5010); // TCP length=5, flags=ACK
- buf.putShort((short) (tcpDetails.rcvWnd >> tcpDetails.rcvWndScale)); // Window size
- final int tcpChecksumOffset = buf.position();
- buf.putShort((short) 0); // TCP checksum
- // URG is not set therefore the urgent pointer is zero.
- buf.putShort((short) 0); // Urgent pointer
-
- buf.putShort(ipChecksumOffset, com.android.net.module.util.IpUtils.ipChecksum(buf, 0));
- buf.putShort(tcpChecksumOffset, IpUtils.tcpChecksum(
- buf, 0, IPV4_HEADER_LENGTH, TCP_HEADER_LENGTH));
-
- return buf.array();
- }
-
- // TODO: add buildV6Packet.
-
- /**
- * Get a {@link TcpKeepalivePacketDataParcelable} from {@link KeepalivePacketData}, if the
- * generic class actually contains TCP keepalive data.
- *
- * @deprecated This method is used on R platforms where android.net.TcpKeepalivePacketData was
- * not yet system API. Newer platforms should use android.net.TcpKeepalivePacketData directly.
- *
- * @param data A {@link KeepalivePacketData} that may contain TCP keepalive data.
- * @return A parcelable containing TCP keepalive data, or null if the input data does not
- * contain TCP keepalive data.
- */
- @Deprecated
- @SuppressWarnings("AndroidFrameworkCompatChange") // API version check used to Log.wtf
- @Nullable
- public static TcpKeepalivePacketDataParcelable parseTcpKeepalivePacketData(
- @Nullable KeepalivePacketData data) {
- if (data == null) return null;
-
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
- Log.wtf(TAG, "parseTcpKeepalivePacketData should not be used after R, use "
- + "TcpKeepalivePacketData instead.");
- }
-
- // Reconstruct TcpKeepalivePacketData from the packet contained in KeepalivePacketData
- final ByteBuffer buffer = ByteBuffer.wrap(data.getPacket());
- buffer.order(ByteOrder.BIG_ENDIAN);
-
- // Most of the fields are accessible from the KeepalivePacketData superclass: instead of
- // using Struct to parse everything, just extract the extra fields necessary for
- // TcpKeepalivePacketData.
- final int tcpSeq;
- final int tcpAck;
- final int wndSize;
- final int ipTos;
- final int ttl;
- try {
- // This only support IPv4, because TcpKeepalivePacketData only supports IPv4 for R and
- // below, and this method should not be used on newer platforms.
- tcpSeq = buffer.getInt(IPV4_HEADER_LENGTH + 4);
- tcpAck = buffer.getInt(IPV4_HEADER_LENGTH + 8);
- wndSize = buffer.getShort(IPV4_HEADER_LENGTH + 14);
- ipTos = buffer.get(1);
- ttl = buffer.get(8);
- } catch (IndexOutOfBoundsException e) {
- return null;
- }
-
- final TcpKeepalivePacketDataParcelable p = new TcpKeepalivePacketDataParcelable();
- p.srcAddress = data.getSrcAddress().getAddress();
- p.srcPort = data.getSrcPort();
- p.dstAddress = data.getDstAddress().getAddress();
- p.dstPort = data.getDstPort();
- p.seq = tcpSeq;
- p.ack = tcpAck;
- // TcpKeepalivePacketData could actually use non-zero wndScale, but this does not affect
- // actual functionality as generated packets will be the same (no wndScale option added)
- p.rcvWnd = wndSize;
- p.rcvWndScale = 0;
- p.tos = ipTos;
- p.ttl = ttl;
- return p;
- }
-}
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 9706d7f..e3e2708 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -37,6 +37,7 @@
import android.provider.DeviceConfig;
import android.util.Log;
+import com.android.internal.R;
import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -302,8 +303,15 @@
return;
}
+ if (!getUploaderEnabledConfig(getContext())) {
+ return;
+ }
+
new Thread(() -> {
try {
+ Context context = getContext();
+ final String uploaderPkg = getUploaderPackageName(context);
+ final String uploaderAction = getUploaderActionName(context);
String reportUuid = mIProfcollect.report();
final int profileId = getBBProfileId();
@@ -317,13 +325,12 @@
}
Intent uploadIntent =
- new Intent("com.google.android.apps.betterbug.intent.action.UPLOAD_PROFILE")
- .setPackage("com.google.android.apps.internal.betterbug")
+ new Intent(uploaderAction)
+ .setPackage(uploaderPkg)
.putExtra("EXTRA_DESTINATION", "PROFCOLLECT")
.putExtra("EXTRA_PACKAGE_NAME", getContext().getPackageName())
.putExtra("EXTRA_PROFILE_PATH", reportPath)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- Context context = getContext();
List<ResolveInfo> receivers =
context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0);
@@ -356,4 +363,19 @@
}
return UserHandle.USER_SYSTEM;
}
+
+ private boolean getUploaderEnabledConfig(Context context) {
+ return context.getResources().getBoolean(
+ R.bool.config_profcollectReportUploaderEnabled);
+ }
+
+ private String getUploaderPackageName(Context context) {
+ return context.getResources().getString(
+ R.string.config_defaultProfcollectReportUploaderApp);
+ }
+
+ private String getUploaderActionName(Context context) {
+ return context.getResources().getString(
+ R.string.config_defaultProfcollectReportUploaderAction);
+ }
}
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
new file mode 100644
index 0000000..479ef8e
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "AppEnumerationInternalTests",
+ srcs: [
+ "src/**/*.java",
+ ],
+ static_libs: [
+ "compatibility-device-util-axt",
+ "androidx.test.runner",
+ "truth-prebuilt",
+ ],
+ platform_apis: true,
+ test_suites: ["device-tests"],
+}
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/AndroidManifest.xml b/services/tests/PackageManagerServiceTests/appenumeration/AndroidManifest.xml
new file mode 100644
index 0000000..2039aaa
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/appenumeration/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.pm.test.appenumeration">
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.pm.test.appenumeration"
+ android:label="Package Manager Service Tests for app enumeration">
+ </instrumentation>
+
+</manifest>
+
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml b/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
new file mode 100644
index 0000000..6f168a3
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml
@@ -0,0 +1,43 @@
+<?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.
+ -->
+
+<configuration description="Runs Package Manager Service App Enumeration Tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="AppEnumerationInternalTests.apk" />
+ </target_preparer>
+
+ <!-- Create place to store apks -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="mkdir -p /data/local/tmp/appenumerationtests" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/appenumerationtests"/>
+ </target_preparer>
+
+ <!-- Load additional APKs onto device -->
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="push" value="AppEnumerationSyncProviderTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationSyncProviderTestApp.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="AppEnumerationInternalTest" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.server.pm.test.appenumeration" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false" />
+ </test>
+</configuration>
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java
new file mode 100644
index 0000000..9337845
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test.appenumeration;
+
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.AppGlobals;
+import android.content.pm.IPackageManager;
+import android.content.pm.ProviderInfo;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Application enumeration tests for the internal apis of package manager service.
+ */
+@RunWith(AndroidJUnit4.class)
+public class AppEnumerationInternalTests {
+ private static final String TEST_DATA_PATH = "/data/local/tmp/appenumerationtests/";
+ private static final String SYNC_PROVIDER_APK_PATH =
+ TEST_DATA_PATH + "AppEnumerationSyncProviderTestApp.apk";
+ private static final String SYNC_PROVIDER_PKG_NAME = "com.android.appenumeration.syncprovider";
+ private static final String SYNC_PROVIDER_AUTHORITY = SYNC_PROVIDER_PKG_NAME;
+
+ private IPackageManager mIPackageManager;
+
+ @Before
+ public void setup() {
+ mIPackageManager = AppGlobals.getPackageManager();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ uninstallPackage(SYNC_PROVIDER_PKG_NAME);
+ }
+
+ @Test
+ public void querySyncProviders_canSeeForceQueryable() throws Exception {
+ final List<String> names = new ArrayList<>();
+ final List<ProviderInfo> infos = new ArrayList<>();
+ installPackage(SYNC_PROVIDER_APK_PATH, true /* forceQueryable */);
+ mIPackageManager.querySyncProviders(names, infos);
+
+ assertThat(names).contains(SYNC_PROVIDER_AUTHORITY);
+ assertThat(infos.stream().map(info -> info.packageName).collect(Collectors.toList()))
+ .contains(SYNC_PROVIDER_PKG_NAME);
+ }
+
+ @Test
+ public void querySyncProviders_cannotSeeSyncProvider() throws Exception {
+ final List<String> names = new ArrayList<>();
+ final List<ProviderInfo> infos = new ArrayList<>();
+ installPackage(SYNC_PROVIDER_APK_PATH, false /* forceQueryable */);
+ mIPackageManager.querySyncProviders(names, infos);
+
+ assertThat(names).doesNotContain(SYNC_PROVIDER_AUTHORITY);
+ assertThat(infos.stream().map(info -> info.packageName).collect(Collectors.toList()))
+ .doesNotContain(SYNC_PROVIDER_PKG_NAME);
+ }
+
+ private static void installPackage(String apkPath, boolean forceQueryable) {
+ final StringBuilder cmd = new StringBuilder("pm install ");
+ if (forceQueryable) {
+ cmd.append("--force-queryable ");
+ }
+ cmd.append(apkPath);
+ final String result = runShellCommand(cmd.toString());
+ assertThat(result.trim()).contains("Success");
+ }
+
+ private static void uninstallPackage(String packageName) {
+ runShellCommand("pm uninstall " + packageName);
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp
new file mode 100644
index 0000000..64239b4
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "AppEnumerationSyncProviderTestApp",
+ srcs: ["src/**/*.java"],
+ manifest: "AndroidManifest-syncprovider.xml",
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+ test_suites: ["device-tests"],
+ platform_apis: true,
+}
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-syncprovider.xml b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-syncprovider.xml
new file mode 100644
index 0000000..de843938
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-syncprovider.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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.appenumeration.syncprovider">
+ <application>
+ <provider android:name="com.android.appenumeration.testapp.DummyProvider"
+ android:authorities="com.android.appenumeration.syncprovider"
+ android:syncable="true" android:exported="true">
+ <intent-filter>
+ <action android:name="com.android.appenumeration.action.PROVIDER"/>
+ </intent-filter>
+ </provider>
+ </application>
+</manifest>
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/src/com/android/appenumeration/testapp/DummyProvider.java b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/src/com/android/appenumeration/testapp/DummyProvider.java
new file mode 100644
index 0000000..e8b6109
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/src/com/android/appenumeration/testapp/DummyProvider.java
@@ -0,0 +1,55 @@
+/*
+ * 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.appenumeration.testapp;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+public class DummyProvider extends ContentProvider {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return "text/plain";
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index e99113d..acf50b45 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -323,6 +323,8 @@
when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock);
doNothing().when(mWakeLock).acquire();
doNothing().when(mAlarmManager).set(anyInt(), anyLong(), anyString(), any(), any());
+ doNothing().when(mAlarmManager)
+ .setWindow(anyInt(), anyLong(), anyLong(), anyString(), any(), any());
doReturn(mock(Sensor.class)).when(mSensorManager)
.getDefaultSensor(eq(Sensor.TYPE_SIGNIFICANT_MOTION), eq(true));
doReturn(true).when(mSensorManager).registerListener(any(), any(), anyInt());
@@ -1043,24 +1045,28 @@
mDeviceIdleController.stepLightIdleStateLocked("testing");
verifyLightStateConditions(LIGHT_STATE_IDLE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
- longThat(l -> l == mConstants.LIGHT_IDLE_TIMEOUT));
+ longThat(l -> l == mConstants.LIGHT_IDLE_TIMEOUT),
+ longThat(l -> l == mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX));
// Should just alternate between IDLE and IDLE_MAINTENANCE now.
mDeviceIdleController.stepLightIdleStateLocked("testing");
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
- longThat(l -> l >= mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET));
+ longThat(l -> l >= mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET),
+ longThat(l -> l == mConstants.FLEX_TIME_SHORT));
mDeviceIdleController.stepLightIdleStateLocked("testing");
verifyLightStateConditions(LIGHT_STATE_IDLE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
- longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT));
+ longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT),
+ longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX));
mDeviceIdleController.stepLightIdleStateLocked("testing");
verifyLightStateConditions(LIGHT_STATE_IDLE_MAINTENANCE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
- longThat(l -> l >= mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET));
+ longThat(l -> l >= mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET),
+ longThat(l -> l == mConstants.FLEX_TIME_SHORT));
// Test that motion doesn't reset the idle timeout.
mDeviceIdleController.handleMotionDetectedLocked(50, "test");
@@ -1068,7 +1074,8 @@
mDeviceIdleController.stepLightIdleStateLocked("testing");
verifyLightStateConditions(LIGHT_STATE_IDLE);
inOrder.verify(mDeviceIdleController).scheduleLightAlarmLocked(
- longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT));
+ longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT),
+ longThat(l -> l > mConstants.LIGHT_IDLE_TIMEOUT_INITIAL_FLEX));
}
///////////////// EXIT conditions ///////////////////
@@ -1824,9 +1831,9 @@
.forClass(AlarmManager.OnAlarmListener.class);
final ArgumentCaptor<AlarmManager.OnAlarmListener> motionRegistrationAlarmListener =
ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
- doNothing().when(mAlarmManager).set(anyInt(), anyLong(), eq("DeviceIdleController.motion"),
- motionAlarmListener.capture(), any());
- doNothing().when(mAlarmManager).set(anyInt(), anyLong(),
+ doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
+ eq("DeviceIdleController.motion"), motionAlarmListener.capture(), any());
+ doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
eq("DeviceIdleController.motion_registration"),
motionRegistrationAlarmListener.capture(), any());
@@ -1900,9 +1907,9 @@
mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT;
final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor
.forClass(AlarmManager.OnAlarmListener.class);
- doNothing().when(mAlarmManager)
- .set(anyInt(), anyLong(), eq("DeviceIdleController.motion"), any(), any());
- doNothing().when(mAlarmManager).set(anyInt(), anyLong(),
+ doNothing().when(mAlarmManager).setWindow(
+ anyInt(), anyLong(), anyLong(), eq("DeviceIdleController.motion"), any(), any());
+ doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
eq("DeviceIdleController.motion_registration"),
alarmListener.capture(), any());
ArgumentCaptor<TriggerEventListener> listenerCaptor =
@@ -1944,9 +1951,9 @@
mInjector.nowElapsed += mConstants.QUICK_DOZE_DELAY_TIMEOUT;
final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListener = ArgumentCaptor
.forClass(AlarmManager.OnAlarmListener.class);
- doNothing().when(mAlarmManager)
- .set(anyInt(), anyLong(), eq("DeviceIdleController.motion"), any(), any());
- doNothing().when(mAlarmManager).set(anyInt(), anyLong(),
+ doNothing().when(mAlarmManager).setWindow(
+ anyInt(), anyLong(), anyLong(), eq("DeviceIdleController.motion"), any(), any());
+ doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
eq("DeviceIdleController.motion_registration"),
alarmListener.capture(), any());
ArgumentCaptor<SensorEventListener> listenerCaptor =
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 21de791..eab1afb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -1914,11 +1914,11 @@
public void hasScheduleExactAlarmBinderCallChangeDisabled() throws RemoteException {
mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false);
- mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT);
- assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+ mockExactAlarmPermissionGrant(false, true, MODE_DEFAULT);
+ assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
- mockExactAlarmPermissionGrant(true, true, MODE_ALLOWED);
- assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
+ mockExactAlarmPermissionGrant(true, false, MODE_ERRORED);
+ assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER));
}
private void mockChangeEnabled(long changeId, boolean enabled) {
@@ -2309,12 +2309,14 @@
public void minWindowChangeDisabled() {
mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, false);
final long minWindow = 73;
+ final long futurity = 10_000;
setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
// 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC.
for (int window = 1; window <= minWindow; window++) {
final PendingIntent pi = getNewMockPendingIntent();
- setTestAlarm(ELAPSED_REALTIME, 0, window, pi, 0, 0, TEST_CALLING_UID, null);
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + futurity, window, pi, 0, 0,
+ TEST_CALLING_UID, null);
assertEquals(1, mService.mAlarmStore.size());
final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
@@ -2323,18 +2325,61 @@
}
@Test
+ public void minWindowExempted() {
+ mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, true);
+ final long minWindow = 73;
+ final long futurity = 10_000;
+
+ setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
+
+ final int coreUid = 2312;
+ doReturn(true).when(() -> UserHandle.isCore(coreUid));
+
+ final int allowlisted = 54239;
+ when(mDeviceIdleInternal.isAppOnWhitelist(UserHandle.getAppId(allowlisted))).thenReturn(
+ true);
+
+ for (final int callingUid : new int[]{SYSTEM_UI_UID, coreUid, coreUid}) {
+ // 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC.
+ for (int window = 1; window <= minWindow; window++) {
+ final PendingIntent pi = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + futurity, window, pi, 0, 0,
+ callingUid, null);
+
+ assertEquals(1, mService.mAlarmStore.size());
+ final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
+ assertEquals(window, a.windowLength);
+ }
+ }
+
+ // 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC.
+ for (int window = 1; window <= minWindow; window++) {
+ final PendingIntent pi = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + futurity, window, pi, 0, 0,
+ TEST_CALLING_UID, null);
+
+ assertEquals(1, mService.mAlarmStore.size());
+ final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
+ assertEquals(minWindow, a.windowLength);
+ }
+ }
+
+ @Test
public void minWindowPriorityAlarm() {
mockChangeEnabled(AlarmManager.ENFORCE_MINIMUM_WINDOW_ON_INEXACT_ALARMS, true);
final long minWindow = 73;
+ final long futurity = 10_000;
setDeviceConfigLong(KEY_MIN_WINDOW, minWindow);
// 0 is WINDOW_EXACT and < 0 is WINDOW_HEURISTIC.
for (int window = 1; window <= minWindow; window++) {
- setPrioritizedAlarm(ELAPSED_REALTIME, 0, window, new IAlarmListener.Stub() {
- @Override
- public void doAlarm(IAlarmCompleteListener callback) throws RemoteException {
- }
- });
+ setPrioritizedAlarm(ELAPSED_REALTIME, mNowElapsedTest + futurity, window,
+ new IAlarmListener.Stub() {
+ @Override
+ public void doAlarm(IAlarmCompleteListener callback)
+ throws RemoteException {
+ }
+ });
assertEquals(1, mService.mAlarmStore.size());
final Alarm a = mService.mAlarmStore.remove(unused -> true).get(0);
assertEquals(window, a.windowLength);
diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java b/services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
index da0b83e..28fcaee 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
@@ -21,7 +21,6 @@
import static com.google.common.truth.Truth.assertThat;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.provider.DeviceConfig;
import androidx.test.core.app.ApplicationProvider;
@@ -63,7 +62,6 @@
public void testCreateExtraStatsLocked_samplingIntervalNotSet_returnsDefault() {
PlatformLogger logger = new PlatformLogger(
ApplicationProvider.getApplicationContext(),
- UserHandle.of(UserHandle.USER_NULL),
mAppSearchConfig);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
@@ -96,8 +94,7 @@
int putDocumentSamplingInterval = 1;
int batchCallSamplingInterval = 2;
PlatformLogger logger = new PlatformLogger(
- ApplicationProvider.getApplicationContext(),
- UserHandle.of(UserHandle.USER_NULL), mAppSearchConfig);
+ ApplicationProvider.getApplicationContext(), mAppSearchConfig);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
@@ -143,7 +140,6 @@
final String testPackageName = "packageName";
PlatformLogger logger = new PlatformLogger(
ApplicationProvider.getApplicationContext(),
- UserHandle.of(UserHandle.USER_NULL),
mAppSearchConfig);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
@@ -162,7 +158,6 @@
final String testPackageName = "packageName";
PlatformLogger logger = new PlatformLogger(
ApplicationProvider.getApplicationContext(),
- UserHandle.of(UserHandle.USER_NULL),
mAppSearchConfig);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
@@ -186,7 +181,6 @@
final String testPackageName = "packageName";
PlatformLogger logger = new PlatformLogger(
ApplicationProvider.getApplicationContext(),
- UserHandle.of(UserHandle.USER_NULL),
mAppSearchConfig);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
@@ -214,7 +208,6 @@
final String testPackageName = "packageName";
PlatformLogger logger = new PlatformLogger(
ApplicationProvider.getApplicationContext(),
- UserHandle.of(UserHandle.USER_NULL),
mAppSearchConfig);
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index e1012a9..0efcc57 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -35,6 +35,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -589,8 +590,8 @@
new DisplayModeDirector.DesiredDisplayModeSpecs(
/*baseModeId*/ baseModeId,
/*allowGroupSwitching*/ false,
- new DisplayModeDirector.RefreshRateRange(60f, 60f),
- new DisplayModeDirector.RefreshRateRange(60f, 60f)
+ new RefreshRateRange(60f, 60f),
+ new RefreshRateRange(60f, 60f)
));
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
verify(mSurfaceControlProxy).setDesiredDisplayModeSpecs(display.token,
@@ -624,8 +625,8 @@
new DisplayModeDirector.DesiredDisplayModeSpecs(
/*baseModeId*/ baseModeId,
/*allowGroupSwitching*/ false,
- new DisplayModeDirector.RefreshRateRange(60f, 60f),
- new DisplayModeDirector.RefreshRateRange(60f, 60f)
+ new RefreshRateRange(60f, 60f),
+ new RefreshRateRange(60f, 60f)
));
waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index f2bb47b..b2471fa 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -30,6 +30,7 @@
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -54,6 +55,9 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
import com.android.server.AppStateTracker;
import com.android.server.AppStateTrackerImpl;
@@ -75,8 +79,11 @@
import java.time.Clock;
import java.time.Duration;
import java.time.ZoneOffset;
+import java.util.Random;
public class JobSchedulerServiceTest {
+ private static final String TAG = JobSchedulerServiceTest.class.getSimpleName();
+
private JobSchedulerService mService;
private MockitoSession mMockingSession;
@@ -178,12 +185,21 @@
}
private static JobInfo.Builder createJobInfo() {
- return new JobInfo.Builder(351, new ComponentName("foo", "bar"));
+ return createJobInfo(351);
+ }
+
+ private static JobInfo.Builder createJobInfo(int jobId) {
+ return new JobInfo.Builder(jobId, new ComponentName("foo", "bar"));
}
private JobStatus createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder) {
+ return createJobStatus(testTag, jobInfoBuilder, 1234);
+ }
+
+ private JobStatus createJobStatus(String testTag, JobInfo.Builder jobInfoBuilder,
+ int callingUid) {
return JobStatus.createFromJobInfo(
- jobInfoBuilder.build(), 1234, "com.android.test", 0, testTag);
+ jobInfoBuilder.build(), callingUid, "com.android.test", 0, testTag);
}
/**
@@ -716,7 +732,7 @@
assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size());
assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed());
}
- maybeQueueFunctor.postProcess();
+ maybeQueueFunctor.postProcessLocked();
assertEquals(0, mService.mPendingJobs.size());
// Enough RARE jobs to run.
@@ -728,7 +744,7 @@
assertEquals(i + 1, maybeQueueFunctor.runnableJobs.size());
assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed());
}
- maybeQueueFunctor.postProcess();
+ maybeQueueFunctor.postProcessLocked();
assertEquals(5, mService.mPendingJobs.size());
// Not enough RARE jobs to run, but a non-batched job saves the day.
@@ -745,7 +761,7 @@
assertEquals(sElapsedRealtimeClock.millis(), job.getFirstForceBatchedTimeElapsed());
}
maybeQueueFunctor.accept(activeJob);
- maybeQueueFunctor.postProcess();
+ maybeQueueFunctor.postProcessLocked();
assertEquals(3, mService.mPendingJobs.size());
// Not enough RARE jobs to run, but an old RARE job saves the day.
@@ -764,7 +780,7 @@
}
maybeQueueFunctor.accept(oldRareJob);
assertEquals(oldBatchTime, oldRareJob.getFirstForceBatchedTimeElapsed());
- maybeQueueFunctor.postProcess();
+ maybeQueueFunctor.postProcessLocked();
assertEquals(3, mService.mPendingJobs.size());
}
@@ -853,4 +869,207 @@
0, ""));
}
}
+
+ @Test
+ public void testPendingJobSorting() {
+ // First letter in job variable name indicate regular (r) or expedited (e).
+ // Capital letters in job variable name indicate the app/UID.
+ // Numbers in job variable name indicate the enqueue time.
+ // Expected sort order:
+ // eA7 > rA1 > eB6 > rB2 > eC3 > rD4 > eE5 > eF9 > rF8 > eC11 > rC10 > rG12 > rG13 > eE14
+ // Intentions:
+ // * A jobs let us test skipping both regular and expedited jobs of other apps
+ // * B jobs let us test skipping only regular job of another app without going too far
+ // * C jobs test that regular jobs don't skip over other app's jobs and that EJs only
+ // skip up to level of the earliest regular job
+ // * E jobs test that expedited jobs don't skip the line when the app has no regular jobs
+ // * F jobs test correct expedited/regular ordering doesn't push jobs too high in list
+ // * G jobs test correct ordering for regular jobs
+ JobStatus rA1 = createJobStatus("testPendingJobSorting", createJobInfo(1), 1);
+ JobStatus rB2 = createJobStatus("testPendingJobSorting", createJobInfo(2), 2);
+ JobStatus eC3 = createJobStatus("testPendingJobSorting",
+ createJobInfo(3).setExpedited(true), 3);
+ JobStatus rD4 = createJobStatus("testPendingJobSorting", createJobInfo(4), 4);
+ JobStatus eE5 = createJobStatus("testPendingJobSorting",
+ createJobInfo(5).setExpedited(true), 5);
+ JobStatus eB6 = createJobStatus("testPendingJobSorting",
+ createJobInfo(6).setExpedited(true), 2);
+ JobStatus eA7 = createJobStatus("testPendingJobSorting",
+ createJobInfo(7).setExpedited(true), 1);
+ JobStatus rF8 = createJobStatus("testPendingJobSorting", createJobInfo(8), 6);
+ JobStatus eF9 = createJobStatus("testPendingJobSorting",
+ createJobInfo(9).setExpedited(true), 6);
+ JobStatus rC10 = createJobStatus("testPendingJobSorting", createJobInfo(10), 3);
+ JobStatus eC11 = createJobStatus("testPendingJobSorting",
+ createJobInfo(11).setExpedited(true), 3);
+ JobStatus rG12 = createJobStatus("testPendingJobSorting", createJobInfo(12), 7);
+ JobStatus rG13 = createJobStatus("testPendingJobSorting", createJobInfo(13), 7);
+ JobStatus eE14 = createJobStatus("testPendingJobSorting",
+ createJobInfo(14).setExpedited(true), 5);
+
+ rA1.enqueueTime = 1;
+ rB2.enqueueTime = 2;
+ eC3.enqueueTime = 3;
+ rD4.enqueueTime = 4;
+ eE5.enqueueTime = 5;
+ eB6.enqueueTime = 6;
+ eA7.enqueueTime = 7;
+ rF8.enqueueTime = 8;
+ eF9.enqueueTime = 9;
+ rC10.enqueueTime = 10;
+ eC11.enqueueTime = 11;
+ rG12.enqueueTime = 12;
+ rG13.enqueueTime = 13;
+ eE14.enqueueTime = 14;
+
+ mService.mPendingJobs.clear();
+ // Add in random order so sorting is apparent.
+ mService.mPendingJobs.add(eC3);
+ mService.mPendingJobs.add(eE5);
+ mService.mPendingJobs.add(rA1);
+ mService.mPendingJobs.add(rG13);
+ mService.mPendingJobs.add(rD4);
+ mService.mPendingJobs.add(eA7);
+ mService.mPendingJobs.add(rG12);
+ mService.mPendingJobs.add(rF8);
+ mService.mPendingJobs.add(eB6);
+ mService.mPendingJobs.add(eE14);
+ mService.mPendingJobs.add(eF9);
+ mService.mPendingJobs.add(rB2);
+ mService.mPendingJobs.add(rC10);
+ mService.mPendingJobs.add(eC11);
+
+ mService.mPendingJobComparator.refreshLocked();
+ mService.mPendingJobs.sort(mService.mPendingJobComparator);
+
+ final JobStatus[] expectedOrder = new JobStatus[]{
+ eA7, rA1, eB6, rB2, eC3, rD4, eE5, eF9, rF8, eC11, rC10, rG12, rG13, eE14};
+ for (int i = 0; i < expectedOrder.length; ++i) {
+ assertEquals("List wasn't correctly sorted @ index " + i,
+ expectedOrder[i].getJobId(), mService.mPendingJobs.get(i).getJobId());
+ }
+ }
+
+ private void checkPendingJobInvariants() {
+ long regJobEnqueueTime = 0;
+ final SparseBooleanArray regJobSeen = new SparseBooleanArray();
+ final SparseLongArray ejEnqueueTimes = new SparseLongArray();
+
+ for (int i = 0; i < mService.mPendingJobs.size(); ++i) {
+ final JobStatus job = mService.mPendingJobs.get(i);
+ final int uid = job.getSourceUid();
+
+ if (!job.isRequestedExpeditedJob()) {
+ // Invariant #1: Regular jobs are sorted by enqueue time.
+ assertTrue("Regular job with earlier enqueue time sorted after a later time: "
+ + regJobEnqueueTime + " vs " + job.enqueueTime,
+ regJobEnqueueTime <= job.enqueueTime);
+ regJobEnqueueTime = job.enqueueTime;
+ regJobSeen.put(uid, true);
+ } else {
+ // Invariant #2: EJs should be before regular jobs for an individual app
+ if (regJobSeen.get(uid)) {
+ fail("UID " + uid + " had an EJ ordered after a regular job");
+ }
+ final long ejEnqueueTime = ejEnqueueTimes.get(uid, 0);
+ // Invariant #3: EJs for an individual app should be sorted by enqueue time.
+ assertTrue("EJ with earlier enqueue time sorted after a later time: "
+ + ejEnqueueTime + " vs " + job.enqueueTime,
+ ejEnqueueTime <= job.enqueueTime);
+ ejEnqueueTimes.put(uid, job.enqueueTime);
+ }
+ }
+ }
+
+ private static String sortedJobToString(JobStatus job) {
+ return "testJob " + job.getSourceUid() + "/" + job.getJobId() + "/"
+ + job.isRequestedExpeditedJob() + "@" + job.enqueueTime;
+ }
+
+ @Test
+ public void testPendingJobSorting_Random() {
+ Random random = new Random(1); // Always use the same series of pseudo random values.
+
+ mService.mPendingJobs.clear();
+
+ for (int i = 0; i < 2500; ++i) {
+ JobStatus job = createJobStatus("testPendingJobSorting_Random",
+ createJobInfo(i).setExpedited(random.nextBoolean()), random.nextInt(250));
+ job.enqueueTime = Math.abs(random.nextInt(1_000_000));
+ mService.mPendingJobs.add(job);
+
+ mService.mPendingJobComparator.refreshLocked();
+ try {
+ mService.mPendingJobs.sort(mService.mPendingJobComparator);
+ } catch (Exception e) {
+ for (JobStatus toDump : mService.mPendingJobs) {
+ Log.i(TAG, sortedJobToString(toDump));
+ }
+ throw e;
+ }
+ checkPendingJobInvariants();
+ }
+ }
+
+ private int sign(int i) {
+ if (i > 0) {
+ return 1;
+ }
+ if (i < 0) {
+ return -1;
+ }
+ return 0;
+ }
+
+ @Test
+ public void testPendingJobSortingTransitivity() {
+ Random random = new Random(1); // Always use the same series of pseudo random values.
+
+ mService.mPendingJobs.clear();
+
+ for (int i = 0; i < 250; ++i) {
+ JobStatus job = createJobStatus("testPendingJobSortingTransitivity",
+ createJobInfo(i).setExpedited(random.nextBoolean()), random.nextInt(50));
+ job.enqueueTime = Math.abs(random.nextInt(1_000_000));
+ job.overrideState = random.nextInt(4);
+ mService.mPendingJobs.add(job);
+ }
+
+ mService.mPendingJobComparator.refreshLocked();
+
+ for (int i = 0; i < mService.mPendingJobs.size(); ++i) {
+ final JobStatus job1 = mService.mPendingJobs.get(i);
+
+ for (int j = 0; j < mService.mPendingJobs.size(); ++j) {
+ final JobStatus job2 = mService.mPendingJobs.get(j);
+ final int sign12 = sign(mService.mPendingJobComparator.compare(job1, job2));
+ final int sign21 = sign(mService.mPendingJobComparator.compare(job2, job1));
+ if (sign12 != -sign21) {
+ final String job1String = sortedJobToString(job1);
+ final String job2String = sortedJobToString(job2);
+ fail("compare(" + job1String + ", " + job2String + ") != "
+ + "-compare(" + job2String + ", " + job1String + ")");
+ }
+
+ for (int k = 0; k < mService.mPendingJobs.size(); ++k) {
+ final JobStatus job3 = mService.mPendingJobs.get(k);
+ final int sign23 = sign(mService.mPendingJobComparator.compare(job2, job3));
+ final int sign13 = sign(mService.mPendingJobComparator.compare(job1, job3));
+
+ // Confirm 1 < 2 < 3 or 1 > 2 > 3 or 1 == 2 == 3
+ if ((sign12 == sign23 && sign12 != sign13)
+ // Confirm that if 1 == 2, then (1 < 3 AND 2 < 3) OR (1 > 3 && 2 > 3)
+ || (sign12 == 0 && sign13 != sign23)) {
+ final String job1String = sortedJobToString(job1);
+ final String job2String = sortedJobToString(job2);
+ final String job3String = sortedJobToString(job3);
+ fail("Transitivity fail"
+ + ": compare(" + job1String + ", " + job2String + ")=" + sign12
+ + ", compare(" + job2String + ", " + job3String + ")=" + sign23
+ + ", compare(" + job1String + ", " + job3String + ")=" + sign13);
+ }
+ }
+ }
+ }
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java
index b480f24..5e219a2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import android.content.Context;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -49,6 +50,7 @@
private static final int NOTIFICATION_RESPONSIVENESS = 0;
private static final int UNKNOWN_TIMER = 0;
+ private @Mock Context mContext;
private @Mock GnssConfiguration mMockConfiguration;
private @Mock GnssNative.GeofenceCallbacks mGeofenceCallbacks;
@@ -63,7 +65,7 @@
GnssNative.setGnssHalForTest(mFakeHal);
GnssNative gnssNative = Objects.requireNonNull(
- GnssNative.create(new TestInjector(), mMockConfiguration));
+ GnssNative.create(new TestInjector(mContext), mMockConfiguration));
gnssNative.setGeofenceCallbacks(mGeofenceCallbacks);
mTestProvider = new GnssGeofenceProxy(gnssNative);
gnssNative.register();
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java
index 3d03781..d728451 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeAppOpsHelper.java
@@ -29,9 +29,10 @@
public class FakeAppOpsHelper extends AppOpsHelper {
private static class AppOp {
- private boolean mAllowed = true;
- private boolean mStarted = false;
- private int mNoteCount = 0;
+ AppOp() {}
+ boolean mAllowed = true;
+ boolean mStarted = false;
+ int mNoteCount = 0;
}
private final HashMap<String, SparseArray<AppOp>> mAppOps;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java
index f1099f0..cd70020 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeSettingsHelper.java
@@ -29,8 +29,8 @@
import java.util.concurrent.CopyOnWriteArrayList;
/**
- * Version of AppOpsHelper for testing. Settings are initialized to reasonable defaults (location is
- * enabled by default).
+ * Version of SettingsHelper for testing. Settings are initialized to reasonable defaults (location
+ * is enabled by default).
*/
public class FakeSettingsHelper extends SettingsHelper {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
index ae70dad..bd24cfd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
@@ -16,9 +16,14 @@
package com.android.server.location.injector;
+import android.content.Context;
+
+import com.android.server.location.settings.FakeLocationSettings;
+
public class TestInjector implements Injector {
private final FakeUserInfoHelper mUserInfoHelper;
+ private final FakeLocationSettings mLocationSettings;
private final FakeAlarmHelper mAlarmHelper;
private final FakeAppOpsHelper mAppOpsHelper;
private final FakeLocationPermissionsHelper mLocationPermissionsHelper;
@@ -32,8 +37,9 @@
private final FakeEmergencyHelper mEmergencyHelper;
private final LocationUsageLogger mLocationUsageLogger;
- public TestInjector() {
+ public TestInjector(Context context) {
mUserInfoHelper = new FakeUserInfoHelper();
+ mLocationSettings = new FakeLocationSettings(context);
mAlarmHelper = new FakeAlarmHelper();
mAppOpsHelper = new FakeAppOpsHelper();
mLocationPermissionsHelper = new FakeLocationPermissionsHelper(mAppOpsHelper);
@@ -54,6 +60,11 @@
}
@Override
+ public FakeLocationSettings getLocationSettings() {
+ return mLocationSettings;
+ }
+
+ @Override
public FakeAlarmHelper getAlarmHelper() {
return mAlarmHelper;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 6bc3b60..f703e2e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -19,6 +19,8 @@
import static android.app.AppOpsManager.OP_FINE_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
+import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
+import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationRequest.PASSIVE_INTERVAL;
import static android.location.provider.ProviderProperties.POWER_USAGE_HIGH;
import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
@@ -56,6 +58,8 @@
import static org.testng.Assert.assertThrows;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.location.ILocationCallback;
import android.location.ILocationListener;
import android.location.LastLocationRequest;
@@ -82,6 +86,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.location.injector.FakeUserInfoHelper;
@@ -139,6 +144,10 @@
@Mock
private Context mContext;
@Mock
+ private Resources mResources;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
private PowerManager mPowerManager;
@Mock
private PowerManager.WakeLock mWakeLock;
@@ -161,20 +170,28 @@
LocalServices.addService(LocationManagerInternal.class, mInternal);
doReturn("android").when(mContext).getPackageName();
+ doReturn(mResources).when(mContext).getResources();
+ doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
doReturn(mWakeLock).when(mPowerManager).newWakeLock(anyInt(), anyString());
- mInjector = new TestInjector();
+ mInjector = new TestInjector(mContext);
mInjector.getUserInfoHelper().startUser(OTHER_USER);
mPassive = new PassiveLocationProviderManager(mContext, mInjector);
mPassive.startManager(null);
mPassive.setRealProvider(new PassiveLocationProvider(mContext));
+ createManager(NAME);
+ }
+
+ private void createManager(String name) {
+ mStateChangedListener = mock(LocationProviderManager.StateChangedListener.class);
+
mProvider = new TestProvider(PROPERTIES, PROVIDER_IDENTITY);
mProvider.setProviderAllowed(true);
- mManager = new LocationProviderManager(mContext, mInjector, NAME, mPassive);
+ mManager = new LocationProviderManager(mContext, mInjector, name, mPassive);
mManager.startManager(mStateChangedListener);
mManager.setRealProvider(mProvider);
}
@@ -1017,6 +1034,95 @@
}
@Test
+ public void testProviderRequest_AdasGnssBypass() {
+ doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+ doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+
+ createManager(GPS_PROVIDER);
+
+ ILocationListener listener1 = createMockLocationListener();
+ LocationRequest request1 = new LocationRequest.Builder(5)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
+ assertThat(mProvider.getRequest().isAdasGnssBypass()).isFalse();
+
+ ILocationListener listener2 = createMockLocationListener();
+ LocationRequest request2 = new LocationRequest.Builder(1)
+ .setAdasGnssBypass(true)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
+
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1);
+ assertThat(mProvider.getRequest().isAdasGnssBypass()).isTrue();
+ }
+
+ @Test
+ public void testProviderRequest_AdasGnssBypass_ProviderDisabled() {
+ doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+ doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+
+ createManager(GPS_PROVIDER);
+
+ ILocationListener listener1 = createMockLocationListener();
+ LocationRequest request1 = new LocationRequest.Builder(1)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+
+ ILocationListener listener2 = createMockLocationListener();
+ LocationRequest request2 = new LocationRequest.Builder(5)
+ .setAdasGnssBypass(true)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
+
+ mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId());
+
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
+ assertThat(mProvider.getRequest().isAdasGnssBypass()).isTrue();
+ }
+
+ @Test
+ public void testProviderRequest_AdasGnssBypass_ProviderDisabled_AdasDisabled() {
+ mInjector.getSettingsHelper().setIgnoreSettingsAllowlist(
+ new PackageTagsList.Builder().add(
+ IDENTITY.getPackageName()).build());
+ doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+ doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+
+ createManager(GPS_PROVIDER);
+
+ ILocationListener listener1 = createMockLocationListener();
+ LocationRequest request1 = new LocationRequest.Builder(5)
+ .setLocationSettingsIgnored(true)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
+
+ ILocationListener listener2 = createMockLocationListener();
+ LocationRequest request2 = new LocationRequest.Builder(1)
+ .setAdasGnssBypass(true)
+ .setWorkSource(WORK_SOURCE)
+ .build();
+ mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2);
+
+ mInjector.getLocationSettings().updateUserSettings(IDENTITY.getUserId(),
+ settings -> settings.withAdasGnssLocationEnabled(false));
+ mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId());
+
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5);
+ assertThat(mProvider.getRequest().isAdasGnssBypass()).isFalse();
+ }
+
+ @Test
public void testProviderRequest_BatterySaver_ScreenOnOff() {
mInjector.getLocationPowerSaveModeHelper().setLocationPowerSaveMode(
LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
index cf5db2e..8532dbb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
@@ -158,7 +158,7 @@
@Test
public void testSetState() {
- assertThat(mProvider.isAllowed()).isFalse();
+ assertThat(mProvider.getState().allowed).isFalse();
AbstractLocationProvider.State newState;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
index 04e0151..63996f0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.MockitoAnnotations.initMocks;
+import android.content.Context;
import android.location.Location;
import android.location.LocationResult;
import android.location.provider.ProviderRequest;
@@ -58,6 +59,7 @@
private TestInjector mInjector;
private FakeProvider mDelegateProvider;
+ private @Mock Context mContext;
private @Mock AbstractLocationProvider.Listener mListener;
private @Mock FakeProvider.FakeProviderInterface mDelegate;
@@ -72,7 +74,7 @@
mRandom = new Random(seed);
- mInjector = new TestInjector();
+ mInjector = new TestInjector(mContext);
mDelegateProvider = new FakeProvider(mDelegate);
mProvider = new StationaryThrottlingLocationProvider("test_provider", mInjector,
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/settings/FakeLocationSettings.java b/services/tests/mockingservicestests/src/com/android/server/location/settings/FakeLocationSettings.java
new file mode 100644
index 0000000..4d46aba
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/settings/FakeLocationSettings.java
@@ -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.
+ */
+
+package com.android.server.location.settings;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import java.io.File;
+
+public class FakeLocationSettings extends LocationSettings {
+
+ public FakeLocationSettings(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected File getUserSettingsDir(int userId) {
+ return ApplicationProvider.getApplicationContext().getCacheDir();
+ }
+
+ @Override
+ protected LocationUserSettingsStore createUserSettingsStore(int userId, File file) {
+ return new FakeLocationUserSettingsStore(userId, file);
+ }
+
+ private class FakeLocationUserSettingsStore extends LocationUserSettingsStore {
+
+ FakeLocationUserSettingsStore(int userId, File file) {
+ super(userId, file);
+ }
+
+ @Override
+ protected void onChange(LocationUserSettings oldSettings,
+ LocationUserSettings newSettings) {
+ fireListeners(mUserId, oldSettings, newSettings);
+ }
+ }
+}
+
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/settings/LocationSettingsTest.java b/services/tests/mockingservicestests/src/com/android/server/location/settings/LocationSettingsTest.java
new file mode 100644
index 0000000..4b6c79b
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/settings/LocationSettingsTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.settings;
+
+import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.R;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.io.File;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LocationSettingsTest {
+
+ private @Mock Context mContext;
+ private @Mock Resources mResources;
+ private @Mock PackageManager mPackageManager;
+
+ private LocationSettings mLocationSettings;
+
+ @Before
+ public void setUp() {
+ initMocks(this);
+
+ doReturn(mResources).when(mContext).getResources();
+ doReturn(mPackageManager).when(mContext).getPackageManager();
+ doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+
+ resetLocationSettings();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mLocationSettings.deleteFiles();
+ }
+
+ private void resetLocationSettings() {
+ mLocationSettings = new LocationSettings(mContext) {
+ @Override
+ protected File getUserSettingsDir(int userId) {
+ return ApplicationProvider.getApplicationContext().getCacheDir();
+ }
+ };
+ }
+
+ @Test
+ public void testLoadDefaults() {
+ doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
+
+ doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+ assertThat(mLocationSettings.getUserSettings(2).isAdasGnssLocationEnabled()).isFalse();
+ }
+
+ @Test
+ public void testUpdate() {
+ doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(true));
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
+
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(false));
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isFalse();
+ }
+
+ @Test
+ public void testSerialization() throws Exception {
+ doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(true));
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
+
+ mLocationSettings.flushFiles();
+ resetLocationSettings();
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isTrue();
+ }
+
+ @Test
+ public void testListeners() {
+ doReturn(false).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+ LocationSettings.LocationUserSettingsListener listener = mock(
+ LocationSettings.LocationUserSettingsListener.class);
+
+ mLocationSettings.registerLocationUserSettingsListener(listener);
+
+ ArgumentCaptor<LocationUserSettings> oldCaptor = ArgumentCaptor.forClass(
+ LocationUserSettings.class);
+ ArgumentCaptor<LocationUserSettings> newCaptor = ArgumentCaptor.forClass(
+ LocationUserSettings.class);
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(true));
+ verify(listener, timeout(500).times(1)).onLocationUserSettingsChanged(eq(1),
+ oldCaptor.capture(), newCaptor.capture());
+ assertThat(oldCaptor.getValue().isAdasGnssLocationEnabled()).isFalse();
+ assertThat(newCaptor.getValue().isAdasGnssLocationEnabled()).isTrue();
+
+ oldCaptor = ArgumentCaptor.forClass(LocationUserSettings.class);
+ newCaptor = ArgumentCaptor.forClass(LocationUserSettings.class);
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(false));
+ verify(listener, timeout(500).times(2)).onLocationUserSettingsChanged(eq(1),
+ oldCaptor.capture(), newCaptor.capture());
+ assertThat(oldCaptor.getValue().isAdasGnssLocationEnabled()).isTrue();
+ assertThat(newCaptor.getValue().isAdasGnssLocationEnabled()).isFalse();
+
+ mLocationSettings.unregisterLocationUserSettingsListener(listener);
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(true));
+ verify(listener, after(500).times(2)).onLocationUserSettingsChanged(anyInt(), any(), any());
+ }
+
+ @Test
+ public void testNonAutomotive() {
+ doReturn(false).when(mPackageManager).hasSystemFeature(FEATURE_AUTOMOTIVE);
+ doReturn(true).when(mResources).getBoolean(R.bool.config_defaultAdasGnssLocationEnabled);
+
+ LocationSettings.LocationUserSettingsListener listener = mock(
+ LocationSettings.LocationUserSettingsListener.class);
+ mLocationSettings.registerLocationUserSettingsListener(listener);
+
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isFalse();
+ mLocationSettings.updateUserSettings(1,
+ settings -> settings.withAdasGnssLocationEnabled(true));
+ assertThat(mLocationSettings.getUserSettings(1).isAdasGnssLocationEnabled()).isFalse();
+ verify(listener, after(500).never()).onLocationUserSettingsChanged(anyInt(), any(), any());
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index 7d628be..68570ff 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -286,6 +286,7 @@
ApexSessionInfo activationFailed = new ApexSessionInfo();
activationFailed.sessionId = 1543;
activationFailed.isActivationFailed = true;
+ activationFailed.errorMessage = "Failed for test";
ApexSessionInfo staged = new ApexSessionInfo();
staged.sessionId = 101;
@@ -309,8 +310,8 @@
assertThat(apexSession1.getErrorCode())
.isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
- assertThat(apexSession1.getErrorMessage()).isEqualTo("APEX activation failed. Check logcat "
- + "messages from apexd for more information.");
+ assertThat(apexSession1.getErrorMessage()).isEqualTo("APEX activation failed. "
+ + "Failed for test");
assertThat(apexSession2.getErrorCode())
.isEqualTo(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED);
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index 5a42c4b..53483f6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -31,6 +31,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.junit.Assume.assumeThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -57,6 +58,9 @@
import android.testing.TestableContext;
import android.util.Log;
import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
import android.view.Display;
import androidx.test.filters.FlakyTest;
@@ -82,9 +86,12 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.quality.Strictness;
+import org.xmlpull.v1.XmlPullParserException;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
/**
* Tests for the {@link WallpaperManagerService} class.
@@ -355,6 +362,31 @@
verifyDisplayData();
}
+ @Test
+ public void testXmlSerializationRoundtrip() {
+ WallpaperData systemWallpaperData = mService.getCurrentWallpaperData(FLAG_SYSTEM, 0);
+ try {
+ TypedXmlSerializer serializer = Xml.newBinarySerializer();
+ serializer.setOutput(new ByteArrayOutputStream(), StandardCharsets.UTF_8.name());
+ serializer.startDocument(StandardCharsets.UTF_8.name(), true);
+ mService.writeWallpaperAttributes(serializer, "wp", systemWallpaperData);
+ } catch (IOException e) {
+ fail("exception occurred while writing system wallpaper attributes");
+ }
+
+ WallpaperData shouldMatchSystem = new WallpaperData(systemWallpaperData.userId,
+ systemWallpaperData.wallpaperFile.getParentFile(),
+ systemWallpaperData.wallpaperFile.getAbsolutePath(),
+ systemWallpaperData.cropFile.getAbsolutePath());
+ try {
+ TypedXmlPullParser parser = Xml.newBinaryPullParser();
+ mService.parseWallpaperAttributes(parser, shouldMatchSystem, true);
+ } catch (XmlPullParserException e) {
+ fail("exception occurred while parsing wallpaper");
+ }
+ assertEquals(systemWallpaperData.primaryColors, shouldMatchSystem.primaryColors);
+ }
+
// Verify that after continue switch user from userId 0 to lastUserId, the wallpaper data for
// non-current user must not bind to wallpaper service.
private void verifyNoConnectionBeforeLastUser(int lastUserId) {
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 8666fa6..3c10789 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
@@ -42,6 +42,7 @@
import com.android.compatibility.common.util.SystemUtil;
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
+import com.android.server.appsearch.visibilitystore.VisibilityStoreImpl;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -66,6 +67,7 @@
private final Map<UserHandle, PackageManager> mMockPackageManagers = new ArrayMap<>();
private Context mContext;
private AppSearchImpl mAppSearchImpl;
+ private VisibilityStoreImpl mVisibilityStore;
private int mGlobalQuerierUid;
@Before
@@ -89,13 +91,9 @@
};
// Give ourselves global query permissions
- mAppSearchImpl =
- AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- mContext,
- /*logger=*/ null,
- ALWAYS_OPTIMIZE);
-
+ mAppSearchImpl = AppSearchImpl.create(
+ mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ mVisibilityStore = VisibilityStoreImpl.create(mAppSearchImpl, mContext);
mGlobalQuerierUid =
mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
}
@@ -128,34 +126,29 @@
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
- /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
- /*schemasPackageAccessible=*/ ImmutableMap.of(
+ mVisibilityStore,
+ /*schemasNotDisplayedBySystem=*/ Collections.singletonList("schema1"),
+ /*schemasVisibleToPackages=*/ ImmutableMap.of(
"schema1",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
/*forceOverride=*/ false,
/*schemaVersion=*/ 0);
// "schema1" is platform hidden now and package visible to package1
- assertThat(
- mAppSearchImpl
- .getVisibilityStoreLocked()
- .isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- mContext.getPackageName(),
- mGlobalQuerierUid))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ prefix + "schema1",
+ mGlobalQuerierUid,
+ /*callerHasSystemAccess=*/ true))
.isFalse();
- assertThat(
- mAppSearchImpl
- .getVisibilityStoreLocked()
- .isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- packageNameFoo,
- uidFoo))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ prefix + "schema1",
+ uidFoo,
+ /*callerHasSystemAccess=*/ false))
.isTrue();
// Add a new schema, and include the already-existing "schema1"
@@ -165,8 +158,9 @@
ImmutableList.of(
new AppSearchSchema.Builder("schema1").build(),
new AppSearchSchema.Builder("schema2").build()),
- /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
- /*schemasPackageAccessible=*/ ImmutableMap.of(
+ mVisibilityStore,
+ /*schemasNotDisplayedBySystem=*/ Collections.singletonList("schema1"),
+ /*schemasVisibleToPackages=*/ ImmutableMap.of(
"schema1",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
/*forceOverride=*/ false,
@@ -174,50 +168,40 @@
// Check that "schema1" still has the same visibility settings
SystemUtil.runWithShellPermissionIdentity(() -> assertThat(
- mAppSearchImpl
- .getVisibilityStoreLocked()
- .isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- mContext.getPackageName(),
- mGlobalQuerierUid))
+ mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ prefix + "schema1",
+ mGlobalQuerierUid,
+ /*callerHasSystemAccess=*/ true))
.isFalse(),
READ_GLOBAL_APP_SEARCH_DATA);
- assertThat(
- mAppSearchImpl
- .getVisibilityStoreLocked()
- .isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- packageNameFoo,
- uidFoo))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ prefix + "schema1",
+ uidFoo,
+ /*callerHasSystemAccess=*/ false))
.isTrue();
// "schema2" has default visibility settings
SystemUtil.runWithShellPermissionIdentity(() -> assertThat(
- mAppSearchImpl
- .getVisibilityStoreLocked()
- .isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema2",
- mContext.getPackageName(),
- mGlobalQuerierUid))
+ mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ prefix + "schema2",
+ mGlobalQuerierUid,
+ /*callerHasSystemAccess=*/ true))
.isTrue(),
READ_GLOBAL_APP_SEARCH_DATA);
- assertThat(
- mAppSearchImpl
- .getVisibilityStoreLocked()
- .isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema2",
- packageNameFoo,
- uidFoo))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ prefix + "schema2",
+ uidFoo,
+ /*callerHasSystemAccess=*/ false))
.isFalse();
}
@@ -248,34 +232,29 @@
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
- /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
- /*schemasPackageAccessible=*/ ImmutableMap.of(
+ mVisibilityStore,
+ /*schemasNotDisplayedBySystem=*/ Collections.singletonList("schema1"),
+ /*schemasVisibleToPackages=*/ ImmutableMap.of(
"schema1",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
/*forceOverride=*/ false,
/*schemaVersion=*/ 0);
// "schema1" is platform hidden now and package accessible
- assertThat(
- mAppSearchImpl
- .getVisibilityStoreLocked()
- .isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- mContext.getPackageName(),
- mGlobalQuerierUid))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ prefix + "schema1",
+ mGlobalQuerierUid,
+ /*callerHasSystemAccess=*/ true))
.isFalse();
- assertThat(
- mAppSearchImpl
- .getVisibilityStoreLocked()
- .isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- packageNameFoo,
- uidFoo))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ prefix + "schema1",
+ uidFoo,
+ /*callerHasSystemAccess=*/ false))
.isTrue();
// Remove "schema1" by force overriding
@@ -283,32 +262,27 @@
"package",
"database",
/*schemas=*/ Collections.emptyList(),
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ mVisibilityStore,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ true,
/*schemaVersion=*/ 0);
// Check that "schema1" is no longer considered platform hidden or package accessible
- assertThat(
- mAppSearchImpl
- .getVisibilityStoreLocked()
- .isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- mContext.getPackageName(),
- mGlobalQuerierUid))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ prefix + "schema1",
+ mGlobalQuerierUid,
+ /*callerHasSystemAccess=*/ true))
.isTrue();
- assertThat(
- mAppSearchImpl
- .getVisibilityStoreLocked()
- .isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- packageNameFoo,
- uidFoo))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ prefix + "schema1",
+ uidFoo,
+ /*callerHasSystemAccess=*/ false))
.isFalse();
// Add "schema1" back, it gets default visibility settings which means it's not platform
@@ -317,30 +291,25 @@
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ mVisibilityStore,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*schemaVersion=*/ 0);
- assertThat(
- mAppSearchImpl
- .getVisibilityStoreLocked()
- .isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- mContext.getPackageName(),
- mGlobalQuerierUid))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ prefix + "schema1",
+ mGlobalQuerierUid,
+ /*callerHasSystemAccess=*/ true))
.isTrue();
- assertThat(
- mAppSearchImpl
- .getVisibilityStoreLocked()
- .isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "schema1",
- packageNameFoo,
- uidFoo))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ prefix + "schema1",
+ uidFoo,
+ /*callerHasSystemAccess=*/ false))
.isFalse();
}
@@ -357,20 +326,18 @@
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ mVisibilityStore,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*schemaVersion=*/ 0);
- assertThat(
- mAppSearchImpl
- .getVisibilityStoreLocked()
- .isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "Schema",
- mContext.getPackageName(),
- mGlobalQuerierUid))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ prefix + "Schema",
+ mGlobalQuerierUid,
+ /*callerHasSystemAccess=*/ true))
.isTrue();
}
@@ -387,25 +354,23 @@
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
- /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("Schema"),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ mVisibilityStore,
+ /*schemasNotDisplayedBySystem=*/ Collections.singletonList("Schema"),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*schemaVersion=*/ 0);
- assertThat(
- mAppSearchImpl
- .getVisibilityStoreLocked()
- .isSchemaSearchableByCaller(
- "package",
- "database",
- prefix + "Schema",
- mContext.getPackageName(),
- mGlobalQuerierUid))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ prefix + "Schema",
+ mGlobalQuerierUid,
+ /*callerHasSystemAccess=*/ true))
.isFalse();
}
@Test
- public void testSetSchema_defaultNotPackageAccessible() throws Exception {
+ public void testSetSchema_defaultNotVisibleToPackages() throws Exception {
String packageName = "com.package";
// Make sure package doesn't global query privileges
@@ -418,24 +383,23 @@
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ mVisibilityStore,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*schemaVersion=*/ 0);
- assertThat(
- mAppSearchImpl
- .getVisibilityStoreLocked()
+ assertThat(mVisibilityStore
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "Schema",
- packageName,
- /*callerUid=*/ 42))
+ /*callerUid=*/ 42,
+ /*callerHasSystemAccess=*/ false))
.isFalse();
}
@Test
- public void testSetSchema_packageAccessible() throws Exception {
+ public void testSetSchema_visibleToPackages() throws Exception {
// Values for a "foo" client
String packageNameFoo = "packageFoo";
byte[] sha256CertFoo = new byte[] {10};
@@ -458,21 +422,20 @@
"package",
"database",
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ ImmutableMap.of(
+ mVisibilityStore,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ ImmutableMap.of(
"Schema",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))),
/*forceOverride=*/ false,
/*schemaVersion=*/ 0);
- assertThat(
- mAppSearchImpl
- .getVisibilityStoreLocked()
+ assertThat(mVisibilityStore
.isSchemaSearchableByCaller(
"package",
"database",
prefix + "Schema",
- packageNameFoo,
- uidFoo))
+ uidFoo,
+ /*callerHasSystemAccess=*/ false))
.isTrue();
}
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 e26cfea..330b1a7 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
@@ -22,7 +22,7 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.testng.Assert.expectThrows;
+import static org.junit.Assert.assertThrows;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSchema;
@@ -34,6 +34,7 @@
import android.app.appsearch.StorageInfo;
import android.app.appsearch.exceptions.AppSearchException;
import android.content.Context;
+import android.os.Process;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -42,20 +43,19 @@
import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter;
import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
-import com.android.server.appsearch.proto.DocumentProto;
-import com.android.server.appsearch.proto.GetOptimizeInfoResultProto;
-import com.android.server.appsearch.proto.PersistType;
-import com.android.server.appsearch.proto.PropertyConfigProto;
-import com.android.server.appsearch.proto.PropertyProto;
-import com.android.server.appsearch.proto.PutResultProto;
-import com.android.server.appsearch.proto.SchemaProto;
-import com.android.server.appsearch.proto.SchemaTypeConfigProto;
-import com.android.server.appsearch.proto.SearchResultProto;
-import com.android.server.appsearch.proto.SearchSpecProto;
-import com.android.server.appsearch.proto.StatusProto;
-import com.android.server.appsearch.proto.StringIndexingConfig;
-import com.android.server.appsearch.proto.TermMatchType;
-import com.android.server.appsearch.visibilitystore.VisibilityStore;
+import com.android.server.appsearch.icing.proto.DocumentProto;
+import com.android.server.appsearch.icing.proto.GetOptimizeInfoResultProto;
+import com.android.server.appsearch.icing.proto.PersistType;
+import com.android.server.appsearch.icing.proto.PropertyConfigProto;
+import com.android.server.appsearch.icing.proto.PropertyProto;
+import com.android.server.appsearch.icing.proto.PutResultProto;
+import com.android.server.appsearch.icing.proto.SchemaProto;
+import com.android.server.appsearch.icing.proto.SchemaTypeConfigProto;
+import com.android.server.appsearch.icing.proto.SearchResultProto;
+import com.android.server.appsearch.icing.proto.SearchSpecProto;
+import com.android.server.appsearch.icing.proto.StatusProto;
+import com.android.server.appsearch.icing.proto.StringIndexingConfig;
+import com.android.server.appsearch.icing.proto.TermMatchType;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -83,15 +83,9 @@
@Before
public void setUp() throws Exception {
- Context context = ApplicationProvider.getApplicationContext();
-
- // Give ourselves global query permissions.
mAppSearchImpl =
AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- context,
- /*logger=*/ null,
- ALWAYS_OPTIMIZE);
+ mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
}
/**
@@ -149,7 +143,7 @@
.build();
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
- mAppSearchImpl.rewriteSchema(
+ AppSearchImpl.rewriteSchema(
createPrefix("package", "newDatabase"), existingSchemaBuilder, newSchema);
// We rewrote all the new types that were added. And nothing was removed.
@@ -249,7 +243,7 @@
.build();
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
- mAppSearchImpl.rewriteSchema(
+ AppSearchImpl.rewriteSchema(
createPrefix("package", "existingDatabase"),
existingSchemaBuilder,
newSchema);
@@ -284,7 +278,7 @@
.build();
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
- mAppSearchImpl.rewriteSchema(
+ AppSearchImpl.rewriteSchema(
createPrefix("package", "existingDatabase"),
existingSchemaBuilder,
newSchema);
@@ -386,7 +380,7 @@
}
@Test
- public void testRemoveDatabasesFromDocumentThrowsException() throws Exception {
+ public void testRemoveDatabasesFromDocumentThrowsException() {
// Set two different database names in the document, which should never happen
DocumentProto documentProto =
DocumentProto.newBuilder()
@@ -397,13 +391,13 @@
DocumentProto.Builder actualDocument = documentProto.toBuilder();
AppSearchException e =
- expectThrows(
+ assertThrows(
AppSearchException.class, () -> removePrefixesFromDocument(actualDocument));
assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names");
}
@Test
- public void testNestedRemoveDatabasesFromDocumentThrowsException() throws Exception {
+ public void testNestedRemoveDatabasesFromDocumentThrowsException() {
// Set two different database names in the outer and inner document, which should never
// happen.
DocumentProto insideDocument =
@@ -422,7 +416,7 @@
DocumentProto.Builder actualDocument = documentProto.toBuilder();
AppSearchException e =
- expectThrows(
+ assertThrows(
AppSearchException.class, () -> removePrefixesFromDocument(actualDocument));
assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names");
}
@@ -436,8 +430,9 @@
"package",
"database",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -473,11 +468,7 @@
Context context = ApplicationProvider.getApplicationContext();
File appsearchDir = mTemporaryFolder.newFolder();
AppSearchImpl appSearchImpl =
- AppSearchImpl.create(
- appsearchDir,
- context,
- /*logger=*/ null,
- ALWAYS_OPTIMIZE);
+ AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
// Insert schema
List<AppSearchSchema> schemas =
@@ -488,8 +479,9 @@
context.getPackageName(),
"database1",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -506,7 +498,9 @@
/*queryExpression=*/ "",
new SearchSpec.Builder().addFilterSchemas("Type1").build(),
context.getPackageName(),
- VisibilityStore.NO_OP_UID,
+ /*visibilityStore=*/ null,
+ Process.INVALID_UID,
+ /*callerHasSystemAccess=*/ false,
/*logger=*/ null);
assertThat(results.getResults()).hasSize(1);
assertThat(results.getResults().get(0).getGenericDocument()).isEqualTo(validDoc);
@@ -519,7 +513,7 @@
.setSchema(context.getPackageName() + "$database1/Type1")
.build();
AppSearchException e =
- expectThrows(
+ assertThrows(
AppSearchException.class,
() -> PrefixUtil.getPrefix(invalidDoc.getNamespace()));
assertThat(e)
@@ -532,27 +526,20 @@
PutResultProto putResultProto = appSearchImpl.mIcingSearchEngineLocked.put(invalidDoc);
assertThat(putResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.OK);
- // Create a logger for capturing initialization to make sure we are logging the recovery
- // process correctly.
- AppSearchLoggerTest.TestLogger testLogger = new AppSearchLoggerTest.TestLogger();
-
// Initialize AppSearchImpl. This should cause a reset.
+ InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
appSearchImpl.close();
- appSearchImpl =
- AppSearchImpl.create(
- appsearchDir, context, testLogger, ALWAYS_OPTIMIZE);
+ appSearchImpl = AppSearchImpl.create(appsearchDir, initStatsBuilder, ALWAYS_OPTIMIZE);
// Check recovery state
- InitializeStats initStats = testLogger.mInitializeStats;
+ InitializeStats initStats = initStatsBuilder.build();
assertThat(initStats).isNotNull();
assertThat(initStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR);
assertThat(initStats.hasDeSync()).isFalse();
assertThat(initStats.getDocumentStoreRecoveryCause())
.isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
- // TODO(b/187879464): There should not be a recovery here, but icing lib reports one if the
- // doc had no tokens. Once the mentioned bug is fixed, uncomment this.
- // assertThat(initStats.getIndexRestorationCause())
- // .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
+ assertThat(initStats.getIndexRestorationCause())
+ .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
assertThat(initStats.getSchemaStoreRecoveryCause())
.isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
assertThat(initStats.getDocumentStoreDataStatus())
@@ -568,7 +555,9 @@
/*queryExpression=*/ "",
new SearchSpec.Builder().addFilterSchemas("Type1").build(),
context.getPackageName(),
- VisibilityStore.NO_OP_UID,
+ /*visibilityStore=*/ null,
+ Process.INVALID_UID,
+ /*callerHasSystemAccess=*/ false,
/*logger=*/ null);
assertThat(results.getResults()).isEmpty();
@@ -577,8 +566,9 @@
context.getPackageName(),
"database1",
Collections.singletonList(new AppSearchSchema.Builder("Type1").build()),
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -592,7 +582,9 @@
/*queryExpression=*/ "",
new SearchSpec.Builder().addFilterSchemas("Type1").build(),
context.getPackageName(),
- VisibilityStore.NO_OP_UID,
+ /*visibilityStore=*/ null,
+ Process.INVALID_UID,
+ /*callerHasSystemAccess=*/ false,
/*logger=*/ null);
assertThat(results.getResults()).hasSize(1);
assertThat(results.getResults().get(0).getGenericDocument()).isEqualTo(validDoc);
@@ -609,8 +601,9 @@
"package",
"database",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -642,16 +635,18 @@
"package",
"database1",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
mAppSearchImpl.setSchema(
"package",
"database2",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -694,8 +689,9 @@
"package",
"database",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -735,8 +731,9 @@
"package1",
"database1",
schema1,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -747,8 +744,9 @@
"package2",
"database2",
schema2,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -789,8 +787,9 @@
"package1",
"database1",
schema1,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -801,8 +800,9 @@
"package2",
"database2",
schema2,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -848,7 +848,9 @@
"",
searchSpec,
/*callerPackageName=*/ "",
- VisibilityStore.NO_OP_UID,
+ /*visibilityStore=*/ null,
+ Process.INVALID_UID,
+ /*callerHasSystemAccess=*/ false,
/*logger=*/ null);
assertThat(searchResultPage.getResults()).isEmpty();
}
@@ -888,8 +890,9 @@
"package",
"database1",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -911,9 +914,6 @@
@Test
public void testSetSchema_incompatible() throws Exception {
- List<SchemaTypeConfigProto> existingSchemas =
- mAppSearchImpl.getSchemaProtoLocked().getTypesList();
-
List<AppSearchSchema> oldSchemas = new ArrayList<>();
oldSchemas.add(
new AppSearchSchema.Builder("Email")
@@ -935,8 +935,9 @@
"package",
"database1",
oldSchemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -950,8 +951,9 @@
"package",
"database1",
newSchemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ true,
/*version=*/ 0);
assertThat(setSchemaResponse.getDeletedTypes()).containsExactly("Text");
@@ -972,8 +974,9 @@
"package",
"database1",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -1004,8 +1007,9 @@
"package",
"database1",
finalSchemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -1017,8 +1021,9 @@
"package",
"database1",
finalSchemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ true,
/*version=*/ 0);
@@ -1054,16 +1059,18 @@
"package",
"database1",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
mAppSearchImpl.setSchema(
"package",
"database2",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -1101,8 +1108,9 @@
"package",
"database1",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ true,
/*version=*/ 0);
@@ -1145,8 +1153,9 @@
"package",
"database",
schema,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -1206,16 +1215,18 @@
"packageA",
"database",
schema,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
mAppSearchImpl.setSchema(
"packageB",
"database",
schema,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -1259,8 +1270,9 @@
"package1",
"database1",
Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
assertThat(mAppSearchImpl.getPackageToDatabases())
@@ -1272,8 +1284,9 @@
"package1",
"database2",
Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
assertThat(mAppSearchImpl.getPackageToDatabases())
@@ -1285,8 +1298,9 @@
"package2",
"database1",
Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
assertThat(mAppSearchImpl.getPackageToDatabases())
@@ -1343,8 +1357,9 @@
"package",
"database",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -1493,8 +1508,9 @@
"package1",
"database",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -1515,8 +1531,9 @@
"package1",
"database",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -1530,8 +1547,9 @@
"package2",
"database",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -1578,8 +1596,9 @@
"package1",
"database",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -1600,8 +1619,9 @@
"package1",
"database1",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -1621,16 +1641,18 @@
"package1",
"database1",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
mAppSearchImpl.setSchema(
"package1",
"database2",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -1664,13 +1686,9 @@
@Test
public void testThrowsExceptionIfClosed() throws Exception {
- Context context = ApplicationProvider.getApplicationContext();
AppSearchImpl appSearchImpl =
AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- context,
- /*logger=*/ null,
- ALWAYS_OPTIMIZE);
+ mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
// Initial check that we could do something at first.
List<AppSearchSchema> schemas =
@@ -1679,150 +1697,126 @@
"package",
"database",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
appSearchImpl.close();
// Check all our public APIs
- expectThrows(
+ assertThrows(
IllegalStateException.class,
- () -> {
- appSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
- });
+ () ->
+ appSearchImpl.setSchema(
+ "package",
+ "database",
+ schemas,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0));
- expectThrows(
- IllegalStateException.class,
- () -> {
- appSearchImpl.getSchema("package", "database");
- });
+ assertThrows(
+ IllegalStateException.class, () -> appSearchImpl.getSchema("package", "database"));
- expectThrows(
+ assertThrows(
IllegalStateException.class,
- () -> {
- appSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id", "type").build(),
- /*logger=*/ null);
- });
+ () ->
+ appSearchImpl.putDocument(
+ "package",
+ "database",
+ new GenericDocument.Builder<>("namespace", "id", "type").build(),
+ /*logger=*/ null));
- expectThrows(
+ assertThrows(
IllegalStateException.class,
- () -> {
- appSearchImpl.getDocument(
- "package", "database", "namespace", "id", Collections.emptyMap());
- });
+ () ->
+ appSearchImpl.getDocument(
+ "package", "database", "namespace", "id", Collections.emptyMap()));
- expectThrows(
+ assertThrows(
IllegalStateException.class,
- () -> {
- appSearchImpl.query(
- "package",
- "database",
- "query",
- new SearchSpec.Builder()
- .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
- .build(),
- /*logger=*/ null);
- });
+ () ->
+ appSearchImpl.query(
+ "package",
+ "database",
+ "query",
+ new SearchSpec.Builder().build(),
+ /*logger=*/ null));
- expectThrows(
+ assertThrows(
IllegalStateException.class,
- () -> {
- appSearchImpl.globalQuery(
- "query",
- new SearchSpec.Builder()
- .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
- .build(),
- "package",
- VisibilityStore.NO_OP_UID,
- /*logger=*/ null);
- });
+ () ->
+ appSearchImpl.globalQuery(
+ "query",
+ new SearchSpec.Builder().build(),
+ "package",
+ /*visibilityStore=*/ null,
+ Process.INVALID_UID,
+ /*callerHasSystemAccess=*/ false,
+ /*logger=*/ null));
- expectThrows(
+ assertThrows(
IllegalStateException.class,
- () -> {
- appSearchImpl.getNextPage(/*nextPageToken=*/ 1L);
- });
+ () -> appSearchImpl.getNextPage(/*nextPageToken=*/ 1L));
- expectThrows(
+ assertThrows(
IllegalStateException.class,
- () -> {
- appSearchImpl.invalidateNextPageToken(/*nextPageToken=*/ 1L);
- });
+ () -> appSearchImpl.invalidateNextPageToken(/*nextPageToken=*/ 1L));
- expectThrows(
+ assertThrows(
IllegalStateException.class,
- () -> {
- appSearchImpl.reportUsage(
- "package",
- "database",
- "namespace",
- "id",
- /*usageTimestampMillis=*/ 1000L,
- /*systemUsage=*/ false);
- });
+ () ->
+ appSearchImpl.reportUsage(
+ "package",
+ "database",
+ "namespace",
+ "id",
+ /*usageTimestampMillis=*/ 1000L,
+ /*systemUsage=*/ false));
- expectThrows(
+ assertThrows(
IllegalStateException.class,
- () -> {
- appSearchImpl.remove(
- "package", "database", "namespace", "id", /*statsBuilder=*/ null);
- });
+ () ->
+ appSearchImpl.remove(
+ "package",
+ "database",
+ "namespace",
+ "id",
+ /*removeStatsBuilder=*/ null));
- expectThrows(
+ assertThrows(
IllegalStateException.class,
- () -> {
- appSearchImpl.removeByQuery(
- "package",
- "database",
- "query",
- new SearchSpec.Builder()
- .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
- .build(),
- /*statsBuilder=*/ null);
- });
+ () ->
+ appSearchImpl.removeByQuery(
+ "package",
+ "database",
+ "query",
+ new SearchSpec.Builder().build(),
+ /*removeStatsBuilder=*/ null));
- expectThrows(
+ assertThrows(
IllegalStateException.class,
- () -> {
- appSearchImpl.getStorageInfoForPackage("package");
- });
+ () -> appSearchImpl.getStorageInfoForPackage("package"));
- expectThrows(
+ assertThrows(
IllegalStateException.class,
- () -> {
- appSearchImpl.getStorageInfoForDatabase("package", "database");
- });
+ () -> appSearchImpl.getStorageInfoForDatabase("package", "database"));
- expectThrows(
+ assertThrows(
IllegalStateException.class,
- () -> {
- appSearchImpl.persistToDisk(PersistType.Code.FULL);
- });
+ () -> appSearchImpl.persistToDisk(PersistType.Code.FULL));
}
@Test
public void testPutPersistsWithLiteFlush() throws Exception {
// Setup the index
- Context context = ApplicationProvider.getApplicationContext();
File appsearchDir = mTemporaryFolder.newFolder();
AppSearchImpl appSearchImpl =
- AppSearchImpl.create(
- appsearchDir,
- context,
- /*logger=*/ null,
- ALWAYS_OPTIMIZE);
+ AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
@@ -1830,8 +1824,9 @@
"package",
"database",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -1848,11 +1843,7 @@
// That document should be visible even from another instance.
AppSearchImpl appSearchImpl2 =
- AppSearchImpl.create(
- appsearchDir,
- context,
- /*logger=*/ null,
- ALWAYS_OPTIMIZE);
+ AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
getResult =
appSearchImpl2.getDocument(
"package", "database", "namespace1", "id1", Collections.emptyMap());
@@ -1862,14 +1853,9 @@
@Test
public void testDeletePersistsWithLiteFlush() throws Exception {
// Setup the index
- Context context = ApplicationProvider.getApplicationContext();
File appsearchDir = mTemporaryFolder.newFolder();
AppSearchImpl appSearchImpl =
- AppSearchImpl.create(
- appsearchDir,
- context,
- /*logger=*/ null,
- ALWAYS_OPTIMIZE);
+ AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
@@ -1877,8 +1863,9 @@
"package",
"database",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -1903,7 +1890,7 @@
// Delete the first document
appSearchImpl.remove("package", "database", "namespace1", "id1", /*statsBuilder=*/ null);
appSearchImpl.persistToDisk(PersistType.Code.LITE);
- expectThrows(
+ assertThrows(
AppSearchException.class,
() ->
appSearchImpl.getDocument(
@@ -1919,12 +1906,8 @@
// Only the second document should be retrievable from another instance.
AppSearchImpl appSearchImpl2 =
- AppSearchImpl.create(
- appsearchDir,
- context,
- /*logger=*/ null,
- ALWAYS_OPTIMIZE);
- expectThrows(
+ AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ assertThrows(
AppSearchException.class,
() ->
appSearchImpl2.getDocument(
@@ -1942,14 +1925,9 @@
@Test
public void testDeleteByQueryPersistsWithLiteFlush() throws Exception {
// Setup the index
- Context context = ApplicationProvider.getApplicationContext();
File appsearchDir = mTemporaryFolder.newFolder();
AppSearchImpl appSearchImpl =
- AppSearchImpl.create(
- appsearchDir,
- context,
- /*logger=*/ null,
- ALWAYS_OPTIMIZE);
+ AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
@@ -1957,8 +1935,9 @@
"package",
"database",
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
@@ -1991,7 +1970,7 @@
.build(),
/*statsBuilder=*/ null);
appSearchImpl.persistToDisk(PersistType.Code.LITE);
- expectThrows(
+ assertThrows(
AppSearchException.class,
() ->
appSearchImpl.getDocument(
@@ -2007,12 +1986,8 @@
// Only the second document should be retrievable from another instance.
AppSearchImpl appSearchImpl2 =
- AppSearchImpl.create(
- appsearchDir,
- context,
- /*logger=*/ null,
- ALWAYS_OPTIMIZE);
- expectThrows(
+ AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ assertThrows(
AppSearchException.class,
() ->
appSearchImpl2.getDocument(
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 c6726c6..080c375 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
@@ -25,27 +25,32 @@
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 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.RemoveStats;
import com.android.server.appsearch.external.localstorage.stats.SearchStats;
-import com.android.server.appsearch.proto.DeleteStatsProto;
-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 com.android.server.appsearch.icing.proto.DeleteStatsProto;
+import com.android.server.appsearch.icing.proto.DocumentProto;
+import com.android.server.appsearch.icing.proto.InitializeStatsProto;
+import com.android.server.appsearch.icing.proto.PutDocumentStatsProto;
+import com.android.server.appsearch.icing.proto.PutResultProto;
+import com.android.server.appsearch.icing.proto.QueryStatsProto;
+import com.android.server.appsearch.icing.proto.ScoringSpecProto;
+import com.android.server.appsearch.icing.proto.StatusProto;
+import com.android.server.appsearch.icing.proto.TermMatchType;
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
+import java.io.File;
import java.util.Collections;
import java.util.List;
@@ -60,15 +65,9 @@
@Before
public void setUp() throws Exception {
- Context context = ApplicationProvider.getApplicationContext();
-
- // Give ourselves global query permissions
mAppSearchImpl =
AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- context,
- /*logger=*/ null,
- ALWAYS_OPTIMIZE);
+ mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
mLogger = new TestLogger();
}
@@ -288,45 +287,155 @@
// Testing actual logging
//
@Test
- public void testLoggingStats_initialize() throws Exception {
- Context context = ApplicationProvider.getApplicationContext();
-
+ public void testLoggingStats_initializeWithoutDocuments_success() throws Exception {
// Create an unused AppSearchImpl to generated an InitializeStats.
- AppSearchImpl appSearchImpl =
- AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- context,
- mLogger,
- ALWAYS_OPTIMIZE);
+ InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
+ AppSearchImpl.create(mTemporaryFolder.newFolder(), initStatsBuilder, ALWAYS_OPTIMIZE);
+ InitializeStats iStats = initStatsBuilder.build();
- InitializeStats iStats = mLogger.mInitializeStats;
assertThat(iStats).isNotNull();
assertThat(iStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
- assertThat(iStats.getTotalLatencyMillis()).isGreaterThan(0);
+ // Total latency captured in LocalStorage
+ assertThat(iStats.getTotalLatencyMillis()).isEqualTo(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);
+ assertThat(iStats.hasReset()).isEqualTo(false);
+ assertThat(iStats.getResetStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
}
@Test
- public void testLoggingStats_putDocument() throws Exception {
+ public void testLoggingStats_initializeWithDocuments_success() throws Exception {
+ final String testPackageName = "testPackage";
+ final String testDatabase = "testDatabase";
+ final File folder = mTemporaryFolder.newFolder();
+
+ AppSearchImpl appSearchImpl =
+ AppSearchImpl.create(folder, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ List<AppSearchSchema> schemas =
+ ImmutableList.of(
+ new AppSearchSchema.Builder("Type1").build(),
+ new AppSearchSchema.Builder("Type2").build());
+ appSearchImpl.setSchema(
+ testPackageName,
+ testDatabase,
+ schemas,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+ GenericDocument doc1 = new GenericDocument.Builder<>("namespace", "id1", "Type1").build();
+ GenericDocument doc2 = new GenericDocument.Builder<>("namespace", "id2", "Type1").build();
+ appSearchImpl.putDocument(testPackageName, testDatabase, doc1, mLogger);
+ appSearchImpl.putDocument(testPackageName, testDatabase, doc2, mLogger);
+ appSearchImpl.close();
+
+ // Create another appsearchImpl on the same folder
+ InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
+ AppSearchImpl.create(folder, initStatsBuilder, ALWAYS_OPTIMIZE);
+ InitializeStats iStats = initStatsBuilder.build();
+
+ assertThat(iStats).isNotNull();
+ assertThat(iStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
+ // Total latency captured in LocalStorage
+ assertThat(iStats.getTotalLatencyMillis()).isEqualTo(0);
+ assertThat(iStats.hasDeSync()).isFalse();
+ assertThat(iStats.getNativeLatencyMillis()).isGreaterThan(0);
+ assertThat(iStats.getDocumentStoreDataStatus())
+ .isEqualTo(InitializeStatsProto.DocumentStoreDataStatus.NO_DATA_LOSS_VALUE);
+ assertThat(iStats.getDocumentCount()).isEqualTo(2);
+ assertThat(iStats.getSchemaTypeCount()).isEqualTo(2);
+ assertThat(iStats.hasReset()).isEqualTo(false);
+ assertThat(iStats.getResetStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
+ }
+
+ @Test
+ public void testLoggingStats_initialize_failure() throws Exception {
+ final String testPackageName = "testPackage";
+ final String testDatabase = "testDatabase";
+ final File folder = mTemporaryFolder.newFolder();
+
+ AppSearchImpl appSearchImpl =
+ AppSearchImpl.create(folder, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+
+ List<AppSearchSchema> schemas =
+ ImmutableList.of(
+ new AppSearchSchema.Builder("Type1").build(),
+ new AppSearchSchema.Builder("Type2").build());
+ appSearchImpl.setSchema(
+ testPackageName,
+ testDatabase,
+ schemas,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Insert a valid doc
+ GenericDocument doc1 = new GenericDocument.Builder<>("namespace", "id1", "Type1").build();
+ appSearchImpl.putDocument(testPackageName, testDatabase, doc1, mLogger);
+
+ // Insert the invalid doc with an invalid namespace right into icing
+ DocumentProto invalidDoc =
+ DocumentProto.newBuilder()
+ .setNamespace("invalidNamespace")
+ .setUri("id2")
+ .setSchema(String.format("%s$%s/Type1", testPackageName, testDatabase))
+ .build();
+ PutResultProto putResultProto = appSearchImpl.mIcingSearchEngineLocked.put(invalidDoc);
+ assertThat(putResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.OK);
+ appSearchImpl.close();
+
+ // Create another appsearchImpl on the same folder
+ InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
+ AppSearchImpl.create(folder, initStatsBuilder, ALWAYS_OPTIMIZE);
+ InitializeStats iStats = initStatsBuilder.build();
+
+ // Some of other fields are already covered by AppSearchImplTest#testReset()
+ assertThat(iStats).isNotNull();
+ assertThat(iStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR);
+ assertThat(iStats.hasReset()).isTrue();
+ }
+
+ @Test
+ public void testLoggingStats_putDocument_success() throws Exception {
// Insert schema
final String testPackageName = "testPackage";
final String testDatabase = "testDatabase";
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ AppSearchSchema testSchema =
+ new AppSearchSchema.Builder("type")
+ .addProperty(
+ new AppSearchSchema.StringPropertyConfig.Builder("subject")
+ .setCardinality(
+ AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(
+ AppSearchSchema.StringPropertyConfig
+ .INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(
+ AppSearchSchema.StringPropertyConfig
+ .TOKENIZER_TYPE_PLAIN)
+ .build())
+ .build();
+ List<AppSearchSchema> schemas = Collections.singletonList(testSchema);
mAppSearchImpl.setSchema(
testPackageName,
testDatabase,
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
- GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build();
+
+ GenericDocument document =
+ new GenericDocument.Builder<>("namespace", "id", "type")
+ .setPropertyString("subject", "testPut example1")
+ .build();
mAppSearchImpl.putDocument(testPackageName, testDatabase, document, mLogger);
@@ -335,41 +444,119 @@
assertThat(pStats.getPackageName()).isEqualTo(testPackageName);
assertThat(pStats.getDatabase()).isEqualTo(testDatabase);
assertThat(pStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
- // The rest of native stats have been tested in testCopyNativeStats
+ // The latency related native stats have been tested in testCopyNativeStats
assertThat(pStats.getNativeDocumentSizeBytes()).isGreaterThan(0);
+ assertThat(pStats.getNativeNumTokensIndexed()).isGreaterThan(0);
}
@Test
- public void testLoggingStats_search() throws Exception {
+ public void testLoggingStats_putDocument_failure() throws Exception {
// Insert schema
final String testPackageName = "testPackage";
final String testDatabase = "testDatabase";
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ AppSearchSchema testSchema =
+ new AppSearchSchema.Builder("type")
+ .addProperty(
+ new AppSearchSchema.StringPropertyConfig.Builder("subject")
+ .setCardinality(
+ AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(
+ AppSearchSchema.StringPropertyConfig
+ .INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(
+ AppSearchSchema.StringPropertyConfig
+ .TOKENIZER_TYPE_PLAIN)
+ .build())
+ .build();
+ List<AppSearchSchema> schemas = Collections.singletonList(testSchema);
mAppSearchImpl.setSchema(
testPackageName,
testDatabase,
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
- GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build();
- mAppSearchImpl.putDocument(testPackageName, testDatabase, document, mLogger);
+
+ GenericDocument document =
+ new GenericDocument.Builder<>("namespace", "id", "type")
+ .setPropertyString("nonExist", "testPut example1")
+ .build();
+
+ // We mainly want to check the status code in stats. So we don't need to inspect the
+ // exception here.
+ Assert.assertThrows(
+ AppSearchException.class,
+ () -> mAppSearchImpl.putDocument(testPackageName, testDatabase, document, mLogger));
+
+ PutDocumentStats pStats = mLogger.mPutDocumentStats;
+ assertThat(pStats).isNotNull();
+ assertThat(pStats.getPackageName()).isEqualTo(testPackageName);
+ assertThat(pStats.getDatabase()).isEqualTo(testDatabase);
+ assertThat(pStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_NOT_FOUND);
+ }
+
+ @Test
+ public void testLoggingStats_search_success() throws Exception {
+ // Insert schema
+ final String testPackageName = "testPackage";
+ final String testDatabase = "testDatabase";
+ AppSearchSchema testSchema =
+ new AppSearchSchema.Builder("type")
+ .addProperty(
+ new AppSearchSchema.StringPropertyConfig.Builder("subject")
+ .setCardinality(
+ AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setIndexingType(
+ AppSearchSchema.StringPropertyConfig
+ .INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(
+ AppSearchSchema.StringPropertyConfig
+ .TOKENIZER_TYPE_PLAIN)
+ .build())
+ .build();
+ List<AppSearchSchema> schemas = Collections.singletonList(testSchema);
+ mAppSearchImpl.setSchema(
+ testPackageName,
+ testDatabase,
+ schemas,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+ GenericDocument document1 =
+ new GenericDocument.Builder<>("namespace", "id1", "type")
+ .setPropertyString("subject", "testPut example1")
+ .build();
+ GenericDocument document2 =
+ new GenericDocument.Builder<>("namespace", "id2", "type")
+ .setPropertyString("subject", "testPut example2")
+ .build();
+ GenericDocument document3 =
+ new GenericDocument.Builder<>("namespace", "id3", "type")
+ .setPropertyString("subject", "testPut 3")
+ .build();
+ mAppSearchImpl.putDocument(testPackageName, testDatabase, document1, mLogger);
+ mAppSearchImpl.putDocument(testPackageName, testDatabase, document2, mLogger);
+ mAppSearchImpl.putDocument(testPackageName, testDatabase, document3, mLogger);
// No query filters specified. package2 should only get its own documents back.
SearchSpec searchSpec =
- new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
+ new SearchSpec.Builder()
+ .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+ .setRankingStrategy(SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP)
+ .build();
+ String queryStr = "testPut e";
SearchResultPage searchResultPage =
mAppSearchImpl.query(
- testPackageName,
- testDatabase,
- /*QueryExpression=*/ "",
- searchSpec,
- /*logger=*/ mLogger);
+ testPackageName, testDatabase, queryStr, searchSpec, /*logger=*/ mLogger);
- assertThat(searchResultPage.getResults()).hasSize(1);
- assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
+ assertThat(searchResultPage.getResults()).hasSize(2);
+ // The ranking strategy is LIFO
+ assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2);
+ assertThat(searchResultPage.getResults().get(1).getGenericDocument()).isEqualTo(document1);
SearchStats sStats = mLogger.mSearchStats;
@@ -379,17 +566,59 @@
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.getTermCount()).isEqualTo(2);
+ assertThat(sStats.getQueryLength()).isEqualTo(queryStr.length());
assertThat(sStats.getFilteredNamespaceCount()).isEqualTo(1);
assertThat(sStats.getFilteredSchemaTypeCount()).isEqualTo(1);
- assertThat(sStats.getCurrentPageReturnedResultCount()).isEqualTo(1);
+ assertThat(sStats.getCurrentPageReturnedResultCount()).isEqualTo(2);
assertThat(sStats.isFirstPage()).isTrue();
- assertThat(sStats.getScoredDocumentCount()).isEqualTo(1);
+ assertThat(sStats.getRankingStrategy())
+ .isEqualTo(SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP);
+ assertThat(sStats.getScoredDocumentCount()).isEqualTo(2);
+ assertThat(sStats.getResultWithSnippetsCount()).isEqualTo(0);
}
@Test
- public void testLoggingStats_remove() throws Exception {
+ public void testLoggingStats_search_failure() throws Exception {
+ final String testPackageName = "testPackage";
+ final String testDatabase = "testDatabase";
+ List<AppSearchSchema> schemas =
+ ImmutableList.of(
+ new AppSearchSchema.Builder("Type1").build(),
+ new AppSearchSchema.Builder("Type2").build());
+ mAppSearchImpl.setSchema(
+ testPackageName,
+ testDatabase,
+ schemas,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ SearchSpec searchSpec =
+ new SearchSpec.Builder()
+ .setTermMatch(TermMatchType.Code.PREFIX_VALUE)
+ .setRankingStrategy(SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP)
+ .addFilterPackageNames("anotherPackage")
+ .build();
+
+ mAppSearchImpl.query(
+ testPackageName,
+ testPackageName,
+ /* queryExpression= */ "",
+ searchSpec,
+ /*logger=*/ mLogger);
+
+ SearchStats sStats = mLogger.mSearchStats;
+ assertThat(sStats).isNotNull();
+ assertThat(sStats.getPackageName()).isEqualTo(testPackageName);
+ assertThat(sStats.getDatabase()).isEqualTo(testPackageName);
+ assertThat(sStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR);
+ }
+
+ @Test
+ public void testLoggingStats_remove_success() throws Exception {
// Insert schema
final String testPackageName = "testPackage";
final String testDatabase = "testDatabase";
@@ -401,8 +630,9 @@
testPackageName,
testDatabase,
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
GenericDocument document =
@@ -416,12 +646,59 @@
assertThat(rStats.getPackageName()).isEqualTo(testPackageName);
assertThat(rStats.getDatabase()).isEqualTo(testDatabase);
// delete by namespace + id
+ assertThat(rStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
assertThat(rStats.getDeleteType()).isEqualTo(DeleteStatsProto.DeleteType.Code.SINGLE_VALUE);
assertThat(rStats.getDeletedDocumentCount()).isEqualTo(1);
}
@Test
- public void testLoggingStats_removeByQuery() throws Exception {
+ public void testLoggingStats_remove_failure() throws Exception {
+ // Insert schema
+ final String testPackageName = "testPackage";
+ final String testDatabase = "testDatabase";
+ final String testNamespace = "testNameSpace";
+ final String testId = "id";
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ mAppSearchImpl.setSchema(
+ testPackageName,
+ testDatabase,
+ schemas,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ GenericDocument document =
+ new GenericDocument.Builder<>(testNamespace, testId, "type").build();
+ mAppSearchImpl.putDocument(testPackageName, testDatabase, document, /*logger=*/ null);
+
+ RemoveStats.Builder rStatsBuilder = new RemoveStats.Builder(testPackageName, testDatabase);
+
+ // We mainly want to check the status code in stats. So we don't need to inspect the
+ // exception here.
+ Assert.assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.remove(
+ testPackageName,
+ testDatabase,
+ testNamespace,
+ "invalidId",
+ rStatsBuilder));
+
+ RemoveStats rStats = rStatsBuilder.build();
+ assertThat(rStats.getPackageName()).isEqualTo(testPackageName);
+ assertThat(rStats.getDatabase()).isEqualTo(testDatabase);
+ assertThat(rStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_NOT_FOUND);
+ // delete by namespace + id
+ assertThat(rStats.getDeleteType()).isEqualTo(DeleteStatsProto.DeleteType.Code.SINGLE_VALUE);
+ assertThat(rStats.getDeletedDocumentCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void testLoggingStats_removeByQuery_success() throws Exception {
// Insert schema
final String testPackageName = "testPackage";
final String testDatabase = "testDatabase";
@@ -432,8 +709,9 @@
testPackageName,
testDatabase,
schemas,
- /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
- /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
GenericDocument document1 =
@@ -453,6 +731,7 @@
assertThat(rStats.getPackageName()).isEqualTo(testPackageName);
assertThat(rStats.getDatabase()).isEqualTo(testDatabase);
+ assertThat(rStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
// delete by query
assertThat(rStats.getDeleteType()).isEqualTo(DeleteStatsProto.DeleteType.Code.QUERY_VALUE);
assertThat(rStats.getDeletedDocumentCount()).isEqualTo(2);
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/FrameworkOptimizeStrategyTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/FrameworkOptimizeStrategyTest.java
index f30cbb8..de71d21 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/FrameworkOptimizeStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/FrameworkOptimizeStrategyTest.java
@@ -22,8 +22,8 @@
import static com.google.common.truth.Truth.assertThat;
-import com.android.server.appsearch.proto.GetOptimizeInfoResultProto;
-import com.android.server.appsearch.proto.StatusProto;
+import com.android.server.appsearch.icing.proto.GetOptimizeInfoResultProto;
+import com.android.server.appsearch.icing.proto.StatusProto;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
index ada49ff..204fc54 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java
@@ -20,10 +20,10 @@
import android.app.appsearch.GenericDocument;
-import com.android.server.appsearch.proto.DocumentProto;
-import com.android.server.appsearch.proto.PropertyConfigProto;
-import com.android.server.appsearch.proto.PropertyProto;
-import com.android.server.appsearch.proto.SchemaTypeConfigProto;
+import com.android.server.appsearch.icing.proto.DocumentProto;
+import com.android.server.appsearch.icing.proto.PropertyConfigProto;
+import com.android.server.appsearch.icing.proto.PropertyProto;
+import com.android.server.appsearch.icing.proto.SchemaTypeConfigProto;
import com.android.server.appsearch.protobuf.ByteString;
import com.google.common.collect.ImmutableMap;
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
index 77e0135..ebceba4 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
@@ -20,10 +20,10 @@
import android.app.appsearch.AppSearchSchema;
-import com.android.server.appsearch.proto.PropertyConfigProto;
-import com.android.server.appsearch.proto.SchemaTypeConfigProto;
-import com.android.server.appsearch.proto.StringIndexingConfig;
-import com.android.server.appsearch.proto.TermMatchType;
+import com.android.server.appsearch.icing.proto.PropertyConfigProto;
+import com.android.server.appsearch.icing.proto.SchemaTypeConfigProto;
+import com.android.server.appsearch.icing.proto.StringIndexingConfig;
+import com.android.server.appsearch.icing.proto.TermMatchType;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
index d3feb12..992961c 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java
@@ -22,12 +22,12 @@
import android.app.appsearch.SearchResultPage;
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
-import com.android.server.appsearch.proto.DocumentProto;
-import com.android.server.appsearch.proto.PropertyProto;
-import com.android.server.appsearch.proto.SchemaTypeConfigProto;
-import com.android.server.appsearch.proto.SearchResultProto;
-import com.android.server.appsearch.proto.SnippetMatchProto;
-import com.android.server.appsearch.proto.SnippetProto;
+import com.android.server.appsearch.icing.proto.DocumentProto;
+import com.android.server.appsearch.icing.proto.PropertyProto;
+import com.android.server.appsearch.icing.proto.SchemaTypeConfigProto;
+import com.android.server.appsearch.icing.proto.SearchResultProto;
+import com.android.server.appsearch.icing.proto.SnippetMatchProto;
+import com.android.server.appsearch.icing.proto.SnippetProto;
import org.junit.Test;
@@ -46,7 +46,6 @@
PREFIX,
Collections.singletonMap(PREFIX + SCHEMA_TYPE, SCHEMA_TYPE_CONFIG_PROTO));
- // TODO(tytytyww): Add tests for Double and Long Snippets.
@Test
public void testSingleStringSnippet() {
final String propertyKeyString = "content";
@@ -112,7 +111,6 @@
assertThat(match.getSnippet()).isEqualTo(window);
}
- // TODO(tytytyww): Add tests for Double and Long Snippets.
@Test
public void testNoSnippets() {
final String propertyKeyString = "content";
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 6d90686..57d9941 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
@@ -254,14 +254,25 @@
@Test
public void testAppSearchStats_SetSchemaStats() {
+ SchemaMigrationStats schemaMigrationStats =
+ new SchemaMigrationStats.Builder()
+ .setGetSchemaLatencyMillis(1)
+ .setQueryAndTransformLatencyMillis(2)
+ .setFirstSetSchemaLatencyMillis(3)
+ .setSecondSetSchemaLatencyMillis(4)
+ .setSaveDocumentLatencyMillis(5)
+ .setMigratedDocumentCount(6)
+ .setSavedDocumentCount(7)
+ .build();
int nativeLatencyMillis = 1;
int newTypeCount = 2;
int compatibleTypeChangeCount = 3;
int indexIncompatibleTypeChangeCount = 4;
int backwardsIncompatibleTypeChangeCount = 5;
- final SetSchemaStats sStats =
+ SetSchemaStats sStats =
new SetSchemaStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE)
.setStatusCode(TEST_STATUS_CODE)
+ .setSchemaMigrationStats(schemaMigrationStats)
.setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
.setNativeLatencyMillis(nativeLatencyMillis)
.setNewTypeCount(newTypeCount)
@@ -274,6 +285,7 @@
assertThat(sStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
assertThat(sStats.getDatabase()).isEqualTo(TEST_DATA_BASE);
assertThat(sStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
+ assertThat(sStats.getSchemaMigrationStats()).isEqualTo(schemaMigrationStats);
assertThat(sStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
assertThat(sStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
assertThat(sStats.getNewTypeCount()).isEqualTo(newTypeCount);
@@ -285,6 +297,35 @@
}
@Test
+ public void testAppSearchStats_SchemaMigrationStats() {
+ int getSchemaLatency = 1;
+ int queryAndTransformLatency = 2;
+ int firstSetSchemaLatency = 3;
+ int secondSetSchemaLatency = 4;
+ int saveDocumentLatency = 5;
+ int migratedDocumentCount = 6;
+ int savedDocumentCount = 7;
+ SchemaMigrationStats sStats =
+ new SchemaMigrationStats.Builder()
+ .setGetSchemaLatencyMillis(getSchemaLatency)
+ .setQueryAndTransformLatencyMillis(queryAndTransformLatency)
+ .setFirstSetSchemaLatencyMillis(firstSetSchemaLatency)
+ .setSecondSetSchemaLatencyMillis(secondSetSchemaLatency)
+ .setSaveDocumentLatencyMillis(saveDocumentLatency)
+ .setMigratedDocumentCount(migratedDocumentCount)
+ .setSavedDocumentCount(savedDocumentCount)
+ .build();
+
+ assertThat(sStats.getGetSchemaLatencyMillis()).isEqualTo(getSchemaLatency);
+ assertThat(sStats.getQueryAndTransformLatencyMillis()).isEqualTo(queryAndTransformLatency);
+ assertThat(sStats.getFirstSetSchemaLatencyMillis()).isEqualTo(firstSetSchemaLatency);
+ assertThat(sStats.getSecondSetSchemaLatencyMillis()).isEqualTo(secondSetSchemaLatency);
+ assertThat(sStats.getSaveDocumentLatencyMillis()).isEqualTo(saveDocumentLatency);
+ assertThat(sStats.getMigratedDocumentCount()).isEqualTo(migratedDocumentCount);
+ assertThat(sStats.getSavedDocumentCount()).isEqualTo(savedDocumentCount);
+ }
+
+ @Test
public void testAppSearchStats_RemoveStats() {
int nativeLatencyMillis = 1;
@RemoveStats.DeleteType int deleteType = 2;
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
index 7c275e1..ec96d6a 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java
@@ -65,13 +65,8 @@
Context context = ApplicationProvider.getApplicationContext();
mContext = new ContextWrapper(context) {
@Override
- public Context createContextAsUser(UserHandle user, int flags) {
- return new ContextWrapper(super.createContextAsUser(user, flags)) {
- @Override
- public PackageManager getPackageManager() {
- return getMockPackageManager(user);
- }
- };
+ public PackageManager getPackageManager() {
+ return getMockPackageManager(mContext.getUser());
}
};
}
@@ -153,7 +148,6 @@
final int testUid = 1234;
PlatformLogger logger = new PlatformLogger(
mContext,
- mContext.getUser(),
AppSearchConfig.create(DIRECT_EXECUTOR));
PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
when(mockPackageManager.getPackageUid(testPackageName, /*flags=*/0)).thenReturn(testUid);
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java b/services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java
similarity index 71%
rename from services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java
rename to services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java
index 4faffc0..07a728b 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java
@@ -14,10 +14,7 @@
* limitations under the License.
*/
-// TODO(b/169883602): This is purposely a different package from the path so that it can access
-// AppSearchImpl methods without having to make methods public. This should be moved into a proper
-// package once AppSearchImpl-VisibilityStore's dependencies are refactored.
-package com.android.server.appsearch.external.localstorage;
+package com.android.server.appsearch.visibilitystore;
import static android.Manifest.permission.READ_GLOBAL_APP_SEARCH_DATA;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
@@ -39,8 +36,10 @@
import androidx.test.core.app.ApplicationProvider;
+import com.android.server.appsearch.external.localstorage.AppSearchImpl;
+import com.android.server.appsearch.external.localstorage.OptimizeStrategy;
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
-import com.android.server.appsearch.visibilitystore.VisibilityStore;
+import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -55,7 +54,7 @@
import java.util.Collections;
import java.util.Map;
-public class VisibilityStoreTest {
+public class VisibilityStoreImplTest {
/**
* Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class.
*/
@@ -64,7 +63,7 @@
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
private final Map<UserHandle, PackageManager> mMockPackageManagers = new ArrayMap<>();
private Context mContext;
- private VisibilityStore mVisibilityStore;
+ private VisibilityStoreImpl mVisibilityStore;
private int mUid;
@Before
@@ -88,15 +87,10 @@
};
// Give ourselves global query permissions
- AppSearchImpl appSearchImpl =
- AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- mContext,
- /*logger=*/ null,
- ALWAYS_OPTIMIZE);
-
+ AppSearchImpl appSearchImpl = AppSearchImpl.create(
+ mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ mVisibilityStore = VisibilityStoreImpl.create(appSearchImpl, mContext);
mUid = mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
- mVisibilityStore = appSearchImpl.getVisibilityStoreLocked();
}
/**
@@ -122,34 +116,50 @@
}
@Test
- public void testSetVisibility_platformSurfaceable() throws Exception {
+ public void testDoesCallerHaveSystemAccess() {
+ PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
+ when(mockPackageManager
+ .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
+ .thenReturn(PERMISSION_GRANTED);
+ assertThat(mVisibilityStore.doesCallerHaveSystemAccess(mContext.getPackageName())).isTrue();
+
+ when(mockPackageManager
+ .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
+ .thenReturn(PERMISSION_DENIED);
+ assertThat(mVisibilityStore.doesCallerHaveSystemAccess(mContext.getPackageName()))
+ .isFalse();
+ }
+
+ @Test
+ public void testSetVisibility_displayedBySystem() throws Exception {
// Make sure we have global query privileges
PackageManager mockPackageManager = getMockPackageManager(mContext.getUser());
when(mockPackageManager
.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName()))
.thenReturn(PERMISSION_GRANTED);
+ assertThat(mVisibilityStore.doesCallerHaveSystemAccess(mContext.getPackageName())).isTrue();
mVisibilityStore.setVisibility(
"package",
"database",
- /*schemasNotPlatformSurfaceable=*/ ImmutableSet.of(
+ /*schemasNotDisplayedBySystem=*/ ImmutableSet.of(
"prefix/schema1", "prefix/schema2"),
- /*schemasPackageAccessible=*/ Collections.emptyMap());
+ /*schemasVisibleToPackages=*/ Collections.emptyMap());
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"package",
"database",
"prefix/schema1",
- mContext.getPackageName(),
- mUid))
+ mUid,
+ /*callerHasSystemAccess=*/ true))
.isFalse();
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"package",
"database",
"prefix/schema2",
- mContext.getPackageName(),
- mUid))
+ mUid,
+ /*callerHasSystemAccess=*/ true))
.isFalse();
// New .setVisibility() call completely overrides previous visibility settings.
@@ -157,68 +167,68 @@
mVisibilityStore.setVisibility(
"package",
"database",
- /*schemasNotPlatformSurfaceable=*/ ImmutableSet.of(
+ /*schemasNotDisplayedBySystem=*/ ImmutableSet.of(
"prefix/schema1", "prefix/schema3"),
- /*schemasPackageAccessible=*/ Collections.emptyMap());
+ /*schemasVisibleToPackages=*/ Collections.emptyMap());
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"package",
"database",
"prefix/schema1",
- mContext.getPackageName(),
- mUid))
+ mUid,
+ /*callerHasSystemAccess=*/ true))
.isFalse();
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"package",
"database",
"prefix/schema2",
- mContext.getPackageName(),
- mUid))
+ mUid,
+ /*callerHasSystemAccess=*/ true))
.isTrue();
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"package",
"database",
"prefix/schema3",
- mContext.getPackageName(),
- mUid))
+ mUid,
+ /*callerHasSystemAccess=*/ true))
.isFalse();
// Everything defaults to visible again.
mVisibilityStore.setVisibility(
"package",
"database",
- /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
- /*schemasPackageAccessible=*/ Collections.emptyMap());
+ /*schemasNotDisplayedBySystem=*/ Collections.emptySet(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap());
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"package",
"database",
"prefix/schema1",
- mContext.getPackageName(),
- mUid))
+ mUid,
+ /*callerHasSystemAccess=*/ true))
.isTrue();
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"package",
"database",
"prefix/schema2",
- mContext.getPackageName(),
- mUid))
+ mUid,
+ /*callerHasSystemAccess=*/ true))
.isTrue();
assertThat(
mVisibilityStore.isSchemaSearchableByCaller(
"package",
"database",
"prefix/schema3",
- mContext.getPackageName(),
- mUid))
+ mUid,
+ /*callerHasSystemAccess=*/ true))
.isTrue();
}
@Test
- public void testSetVisibility_packageAccessible() throws Exception {
+ public void testSetVisibility_visibleToPackages() throws Exception {
// Values for a "foo" client
String packageNameFoo = "packageFoo";
byte[] sha256CertFoo = new byte[] {10};
@@ -242,21 +252,27 @@
.thenReturn(PERMISSION_DENIED);
// By default, a schema isn't package accessible.
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ "prefix/schemaFoo",
+ uidFoo,
+ /*callerHasSystemAccess=*/ false))
.isFalse();
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package", "database", "prefix/schemaBar", packageNameBar, uidBar))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ "prefix/schemaBar",
+ uidBar,
+ /*callerHasSystemAccess=*/ false))
.isFalse();
// Grant package access
mVisibilityStore.setVisibility(
"package",
"database",
- /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
- /*schemasPackageAccessible=*/ ImmutableMap.of(
+ /*schemasNotDisplayedBySystem=*/ Collections.emptySet(),
+ /*schemasVisibleToPackages=*/ ImmutableMap.of(
"prefix/schemaFoo",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo)),
"prefix/schemaBar",
@@ -268,9 +284,12 @@
when(mockPackageManager.hasSigningCertificate(
packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
.thenReturn(false);
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ "prefix/schemaFoo",
+ uidFoo,
+ /*callerHasSystemAccess=*/ false))
.isFalse();
// Should fail if PackageManager doesn't think the package belongs to the uid
@@ -279,9 +298,12 @@
when(mockPackageManager.hasSigningCertificate(
packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
.thenReturn(true);
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ "prefix/schemaFoo",
+ uidFoo,
+ /*callerHasSystemAccess=*/ false))
.isFalse();
// But if uid and certificate match, then we should have access
@@ -290,9 +312,12 @@
when(mockPackageManager.hasSigningCertificate(
packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
.thenReturn(true);
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ "prefix/schemaFoo",
+ uidFoo,
+ /*callerHasSystemAccess=*/ false))
.isTrue();
when(mockPackageManager.getPackageUid(eq(packageNameBar), /*flags=*/ anyInt()))
@@ -300,9 +325,12 @@
when(mockPackageManager.hasSigningCertificate(
packageNameBar, sha256CertBar, PackageManager.CERT_INPUT_SHA256))
.thenReturn(true);
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package", "database", "prefix/schemaBar", packageNameBar, uidBar))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ "prefix/schemaBar",
+ uidBar,
+ /*callerHasSystemAccess=*/ false))
.isTrue();
// New .setVisibility() call completely overrides previous visibility settings. So
@@ -310,8 +338,8 @@
mVisibilityStore.setVisibility(
"package",
"database",
- /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
- /*schemasPackageAccessible=*/ ImmutableMap.of(
+ /*schemasNotDisplayedBySystem=*/ Collections.emptySet(),
+ /*schemasVisibleToPackages=*/ ImmutableMap.of(
"prefix/schemaFoo",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))));
@@ -320,9 +348,12 @@
when(mockPackageManager.hasSigningCertificate(
packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256))
.thenReturn(true);
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ "prefix/schemaFoo",
+ uidFoo,
+ /*callerHasSystemAccess=*/ false))
.isTrue();
when(mockPackageManager.getPackageUid(eq(packageNameBar), /*flags=*/ anyInt()))
@@ -330,9 +361,12 @@
when(mockPackageManager.hasSigningCertificate(
packageNameBar, sha256CertBar, PackageManager.CERT_INPUT_SHA256))
.thenReturn(true);
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package", "database", "prefix/schemaBar", packageNameBar, uidBar))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ "prefix/schemaBar",
+ uidBar,
+ /*callerHasSystemAccess=*/ false))
.isFalse();
}
@@ -357,15 +391,18 @@
mVisibilityStore.setVisibility(
"package",
"database",
- /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
- /*schemasPackageAccessible=*/ ImmutableMap.of(
+ /*schemasNotDisplayedBySystem=*/ Collections.emptySet(),
+ /*schemasVisibleToPackages=*/ ImmutableMap.of(
"prefix/schemaFoo",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))));
// If we can't verify the Foo package that has access, assume it doesn't have access.
- assertThat(
- mVisibilityStore.isSchemaSearchableByCaller(
- "package", "database", "prefix/schemaFoo", packageNameFoo, uidFoo))
+ assertThat(mVisibilityStore.isSchemaSearchableByCaller(
+ "package",
+ "database",
+ "prefix/schemaFoo",
+ uidFoo,
+ /*callerHasSystemAccess=*/ false))
.isFalse();
}
@@ -387,8 +424,8 @@
mVisibilityStore.setVisibility(
/*packageName=*/ "",
/*databaseName=*/ "",
- /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
- /*schemasPackageAccessible=*/ ImmutableMap.of(
+ /*schemasNotDisplayedBySystem=*/ Collections.emptySet(),
+ /*schemasVisibleToPackages=*/ ImmutableMap.of(
"schema",
ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))));
@@ -397,8 +434,8 @@
/*packageName=*/ "",
/*databaseName=*/ "",
"schema",
- mContext.getPackageName(),
- mUid))
+ mUid,
+ /*callerHasSystemAccess=*/ true))
.isTrue();
when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt()))
@@ -411,8 +448,8 @@
/*packageName=*/ "",
/*databaseName=*/ "",
"schema",
- packageNameFoo,
- uidFoo))
+ uidFoo,
+ /*callerHasSystemAccess=*/ false))
.isTrue();
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index fa3f45c..a2b1c1c 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -34,6 +34,7 @@
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DevicePolicyManagerLiteInternal;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -158,6 +159,7 @@
final long ident = mContext.binder.clearCallingIdentity();
try {
+ LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
@@ -271,6 +273,7 @@
final long ident = mContext.binder.clearCallingIdentity();
try {
+ LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
@@ -339,6 +342,7 @@
// (Need clearCallingIdentity() to pass permission checks.)
final long ident = mContext.binder.clearCallingIdentity();
try {
+ LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
@@ -499,6 +503,7 @@
DevicePolicyManagerServiceTestable dpms;
final long ident = mContext.binder.clearCallingIdentity();
try {
+ LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 5447a58..cedf636 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -84,6 +84,7 @@
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.admin.DevicePolicyManagerLiteInternal;
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.PasswordMetrics;
import android.app.admin.SystemUpdatePolicy;
@@ -280,6 +281,7 @@
private void initializeDpms() {
// Need clearCallingIdentity() to pass permission checks.
final long ident = mContext.binder.clearCallingIdentity();
+ LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
@@ -369,10 +371,14 @@
.thenReturn(false);
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+ LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class);
new DevicePolicyManagerServiceTestable(getServices(), mContext);
// If the device has no DPMS feature, it shouldn't register the local service.
assertThat(LocalServices.getService(DevicePolicyManagerInternal.class)).isNull();
+
+ // But should still register the lite one
+ assertThat(LocalServices.getService(DevicePolicyManagerLiteInternal.class)).isNotNull();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index f156779..5ba375b 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -64,6 +64,7 @@
import com.android.server.SystemService;
import com.android.server.display.DisplayManagerService.SyncRoot;
import com.android.server.lights.LightsManager;
+import com.android.server.sensors.SensorManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
@@ -150,6 +151,7 @@
@Mock LightsManager mMockLightsManager;
@Mock VirtualDisplayAdapter mMockVirtualDisplayAdapter;
@Mock IBinder mMockDisplayToken;
+ @Mock SensorManagerInternal mMockSensorManagerInternal;
@Before
public void setUp() throws Exception {
@@ -161,6 +163,8 @@
LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerInternal);
LocalServices.removeServiceForTest(LightsManager.class);
LocalServices.addService(LightsManager.class, mMockLightsManager);
+ LocalServices.removeServiceForTest(SensorManagerInternal.class);
+ LocalServices.addService(SensorManagerInternal.class, mMockSensorManagerInternal);
mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 918979c..cae6c86 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -48,7 +48,12 @@
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.hardware.display.BrightnessInfo;
import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayManagerInternal.RefreshRateLimitation;
+import android.hardware.display.DisplayManagerInternal.RefreshRateRange;
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.os.Handler;
import android.os.Looper;
@@ -70,6 +75,8 @@
import com.android.server.display.DisplayModeDirector.BrightnessObserver;
import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs;
import com.android.server.display.DisplayModeDirector.Vote;
+import com.android.server.sensors.SensorManagerInternal;
+import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.testutils.FakeDeviceConfigInterface;
@@ -105,6 +112,10 @@
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@Mock
public StatusBarManagerInternal mStatusBarMock;
+ @Mock
+ public SensorManagerInternal mSensorManagerInternalMock;
+ @Mock
+ public DisplayManagerInternal mDisplayManagerInternalMock;
@Before
public void setUp() throws Exception {
@@ -112,11 +123,15 @@
mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext);
when(mContext.getContentResolver()).thenReturn(resolver);
- mInjector = new FakesInjector();
+ mInjector = spy(new FakesInjector());
mHandler = new Handler(Looper.getMainLooper());
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
LocalServices.addService(StatusBarManagerInternal.class, mStatusBarMock);
+ LocalServices.removeServiceForTest(SensorManagerInternal.class);
+ LocalServices.addService(SensorManagerInternal.class, mSensorManagerInternalMock);
+ LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+ LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
}
private DisplayModeDirector createDirectorFromRefreshRateArray(
@@ -1214,14 +1229,193 @@
assertThat(desiredSpecs.baseModeId).isEqualTo(8);
}
+ @Test
+ public void testProximitySensorVoting() throws Exception {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+ director.start(createMockSensorManager());
+
+ ArgumentCaptor<ProximityActiveListener> captor =
+ ArgumentCaptor.forClass(ProximityActiveListener.class);
+ verify(mSensorManagerInternalMock).addProximityActiveListener(any(Executor.class),
+ captor.capture());
+ ProximityActiveListener listener = captor.getValue();
+
+ // Verify that there is no proximity vote initially
+ Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
+ assertNull(vote);
+
+ when(mDisplayManagerInternalMock.getRefreshRateForDisplayAndSensor(eq(DISPLAY_ID), eq(null),
+ eq(Sensor.STRING_TYPE_PROXIMITY))).thenReturn(new RefreshRateRange(60, 60));
+
+ // Set the proximity to active and verify that we added a vote.
+ listener.onProximityActive(true);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
+ assertVoteForRefreshRate(vote, 60.f);
+
+ // Turn prox off and verify vote is gone.
+ listener.onProximityActive(false);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY);
+ assertNull(vote);
+ }
+
+ @Test
+ public void testHbmVoting_forHdr() {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
+ director.start(createMockSensorManager());
+
+ ArgumentCaptor<DisplayListener> captor =
+ ArgumentCaptor.forClass(DisplayListener.class);
+ verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+ | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+ DisplayListener listener = captor.getValue();
+
+ // Specify Limitation
+ when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn(
+ List.of(new RefreshRateLimitation(
+ DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
+ 60.f, 60.f)));
+
+ // Verify that there is no HBM vote initially
+ Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+
+ // Turn on HBM
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertVoteForRefreshRate(vote, 60.f);
+
+ // Turn off HBM
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+ }
+
+ @Test
+ public void testHbmVoting_forSunlight() {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
+ director.start(createMockSensorManager());
+
+ ArgumentCaptor<DisplayListener> captor =
+ ArgumentCaptor.forClass(DisplayListener.class);
+ verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+ | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+ DisplayListener listener = captor.getValue();
+
+ // Specify Limitation
+ when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn(
+ List.of(new RefreshRateLimitation(
+ DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
+ 60.f, 60.f)));
+
+ // Verify that there is no HBM vote initially
+ Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+
+ // Turn on HBM
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertVoteForRefreshRate(vote, 60.f);
+
+ // Turn off HBM
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+ }
+
+ @Test
+ public void testHbmVoting_forSunlight_NoLimitation() {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
+ director.start(createMockSensorManager());
+
+ ArgumentCaptor<DisplayListener> captor =
+ ArgumentCaptor.forClass(DisplayListener.class);
+ verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+ | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+ DisplayListener listener = captor.getValue();
+
+ // Specify Limitation for different display
+ when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID + 1)).thenReturn(
+ List.of(new RefreshRateLimitation(
+ DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
+ 60.f, 60.f)));
+
+ // Verify that there is no HBM vote initially
+ Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+
+ // Turn on HBM
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+
+ // Turn off HBM
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+ }
+
+ @Test
+ public void testHbmVoting_RemovedDisplay() {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
+ director.start(createMockSensorManager());
+
+ ArgumentCaptor<DisplayListener> captor =
+ ArgumentCaptor.forClass(DisplayListener.class);
+ verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+ | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+ DisplayListener listener = captor.getValue();
+
+ // Specify Limitation for different display
+ when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn(
+ List.of(new RefreshRateLimitation(
+ DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
+ 60.f, 60.f)));
+
+ // Verify that there is no HBM vote initially
+ Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+
+ // Turn on HBM
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertVoteForRefreshRate(vote, 60.f);
+
+ // Turn off HBM
+ listener.onDisplayRemoved(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+ }
+
private void assertVoteForRefreshRate(Vote vote, float refreshRate) {
assertThat(vote).isNotNull();
- final DisplayModeDirector.RefreshRateRange expectedRange =
- new DisplayModeDirector.RefreshRateRange(refreshRate, refreshRate);
+ final RefreshRateRange expectedRange = new RefreshRateRange(refreshRate, refreshRate);
assertThat(vote.refreshRateRange).isEqualTo(expectedRange);
}
- private static class FakeDeviceConfig extends FakeDeviceConfigInterface {
+ public static class FakeDeviceConfig extends FakeDeviceConfigInterface {
@Override
public String getProperty(String namespace, String name) {
Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace));
@@ -1362,7 +1556,7 @@
mHandler.runWithScissors(() -> { }, 500 /*timeout*/);
}
- static class FakesInjector implements DisplayModeDirector.Injector {
+ public static class FakesInjector implements DisplayModeDirector.Injector {
private final FakeDeviceConfig mDeviceConfig;
private ContentObserver mBrightnessObserver;
private ContentObserver mPeakRefreshRateObserver;
@@ -1403,6 +1597,14 @@
mPeakRefreshRateObserver = observer;
}
+ @Override
+ public void registerDisplayListener(DisplayListener listener, Handler handler, long flag) {}
+
+ @Override
+ public BrightnessInfo getBrightnessInfo(int displayId) {
+ return null;
+ }
+
void notifyPeakRefreshRateChanged() {
if (mPeakRefreshRateObserver != null) {
mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 0cf212c..f53ae52 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -40,6 +40,7 @@
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
+import android.hardware.hdmi.IHdmiControlStatusChangeListener;
import android.os.Binder;
import android.os.IPowerManager;
import android.os.IThermalService;
@@ -683,6 +684,100 @@
HdmiControlManager.HDMI_CEC_VERSION_2_0);
}
+ @Test
+ public void initCec_statusListener_CecDisabled() {
+ HdmiControlStatusCallback hdmiControlStatusCallback = new HdmiControlStatusCallback();
+
+ mHdmiControlServiceSpy.addHdmiControlStatusChangeListener(hdmiControlStatusCallback);
+
+ mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+ HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
+ mTestLooper.dispatchAll();
+
+ assertThat(hdmiControlStatusCallback.mCecEnabled).isFalse();
+ assertThat(hdmiControlStatusCallback.mCecAvailable).isFalse();
+ }
+
+ @Test
+ public void initCec_statusListener_CecEnabled_NoCecResponse() {
+ HdmiControlStatusCallback hdmiControlStatusCallback = new HdmiControlStatusCallback();
+
+ mHdmiControlServiceSpy.addHdmiControlStatusChangeListener(hdmiControlStatusCallback);
+
+ mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+ HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
+ mTestLooper.dispatchAll();
+ mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+ HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
+ mTestLooper.dispatchAll();
+ // Hit timeout twice due to retries
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(hdmiControlStatusCallback.mCecEnabled).isTrue();
+ assertThat(hdmiControlStatusCallback.mCecAvailable).isFalse();
+ }
+
+ @Test
+ public void initCec_statusListener_CecEnabled_CecAvailable_TvOn() {
+ HdmiControlStatusCallback hdmiControlStatusCallback = new HdmiControlStatusCallback();
+ mHdmiControlServiceSpy.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
+ mTestLooper.dispatchAll();
+
+ mHdmiControlServiceSpy.addHdmiControlStatusChangeListener(hdmiControlStatusCallback);
+ mHdmiControlServiceSpy.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
+ mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
+ Constants.ADDR_TV,
+ mHdmiControlServiceSpy.playback().mAddress, HdmiControlManager.POWER_STATUS_ON);
+ mNativeWrapper.onCecMessage(reportPowerStatus);
+ mTestLooper.dispatchAll();
+
+ assertThat(hdmiControlStatusCallback.mCecEnabled).isTrue();
+ assertThat(hdmiControlStatusCallback.mCecAvailable).isTrue();
+ }
+
+ @Test
+ public void initCec_statusListener_CecEnabled_CecAvailable_TvStandby() {
+ HdmiControlStatusCallback hdmiControlStatusCallback = new HdmiControlStatusCallback();
+ mHdmiControlServiceSpy.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
+ mTestLooper.dispatchAll();
+
+ mHdmiControlServiceSpy.addHdmiControlStatusChangeListener(hdmiControlStatusCallback);
+ mHdmiControlServiceSpy.setControlEnabled(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
+ mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage reportPowerStatus = HdmiCecMessageBuilder.buildReportPowerStatus(
+ Constants.ADDR_TV,
+ mHdmiControlServiceSpy.playback().mAddress,
+ HdmiControlManager.POWER_STATUS_STANDBY);
+ mNativeWrapper.onCecMessage(reportPowerStatus);
+ mTestLooper.dispatchAll();
+
+ assertThat(hdmiControlStatusCallback.mCecEnabled).isTrue();
+ assertThat(hdmiControlStatusCallback.mCecAvailable).isTrue();
+ }
+
+ private static class HdmiControlStatusCallback extends IHdmiControlStatusChangeListener.Stub {
+ boolean mCecEnabled = false;
+ boolean mCecAvailable = false;
+
+ @Override
+ public void onStatusChange(int isCecEnabled, boolean isCecAvailable)
+ throws RemoteException {
+ mCecEnabled = isCecEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED;
+ mCecAvailable = isCecAvailable;
+ }
+ }
+
private static class VolumeControlFeatureCallback extends
IHdmiCecVolumeControlFeatureListener.Stub {
boolean mCallbackReceived = false;
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 2efebbf..8eb3cf3 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -4,7 +4,9 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyString;
@@ -102,6 +104,20 @@
}
@Test
+ public void testStringToIntArrayAndIntArrayToString() {
+ final int[] netCapabilitiesIntArray = { 1, 3, 5, 7, 9 };
+ final String netCapabilitiesStr = "1,3,5,7,9";
+ final String netCapabilitiesStrWithErrorInt = "1,3,a,7,9";
+ final String emptyString = "";
+ final String str1 = JobStore.intArrayToString(netCapabilitiesIntArray);
+ assertArrayEquals(netCapabilitiesIntArray, JobStore.stringToIntArray(str1));
+ assertEquals(0, JobStore.stringToIntArray(emptyString).length);
+ assertThrows(NumberFormatException.class,
+ () -> JobStore.stringToIntArray(netCapabilitiesStrWithErrorInt));
+ assertEquals(netCapabilitiesStr, JobStore.intArrayToString(netCapabilitiesIntArray));
+ }
+
+ @Test
public void testMaybeWriteStatusToDisk() throws Exception {
int taskId = 5;
long runByMillis = 20000L; // 20s
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 67dd055..dc745cd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -83,9 +83,17 @@
private static final int DUMMY_OVERLAY_APPID = 10756;
private static final int SYSTEM_USER = 0;
private static final int SECONDARY_USER = 10;
+ private static final int ADDED_USER = 11;
private static final int[] USER_ARRAY = {SYSTEM_USER, SECONDARY_USER};
- private static final UserInfo[] USER_INFO_LIST = Arrays.stream(USER_ARRAY).mapToObj(
- id -> new UserInfo(id, Integer.toString(id), 0)).toArray(UserInfo[]::new);
+ private static final int[] USER_ARRAY_WITH_ADDED = {SYSTEM_USER, SECONDARY_USER, ADDED_USER};
+ private static final UserInfo[] USER_INFO_LIST = toUserInfos(USER_ARRAY);
+ private static final UserInfo[] USER_INFO_LIST_WITH_ADDED = toUserInfos(USER_ARRAY_WITH_ADDED);
+
+ private static UserInfo[] toUserInfos(int[] userIds) {
+ return Arrays.stream(userIds)
+ .mapToObj(id -> new UserInfo(id, Integer.toString(id), 0))
+ .toArray(UserInfo[]::new);
+ }
@Mock
AppsFilter.FeatureConfig mFeatureConfigMock;
@@ -319,6 +327,47 @@
}
@Test
+ public void testOnUserCreated_FilterMatches() throws Exception {
+ final AppsFilter appsFilter =
+ new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
+ mMockExecutor);
+ simulateAddBasicAndroid(appsFilter);
+
+ appsFilter.onSystemReady();
+
+ PackageSetting target = simulateAddPackage(appsFilter,
+ pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_APPID);
+ PackageSetting calling = simulateAddPackage(appsFilter,
+ pkgQueriesProvider("com.some.other.package", "com.some.authority"),
+ DUMMY_CALLING_APPID);
+
+ for (int subjectUserId : USER_ARRAY) {
+ for (int otherUserId : USER_ARRAY) {
+ assertFalse(appsFilter.shouldFilterApplication(
+ UserHandle.getUid(DUMMY_CALLING_APPID, subjectUserId), calling, target,
+ otherUserId));
+ }
+ }
+
+ // adds new user
+ doAnswer(invocation -> {
+ ((AppsFilter.StateProvider.CurrentStateCallback) invocation.getArgument(0))
+ .currentState(mExisting, USER_INFO_LIST_WITH_ADDED);
+ return new Object();
+ }).when(mStateProvider)
+ .runWithState(any(AppsFilter.StateProvider.CurrentStateCallback.class));
+ appsFilter.onUserCreated(ADDED_USER);
+
+ for (int subjectUserId : USER_ARRAY_WITH_ADDED) {
+ for (int otherUserId : USER_ARRAY_WITH_ADDED) {
+ assertFalse(appsFilter.shouldFilterApplication(
+ UserHandle.getUid(DUMMY_CALLING_APPID, subjectUserId), calling, target,
+ otherUserId));
+ }
+ }
+ }
+
+ @Test
public void testQueriesDifferentProvider_Filters() throws Exception {
final AppsFilter appsFilter =
new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 7df2dd6..c572dd6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -649,7 +649,7 @@
protected class MockAppSearchManager implements IAppSearchManager {
- protected Map<String, List<PackageIdentifier>> mSchemasPackageAccessible =
+ protected Map<String, List<PackageIdentifier>> mSchemasVisibleToPackages =
new ArrayMap<>(1);
private Map<String, Map<String, GenericDocument>> mDocumentMap = new ArrayMap<>(1);
@@ -659,19 +659,19 @@
@Override
public void setSchema(String packageName, String databaseName, List<Bundle> schemaBundles,
- List<String> schemasNotPlatformSurfaceable,
- Map<String, List<Bundle>> schemasPackageAccessibleBundles, boolean forceOverride,
+ List<String> schemasNotDisplayedBySystem,
+ Map<String, List<Bundle>> schemasVisibleToPackagesBundles, boolean forceOverride,
int version, UserHandle userHandle, long binderCallStartTimeMillis,
IAppSearchResultCallback callback) throws RemoteException {
for (Map.Entry<String, List<Bundle>> entry :
- schemasPackageAccessibleBundles.entrySet()) {
+ schemasVisibleToPackagesBundles.entrySet()) {
final String key = entry.getKey();
final List<PackageIdentifier> packageIdentifiers;
- if (!mSchemasPackageAccessible.containsKey(key)) {
+ if (!mSchemasVisibleToPackages.containsKey(key)) {
packageIdentifiers = new ArrayList<>(entry.getValue().size());
- mSchemasPackageAccessible.put(key, packageIdentifiers);
+ mSchemasVisibleToPackages.put(key, packageIdentifiers);
} else {
- packageIdentifiers = mSchemasPackageAccessible.get(key);
+ packageIdentifiers = mSchemasVisibleToPackages.get(key);
}
for (int i = 0; i < entry.getValue().size(); i++) {
packageIdentifiers.add(new PackageIdentifier(entry.getValue().get(i)));
@@ -785,7 +785,7 @@
}
@Override
- public void getNextPage(long nextPageToken, UserHandle userHandle,
+ public void getNextPage(String packageName, long nextPageToken, UserHandle userHandle,
IAppSearchResultCallback callback) throws RemoteException {
final Bundle page = new Bundle();
page.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, 1);
@@ -795,8 +795,8 @@
}
@Override
- public void invalidateNextPageToken(long nextPageToken, UserHandle userHandle)
- throws RemoteException {
+ public void invalidateNextPageToken(String packageName, long nextPageToken,
+ UserHandle userHandle) throws RemoteException {
}
@Override
@@ -875,13 +875,13 @@
}
@Override
- public void persistToDisk(UserHandle userHandle, long binderCallStartTimeMillis)
- throws RemoteException {
+ public void persistToDisk(String packageName, UserHandle userHandle,
+ long binderCallStartTimeMillis) throws RemoteException {
}
@Override
- public void initialize(UserHandle userHandle, long binderCallStartTimeMillis,
- IAppSearchResultCallback callback)
+ public void initialize(String packageName, UserHandle userHandle,
+ long binderCallStartTimeMillis, IAppSearchResultCallback callback)
throws RemoteException {
ignore(callback);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index 558fb30..976a588 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -18,7 +18,11 @@
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assert.fail;
+
import static java.lang.reflect.Modifier.isFinal;
+import static java.lang.reflect.Modifier.isPrivate;
+import static java.lang.reflect.Modifier.isProtected;
import static java.lang.reflect.Modifier.isPublic;
import static java.lang.reflect.Modifier.isStatic;
@@ -44,9 +48,12 @@
import java.io.File;
import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;
@@ -393,6 +400,178 @@
Assert.assertEquals(3600000003L, multiPackage[1].timeouts.maxPendingTimeUs);
}
+ // Report an error from the Computer structure validation test.
+ private void flag(String name, String msg) {
+ fail(name + " " + msg);
+ }
+
+ // Return a string that identifies a Method. This is not very efficient but it is not
+ // called very often.
+ private String displayName(Method m) {
+ String r = m.getName();
+ String p = Arrays.toString(m.getGenericParameterTypes())
+ .replaceAll("([a-zA-Z0-9]+\\.)+", "")
+ .replace("class ", "")
+ .replaceAll("^\\[", "(")
+ .replaceAll("\\]$", ")");
+ return r + p;
+ }
+
+ // Match a method to an array of Methods. Matching is on method signature: name and
+ // parameter types. If a method in the declared array matches, return it. Otherwise
+ // return null.
+ private Method matchMethod(Method m, Method[] declared) {
+ String n = m.getName();
+ Type[] t = m.getGenericParameterTypes();
+ for (int i = 0; i < declared.length; i++) {
+ Method l = declared[i];
+ if (l != null && l.getName().equals(n)
+ && Arrays.equals(l.getGenericParameterTypes(), t)) {
+ Method result = l;
+ // Set the method to null since it has been visited already.
+ declared[i] = null;
+ return result;
+ }
+ }
+ return null;
+ }
+
+ // Return the boolean locked value. A null return means the annotation was not
+ // found. This method will fail if the annotation is found but is not one of the
+ // known constants.
+ private Boolean getOverride(Method m) {
+ final String name = "Computer." + displayName(m);
+ final PackageManagerService.Computer.LiveImplementation annotation =
+ m.getAnnotation(PackageManagerService.Computer.LiveImplementation.class);
+ if (annotation == null) {
+ return null;
+ }
+ final int override = annotation.override();
+ if (override == PackageManagerService.Computer.LiveImplementation.MANDATORY) {
+ return true;
+ } else if (override == PackageManagerService.Computer.LiveImplementation.NOT_ALLOWED) {
+ return false;
+ } else {
+ flag(name, "invalid Live value: " + override);
+ return null;
+ }
+ }
+
+ @Test
+ public void testComputerStructure() {
+ // Verify that Copmuter methods are properly annotated and that ComputerLocked is
+ // properly populated per annotations.
+ // Call PackageManagerService.validateComputer();
+ Class base = PackageManagerService.Computer.class;
+
+ HashMap<Method, Boolean> methodType = new HashMap<>();
+
+ // Verify that all Computer methods are annotated and that the annotation
+ // parameter locked() is valid.
+ for (Method m : base.getDeclaredMethods()) {
+ final String name = "Computer." + displayName(m);
+ Boolean override = getOverride(m);
+ if (override == null) {
+ flag(name, "missing required Live annotation");
+ }
+ methodType.put(m, override);
+ }
+
+ Class coreClass = PackageManagerService.ComputerEngine.class;
+ final Method[] coreMethods = coreClass.getDeclaredMethods();
+
+ // Examine every method in the core. If it inherits from a base method it must be
+ // "public final" if the base is NOT_ALLOWED or "public" if the base is MANDATORY.
+ // If the core method does not inherit from the base then it must be either
+ // private or protected.
+ for (Method m : base.getDeclaredMethods()) {
+ String name = "Computer." + displayName(m);
+ final boolean locked = methodType.get(m);
+ final Method core = matchMethod(m, coreMethods);
+ if (core == null) {
+ flag(name, "not overridden in ComputerEngine");
+ continue;
+ }
+ name = "ComputerEngine." + displayName(m);
+ final int modifiers = core.getModifiers();
+ if (!locked) {
+ if (!isPublic(modifiers)) {
+ flag(name, "is not public");
+ }
+ if (!isFinal(modifiers)) {
+ flag(name, "is not final");
+ }
+ }
+ }
+ // Any methods left in the coreMethods array must be private or protected.
+ // Protected methods must be overridden (and final) in the live list.
+ Method[] coreHelpers = new Method[coreMethods.length];
+ int coreIndex = 0;
+ for (Method m : coreMethods) {
+ if (m != null) {
+ final String name = "ComputerEngine." + displayName(m);
+ final int modifiers = m.getModifiers();
+ if (isPrivate(modifiers)) {
+ // Okay
+ } else if (isProtected(modifiers)) {
+ coreHelpers[coreIndex++] = m;
+ } else {
+ flag(name, "is neither private nor protected");
+ }
+ }
+ }
+
+ Class liveClass = PackageManagerService.ComputerLocked.class;
+ final Method[] liveMethods = liveClass.getDeclaredMethods();
+
+ // Examine every method in the live list. Every method must be final and must
+ // inherit either from base or core. If the method inherits from a base method
+ // then the base must be MANDATORY.
+ for (Method m : base.getDeclaredMethods()) {
+ String name = "Computer." + displayName(m);
+ final boolean locked = methodType.get(m);
+ final Method live = matchMethod(m, liveMethods);
+ if (live == null) {
+ if (locked) {
+ flag(name, "not overridden in ComputerLocked");
+ }
+ continue;
+ }
+ if (!locked) {
+ flag(name, "improperly overridden in ComputerLocked");
+ continue;
+ }
+
+ name = "ComputerLocked." + displayName(m);
+ final int modifiers = live.getModifiers();
+ if (!locked) {
+ if (!isPublic(modifiers)) {
+ flag(name, "is not public");
+ }
+ if (!isFinal(modifiers)) {
+ flag(name, "is not final");
+ }
+ }
+ }
+ for (Method m : coreHelpers) {
+ if (m == null) {
+ continue;
+ }
+ String name = "ComputerLocked." + displayName(m);
+ final Method live = matchMethod(m, liveMethods);
+ if (live == null) {
+ flag(name, "is not overridden in ComputerLocked");
+ continue;
+ }
+ }
+ for (Method m : liveMethods) {
+ if (m != null) {
+ String name = "ComputerLocked." + displayName(m);
+ flag(name, "illegal local method");
+ }
+ }
+ }
+
private static PerPackageReadTimeouts[] getPerPackageReadTimeouts(String knownDigestersList) {
final String defaultTimeouts = "3600000001:3600000002:3600000003";
List<PerPackageReadTimeouts> result = PerPackageReadTimeouts.parseDigestersList(
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 7241fa0..90a1277 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -253,6 +253,7 @@
.setPerson(makePerson("person", "personKey", "personUri"))
.setLongLived(true)
.setExtras(pb)
+ .setStartingTheme(android.R.style.Theme_Black_NoTitleBar_Fullscreen)
.build();
si.addFlags(ShortcutInfo.FLAG_PINNED);
si.setBitmapPath("abc");
@@ -288,6 +289,8 @@
assertEquals(null, si.getTextResName());
assertEquals(0, si.getDisabledMessageResourceId());
assertEquals(null, si.getDisabledMessageResName());
+ assertEquals("android:style/Theme.Black.NoTitleBar.Fullscreen",
+ si.getStartingThemeResName());
}
public void testShortcutInfoParcel_resId() {
@@ -308,6 +311,7 @@
.setCategories(set(ShortcutInfo.SHORTCUT_CATEGORY_CONVERSATION, "xyz"))
.setRank(123)
.setExtras(pb)
+ .setStartingTheme(android.R.style.Theme_Black_NoTitleBar_Fullscreen)
.build();
si.addFlags(ShortcutInfo.FLAG_PINNED);
si.setBitmapPath("abc");
@@ -339,6 +343,8 @@
assertEquals(456, si.getIconResourceId());
assertEquals("string/r456", si.getIconResName());
assertEquals("test_uri", si.getIconUri());
+ assertEquals("android:style/Theme.Black.NoTitleBar.Fullscreen",
+ si.getStartingThemeResName());
}
public void testShortcutInfoClone() {
@@ -2210,6 +2216,10 @@
android.R.drawable.alert_dark_frame, true, getTestContext().getPackageName()));
assertEquals("" + android.R.string.cancel, ShortcutInfo.lookUpResourceName(res,
android.R.string.cancel, false, getTestContext().getPackageName()));
+ assertEquals("" + android.R.style.Theme_Black_NoTitleBar_Fullscreen,
+ ShortcutInfo.lookUpResourceName(
+ res, android.R.style.Theme_Black_NoTitleBar_Fullscreen, true,
+ getTestContext().getPackageName()));
}
public void testLookUpResourceName_appResources() {
@@ -2236,6 +2246,10 @@
assertEquals(android.R.drawable.alert_dark_frame, ShortcutInfo.lookUpResourceId(res,
"" + android.R.drawable.alert_dark_frame, null,
getTestContext().getPackageName()));
+ assertEquals(android.R.style.Theme_Black_NoTitleBar_Fullscreen,
+ ShortcutInfo.lookUpResourceId(
+ res, "" + android.R.style.Theme_Black_NoTitleBar_Fullscreen,
+ null, getTestContext().getPackageName()));
}
// Test for a ShortcutInfo method.
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index 17d99e6..9044b27 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -222,6 +222,64 @@
}
/**
+ * Tests that readPermissions works correctly with {@link SystemConfig#ALLOW_VENDOR_APEX}
+ * permission flag for the tag: {@code allowed-vendor-apex}.
+ */
+ @Test
+ public void readPermissions_allowVendorApex_parsesVendorApexAllowList()
+ throws IOException {
+ final String contents =
+ "<config>\n"
+ + " <allowed-vendor-apex package=\"com.android.apex1\" />\n"
+ + "</config>";
+ final File folder = createTempSubfolder("folder");
+ createTempFile(folder, "vendor-apex-allowlist.xml", contents);
+
+ mSysConfig.readPermissions(folder, /* Grant all permission flags */ ~0);
+
+ assertThat(mSysConfig.getAllowedVendorApexes()).containsExactly("com.android.apex1");
+ }
+
+ /**
+ * Tests that readPermissions works correctly with {@link SystemConfig#ALLOW_VENDOR_APEX}
+ * permission flag for the tag: {@code allowed-vendor-apex}.
+ */
+ @Test
+ public void readPermissions_allowVendorApex_parsesVendorApexAllowList_noPackage()
+ throws IOException {
+ final String contents =
+ "<config>\n"
+ + " <allowed-vendor-apex/>\n"
+ + "</config>";
+ final File folder = createTempSubfolder("folder");
+ createTempFile(folder, "vendor-apex-allowlist.xml", contents);
+
+ mSysConfig.readPermissions(folder, /* Grant all permission flags */ ~0);
+
+ assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty();
+ }
+
+
+ /**
+ * Tests that readPermissions works correctly without {@link SystemConfig#ALLOW_VENDOR_APEX}
+ * permission flag for the tag: {@code allowed-oem-apex}.
+ */
+ @Test
+ public void readPermissions_notAllowVendorApex_doesNotParseVendorApexAllowList()
+ throws IOException {
+ final String contents =
+ "<config>\n"
+ + " <allowed-vendor-apex package=\"com.android.apex1\" />\n"
+ + "</config>";
+ final File folder = createTempSubfolder("folder");
+ createTempFile(folder, "vendor-apex-allowlist.xml", contents);
+
+ mSysConfig.readPermissions(folder, /* Grant all but ALLOW_VENDOR_APEX flag */ ~0x400);
+
+ assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty();
+ }
+
+ /**
* Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
*
* @param folderName subdirectory of mTemporaryFolder to put the file, creating if needed
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index b878d9b..645fa63 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -1182,6 +1182,36 @@
assertBucket(STANDBY_BUCKET_RESTRICTED);
}
+ /**
+ * Test that an app that "timed out" into the RESTRICTED bucket can be raised out by system
+ * interaction.
+ */
+ @Test
+ public void testSystemInteractionOverridesRestrictedTimeout() throws Exception {
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+
+ // Long enough that it could have timed out into RESTRICTED.
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
+ mController.checkIdleStates(USER_ID);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+
+ // Report system interaction.
+ mInjector.mElapsedRealtime += 1000;
+ reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+
+ // Ensure that it's raised out of RESTRICTED for the system interaction elevation duration.
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+ mInjector.mElapsedRealtime += 1000;
+ mController.checkIdleStates(USER_ID);
+ assertBucket(STANDBY_BUCKET_ACTIVE);
+
+ // Elevation duration over. Should fall back down.
+ mInjector.mElapsedRealtime += 10 * MINUTE_MS;
+ mController.checkIdleStates(USER_ID);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+ }
+
@Test
public void testRestrictedBucketDisabled() throws Exception {
mInjector.mIsRestrictedBucketEnabled = false;
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
index 0585758..0449e44 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java
@@ -56,6 +56,8 @@
private int[] mSupportedEffects;
private int[] mSupportedBraking;
private int[] mSupportedPrimitives;
+ private int mCompositionSizeMax;
+ private int mPwleSizeMax;
private float mMinFrequency = Float.NaN;
private float mResonantFrequency = Float.NaN;
private float mFrequencyResolution = Float.NaN;
@@ -151,12 +153,22 @@
}
@Override
- public VibratorInfo getInfo(float suggestedFrequencyRange) {
- VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
- mMinFrequency, mResonantFrequency, mFrequencyResolution,
- suggestedFrequencyRange, mMaxAmplitudes);
- return new VibratorInfo(vibratorId, mCapabilities, mSupportedEffects, mSupportedBraking,
- mSupportedPrimitives, null, mQFactor, frequencyMapping);
+ public boolean getInfo(float suggestedFrequencyRange, VibratorInfo.Builder infoBuilder) {
+ infoBuilder.setCapabilities(mCapabilities);
+ infoBuilder.setSupportedBraking(mSupportedBraking);
+ infoBuilder.setPwleSizeMax(mPwleSizeMax);
+ infoBuilder.setSupportedEffects(mSupportedEffects);
+ if (mSupportedPrimitives != null) {
+ for (int primitive : mSupportedPrimitives) {
+ infoBuilder.setSupportedPrimitive(primitive, EFFECT_DURATION);
+ }
+ }
+ infoBuilder.setCompositionSizeMax(mCompositionSizeMax);
+ infoBuilder.setQFactor(mQFactor);
+ infoBuilder.setFrequencyMapping(new VibratorInfo.FrequencyMapping(mMinFrequency,
+ mResonantFrequency, mFrequencyResolution, suggestedFrequencyRange,
+ mMaxAmplitudes));
+ return true;
}
private void applyLatency() {
@@ -236,6 +248,16 @@
mSupportedPrimitives = primitives;
}
+ /** Set the max number of primitives allowed in a composition by the fake vibrator hardware. */
+ public void setCompositionSizeMax(int compositionSizeMax) {
+ mCompositionSizeMax = compositionSizeMax;
+ }
+
+ /** Set the max number of PWLEs allowed in a composition by the fake vibrator hardware. */
+ public void setPwleSizeMax(int pwleSizeMax) {
+ mPwleSizeMax = pwleSizeMax;
+ }
+
/** Set the resonant frequency of the fake vibrator hardware. */
public void setResonantFrequency(float frequencyHz) {
mResonantFrequency = frequencyHz;
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java b/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
index f4eb2de..32988ef 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/StepToRampAdapterTest.java
@@ -68,6 +68,38 @@
}
@Test
+ public void testRampSegments_withPwleDurationLimit_splitsLongRamps() {
+ List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+ new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
+ /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+ new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
+ /* startFrequency= */ 0, /* endFrequency= */ -1, /* duration= */ 25),
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1,
+ /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 5)));
+ List<VibrationEffectSegment> expectedSegments = Arrays.asList(
+ new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
+ /* startFrequency= */ -1, /* endFrequency= */ -1, /* duration= */ 10),
+ new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0.32f,
+ /* startFrequency= */ 0, /* endFrequency= */ -0.32f, /* duration= */ 8),
+ new RampSegment(/* startAmplitude= */ 0.32f, /* endAmplitude= */ 0.64f,
+ /* startFrequency= */ -0.32f, /* endFrequency= */ -0.64f,
+ /* duration= */ 8),
+ new RampSegment(/* startAmplitude= */ 0.64f, /* endAmplitude= */ 1,
+ /* startFrequency= */ -0.64f, /* endFrequency= */ -1, /* duration= */ 9),
+ new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1,
+ /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 5));
+
+ VibratorInfo vibratorInfo = new VibratorInfo.Builder(0)
+ .setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)
+ .setPwlePrimitiveDurationMax(10)
+ .build();
+
+ // Update repeat index to skip the ramp splits.
+ assertEquals(4, mAdapter.apply(segments, 2, vibratorInfo));
+ assertEquals(expectedSegments, segments);
+ }
+
+ @Test
public void testStepAndRampSegments_withoutPwleCapability_keepsListUnchanged() {
mAdapter = new StepToRampAdapter(50);
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index 85501245..7d24a2f 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -33,6 +33,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.media.AudioManager;
import android.os.Handler;
import android.os.PowerManagerInternal;
@@ -69,6 +70,7 @@
public class VibrationSettingsTest {
private static final int UID = 1;
+ private static final int USER_OPERATION_TIMEOUT_MILLIS = 60_000; // 1 min
private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build();
private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder()
.setBatterySaverEnabled(true).build();
@@ -408,6 +410,25 @@
}
@Test
+ public void getCurrentIntensity_updateTriggeredAfterUserSwitched() {
+ mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_OFF);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
+ Vibrator.VIBRATION_INTENSITY_HIGH);
+ assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
+ mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE));
+
+ // Switching user is not working with FakeSettingsProvider.
+ // Testing the broadcast flow manually.
+ Settings.System.putIntForUser(mContextSpy.getContentResolver(),
+ Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW,
+ UserHandle.USER_CURRENT);
+ mVibrationSettings.mUserReceiver.onReceive(mContextSpy,
+ new Intent(Intent.ACTION_USER_SWITCHED));
+ assertEquals(Vibrator.VIBRATION_INTENSITY_LOW,
+ mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE));
+ }
+
+ @Test
public void getFallbackEffect_returnsEffectsFromSettings() {
assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_TICK));
assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_TEXTURE_TICK));
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
index f02e2f0..b8fdb55 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -356,7 +356,8 @@
@Test
public void vibrate_singleVibratorComposed_runsVibration() throws Exception {
- mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+ fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
long vibrationId = 1;
VibrationEffect effect = VibrationEffect.startComposition()
@@ -374,7 +375,7 @@
assertEquals(Arrays.asList(
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0)),
- mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
+ fakeVibrator.getEffectSegments());
}
@Test
@@ -395,6 +396,27 @@
}
@Test
+ public void vibrate_singleVibratorLargeComposition_splitsVibratorComposeCalls() {
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+ fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
+ fakeVibrator.setCompositionSizeMax(2);
+
+ long vibrationId = 1;
+ VibrationEffect effect = VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.8f)
+ .compose();
+ VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+ waitForCompletion(thread);
+
+ verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
+ // Vibrator compose called twice.
+ verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
+ assertEquals(3, fakeVibrator.getEffectSegments().size());
+ }
+
+ @Test
public void vibrate_singleVibratorComposedEffects_runsDifferentVibrations() throws Exception {
mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(
@@ -432,12 +454,13 @@
@Test
public void vibrate_singleVibratorPwle_runsComposePwle() throws Exception {
- mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
- mVibratorProviders.get(VIBRATOR_ID).setSupportedBraking(Braking.CLAB);
- mVibratorProviders.get(VIBRATOR_ID).setMinFrequency(100);
- mVibratorProviders.get(VIBRATOR_ID).setResonantFrequency(150);
- mVibratorProviders.get(VIBRATOR_ID).setFrequencyResolution(50);
- mVibratorProviders.get(VIBRATOR_ID).setMaxAmplitudes(
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+ fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ fakeVibrator.setSupportedBraking(Braking.CLAB);
+ fakeVibrator.setMinFrequency(100);
+ fakeVibrator.setResonantFrequency(150);
+ fakeVibrator.setFrequencyResolution(50);
+ fakeVibrator.setMaxAmplitudes(
0.5f /* 100Hz*/, 1 /* 150Hz */, 0.6f /* 200Hz */);
long vibrationId = 1;
@@ -462,8 +485,34 @@
expectedRamp(/* amplitude= */ 0.6f, /* frequency= */ 200, /* duration= */ 30),
expectedRamp(/* StartAmplitude= */ 0.6f, /* endAmplitude= */ 0.5f,
/* startFrequency= */ 200, /* endFrequency= */ 100, /* duration= */ 40)),
- mVibratorProviders.get(VIBRATOR_ID).getEffectSegments());
- assertEquals(Arrays.asList(Braking.CLAB), mVibratorProviders.get(VIBRATOR_ID).getBraking());
+ fakeVibrator.getEffectSegments());
+ assertEquals(Arrays.asList(Braking.CLAB), fakeVibrator.getBraking());
+ }
+
+ @Test
+ public void vibrate_singleVibratorLargePwle_splitsVibratorComposeCalls() {
+ FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
+ fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
+ fakeVibrator.setMinFrequency(100);
+ fakeVibrator.setResonantFrequency(150);
+ fakeVibrator.setFrequencyResolution(50);
+ fakeVibrator.setMaxAmplitudes(1, 1, 1);
+ fakeVibrator.setPwleSizeMax(2);
+
+ long vibrationId = 1;
+ VibrationEffect effect = VibrationEffect.startWaveform()
+ .addStep(1, 10)
+ .addRamp(0, 20)
+ .addStep(0.8f, 1, 30)
+ .addRamp(0.6f, -1, 40)
+ .build();
+ VibrationThread thread = startThreadAndDispatcher(vibrationId, effect);
+ waitForCompletion(thread);
+
+ verify(mThreadCallbacks).onVibrationEnded(eq(vibrationId), eq(Vibration.Status.FINISHED));
+ // Vibrator compose called twice.
+ verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
+ assertEquals(4, fakeVibrator.getEffectSegments().size());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
index 9e98e7d..a732bd1 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorControllerTest.java
@@ -298,11 +298,13 @@
private void mockVibratorCapabilities(int capabilities) {
VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
- when(mNativeWrapperMock.getInfo(anyFloat())).thenReturn(
- new VibratorInfo.Builder(VIBRATOR_ID)
- .setCapabilities(capabilities)
- .setFrequencyMapping(frequencyMapping)
- .build());
+ when(mNativeWrapperMock.getInfo(anyFloat(), any(VibratorInfo.Builder.class)))
+ .then(invocation -> {
+ ((VibratorInfo.Builder) invocation.getArgument(1))
+ .setCapabilities(capabilities)
+ .setFrequencyMapping(frequencyMapping);
+ return true;
+ });
}
private PrebakedSegment createPrebaked(int effectId, int effectStrength) {
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index ea2dc0a..9117ae6 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -234,7 +234,7 @@
CombinedVibration effect = CombinedVibration.createParallel(
VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
vibrate(service, effect, HAPTIC_FEEDBACK_ATTRS);
- service.cancelVibrate(/* usageFilter= */ -1, service);
+ service.cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, service);
assertTrue(service.setAlwaysOnEffect(UID, PACKAGE_NAME, 1, effect, ALARM_ATTRS));
@@ -889,13 +889,13 @@
mockVibrators(1);
VibratorManagerService service = createSystemReadyService();
- service.cancelVibrate(/* usageFilter= */ -1, service);
+ service.cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, service);
assertFalse(service.isVibrating(1));
vibrate(service, VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100), ALARM_ATTRS);
assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
- service.cancelVibrate(/* usageFilter= */ -1, service);
+ service.cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, service);
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
}
@@ -924,6 +924,39 @@
assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
}
+ @Test
+ public void cancelVibrate_withoutUnknownUsage_onlyStopsIfFilteringUnknownOrAllUsages()
+ throws Exception {
+ mockVibrators(1);
+ VibrationAttributes attrs = new VibrationAttributes.Builder()
+ .setUsage(VibrationAttributes.USAGE_UNKNOWN)
+ .build();
+ VibratorManagerService service = createSystemReadyService();
+
+ vibrate(service, VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100), attrs);
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ // Do not cancel UNKNOWN vibration when filter is being applied for other usages.
+ service.cancelVibrate(VibrationAttributes.USAGE_RINGTONE, service);
+ assertFalse(waitUntil(s -> !s.isVibrating(1), service, /* timeout= */ 50));
+
+ service.cancelVibrate(
+ VibrationAttributes.USAGE_CLASS_ALARM | ~VibrationAttributes.USAGE_CLASS_MASK,
+ service);
+ assertFalse(waitUntil(s -> !s.isVibrating(1), service, /* timeout= */ 50));
+
+ // Cancel UNKNOWN vibration when filtered for that vibration specifically.
+ service.cancelVibrate(VibrationAttributes.USAGE_UNKNOWN, service);
+ assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ vibrate(service, VibrationEffect.createOneShot(10 * TEST_TIMEOUT_MILLIS, 100), attrs);
+ assertTrue(waitUntil(s -> s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+
+ // Cancel UNKNOWN vibration when all vibrations are being cancelled.
+ service.cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, service);
+ assertTrue(waitUntil(s -> !s.isVibrating(1), service, TEST_TIMEOUT_MILLIS));
+ }
+
private void mockCapabilities(long... capabilities) {
when(mNativeWrapperMock.getCapabilities()).thenReturn(
Arrays.stream(capabilities).reduce(0, (a, b) -> a | b));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 3862d75..71c05b5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -48,6 +48,7 @@
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.KeyguardManager;
import android.app.Notification;
import android.app.Notification.Builder;
import android.app.NotificationChannel;
@@ -111,6 +112,8 @@
NotificationUsageStats mUsageStats;
@Mock
IAccessibilityManager mAccessibilityService;
+ @Mock
+ KeyguardManager mKeyguardManager;
NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
1 << 30);
@@ -153,6 +156,7 @@
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
when(mUsageStats.isAlertRateLimited(any())).thenReturn(false);
when(mVibrator.hasFrequencyControl()).thenReturn(false);
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false);
long serviceReturnValue = IntPair.of(
AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED,
@@ -174,6 +178,7 @@
mService.setScreenOn(false);
mService.setUsageStats(mUsageStats);
mService.setAccessibilityManager(accessibilityManager);
+ mService.setKeyguardManager(mKeyguardManager);
mService.mScreenOn = false;
mService.mInCallStateOffHook = false;
mService.mNotificationPulseEnabled = true;
@@ -496,6 +501,94 @@
}
@Test
+ public void testLockedPrivateA11yRedaction() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+ r.getNotification().visibility = Notification.VISIBILITY_PRIVATE;
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true);
+ AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+ when(accessibilityManager.isEnabled()).thenReturn(true);
+ mService.setAccessibilityManager(accessibilityManager);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ ArgumentCaptor<AccessibilityEvent> eventCaptor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+ verify(accessibilityManager, times(1))
+ .sendAccessibilityEvent(eventCaptor.capture());
+
+ AccessibilityEvent event = eventCaptor.getValue();
+ assertEquals(r.getNotification().publicVersion, event.getParcelableData());
+ }
+
+ @Test
+ public void testLockedOverridePrivateA11yRedaction() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setPackageVisibilityOverride(Notification.VISIBILITY_PRIVATE);
+ r.getNotification().visibility = Notification.VISIBILITY_PUBLIC;
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true);
+ AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+ when(accessibilityManager.isEnabled()).thenReturn(true);
+ mService.setAccessibilityManager(accessibilityManager);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ ArgumentCaptor<AccessibilityEvent> eventCaptor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+ verify(accessibilityManager, times(1))
+ .sendAccessibilityEvent(eventCaptor.capture());
+
+ AccessibilityEvent event = eventCaptor.getValue();
+ assertEquals(r.getNotification().publicVersion, event.getParcelableData());
+ }
+
+ @Test
+ public void testLockedPublicA11yNoRedaction() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+ r.getNotification().visibility = Notification.VISIBILITY_PUBLIC;
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(true);
+ AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+ when(accessibilityManager.isEnabled()).thenReturn(true);
+ mService.setAccessibilityManager(accessibilityManager);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ ArgumentCaptor<AccessibilityEvent> eventCaptor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+ verify(accessibilityManager, times(1))
+ .sendAccessibilityEvent(eventCaptor.capture());
+
+ AccessibilityEvent event = eventCaptor.getValue();
+ assertEquals(r.getNotification(), event.getParcelableData());
+ }
+
+ @Test
+ public void testUnlockedPrivateA11yNoRedaction() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setPackageVisibilityOverride(NotificationManager.VISIBILITY_NO_OVERRIDE);
+ r.getNotification().visibility = Notification.VISIBILITY_PRIVATE;
+ when(mKeyguardManager.isDeviceLocked(anyInt())).thenReturn(false);
+ AccessibilityManager accessibilityManager = Mockito.mock(AccessibilityManager.class);
+ when(accessibilityManager.isEnabled()).thenReturn(true);
+ mService.setAccessibilityManager(accessibilityManager);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ ArgumentCaptor<AccessibilityEvent> eventCaptor =
+ ArgumentCaptor.forClass(AccessibilityEvent.class);
+
+ verify(accessibilityManager, times(1))
+ .sendAccessibilityEvent(eventCaptor.capture());
+
+ AccessibilityEvent event = eventCaptor.getValue();
+ assertEquals(r.getNotification(), event.getParcelableData());
+ }
+
+ @Test
public void testBeepInsistently() throws Exception {
NotificationRecord r = getInsistentBeepyNotification();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index 5614aa2..577e36c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -394,7 +394,7 @@
"disabledMessage", 0, "disabledMessageResName",
null, null, 0, null, 0, 0,
0, "iconResName", "bitmapPath", null, 0,
- null, null, 0);
+ null, null, null);
return si;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 70ba2cf..3c6f62a 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -94,6 +94,7 @@
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
@@ -232,6 +233,7 @@
@RunWithLooper
public class NotificationManagerServiceTest extends UiServiceTestCase {
private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
+ private static final int UID_HEADLESS = 1000000;
private final int mUid = Binder.getCallingUid();
private TestableNotificationManagerService mService;
@@ -2425,6 +2427,8 @@
when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
.thenReturn(mTestNotificationChannel);
+ when(mPreferencesHelper.deleteNotificationChannel(eq(PKG), anyInt(),
+ eq(mTestNotificationChannel.getId()))).thenReturn(true);
reset(mListeners);
mBinderService.deleteNotificationChannel(PKG, mTestNotificationChannel.getId());
verify(mListeners, times(1)).notifyNotificationChannelChanged(eq(PKG),
@@ -2433,6 +2437,22 @@
}
@Test
+ public void testDeleteChannelOnlyDoExtraWorkIfExisted() throws Exception {
+ List<String> associations = new ArrayList<>();
+ associations.add("a");
+ when(mCompanionMgr.getAssociations(PKG, UserHandle.getUserId(mUid)))
+ .thenReturn(associations);
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
+ eq(mTestNotificationChannel.getId()), anyBoolean()))
+ .thenReturn(null);
+ reset(mListeners);
+ mBinderService.deleteNotificationChannel(PKG, mTestNotificationChannel.getId());
+ verifyNoMoreInteractions(mListeners);
+ verifyNoMoreInteractions(mHistoryManager);
+ }
+
+ @Test
public void testDeleteChannelGroupNotifyListener() throws Exception {
List<String> associations = new ArrayList<>();
associations.add("a");
@@ -6472,7 +6492,7 @@
zenPolicy, NotificationManager.INTERRUPTION_FILTER_NONE, isEnabled);
try {
- mBinderService.addAutomaticZenRule(rule);
+ mBinderService.addAutomaticZenRule(rule, mContext.getPackageName());
fail("Zen policy only applies to priority only mode");
} catch (IllegalArgumentException e) {
// yay
@@ -6480,11 +6500,11 @@
rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
- mBinderService.addAutomaticZenRule(rule);
+ mBinderService.addAutomaticZenRule(rule, mContext.getPackageName());
rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
null, NotificationManager.INTERRUPTION_FILTER_NONE, isEnabled);
- mBinderService.addAutomaticZenRule(rule);
+ mBinderService.addAutomaticZenRule(rule, mContext.getPackageName());
}
@Test
@@ -6739,7 +6759,11 @@
@Test
public void testGrantInlineReplyUriPermission_recordExists() throws Exception {
- NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, 0);
+ int userId = UserManager.isHeadlessSystemUserMode()
+ ? UserHandle.getUserId(UID_HEADLESS)
+ : USER_SYSTEM;
+
+ NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, userId);
mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
waitForIdle();
@@ -6764,7 +6788,11 @@
@Test
public void testGrantInlineReplyUriPermission_noRecordExists() throws Exception {
- NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, 0);
+ int userId = UserManager.isHeadlessSystemUserMode()
+ ? UserHandle.getUserId(UID_HEADLESS)
+ : USER_SYSTEM;
+
+ NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, userId);
waitForIdle();
// No notifications exist for the given record
@@ -6808,7 +6836,9 @@
// Target user for the grant is USER_ALL instead of USER_SYSTEM
verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
eq(nr.getSbn().getUid()), eq(nr.getSbn().getPackageName()), eq(uri), anyInt(),
- anyInt(), eq(UserHandle.USER_SYSTEM));
+ anyInt(), UserManager.isHeadlessSystemUserMode()
+ ? eq(UserHandle.getUserId(UID_HEADLESS))
+ : eq(USER_SYSTEM));
}
@Test
@@ -6851,7 +6881,11 @@
@Test
public void testClearInlineReplyUriPermission_uriRecordExists() throws Exception {
- NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, 0);
+ int userId = UserManager.isHeadlessSystemUserMode()
+ ? UserHandle.getUserId(UID_HEADLESS)
+ : USER_SYSTEM;
+
+ NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, userId);
reset(mPackageManager);
Uri uri1 = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
@@ -6913,7 +6947,10 @@
// permissionOwner destroyed for USER_SYSTEM, not USER_ALL
verify(mUgmInternal, times(1)).revokeUriPermissionFromOwner(
- eq(record.getPermissionOwner()), eq(null), eq(~0), eq(USER_SYSTEM));
+ eq(record.getPermissionOwner()), eq(null), eq(~0),
+ UserManager.isHeadlessSystemUserMode()
+ ? eq(UserHandle.getUserId(UID_HEADLESS))
+ : eq(USER_SYSTEM));
}
@Test
@@ -7406,6 +7443,10 @@
@Test
public void createConversationNotificationChannel() throws Exception {
+ int userId = UserManager.isHeadlessSystemUserMode()
+ ? UserHandle.getUserId(UID_HEADLESS)
+ : USER_SYSTEM;
+
NotificationChannel original = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
original.setAllowBubbles(!original.canBubble());
original.setShowBadge(!original.canShowBadge());
@@ -7424,7 +7465,7 @@
PKG, mUid, orig, "friend");
NotificationChannel friendChannel = mBinderService.getConversationNotificationChannel(
- PKG, 0, PKG, original.getId(), false, "friend");
+ PKG, userId, PKG, original.getId(), false, "friend");
assertEquals(original.getName(), friendChannel.getName());
assertEquals(original.getId(), friendChannel.getParentChannelId());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 3a51ff2..bf0ed71 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -90,7 +90,7 @@
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.Settings;
+import android.os.UserManager;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.service.notification.ConversationChannelWrapper;
@@ -137,6 +137,7 @@
@RunWith(AndroidJUnit4.class)
public class PreferencesHelperTest extends UiServiceTestCase {
private static final int UID_N_MR1 = 0;
+ private static final int UID_HEADLESS = 1000000;
private static final UserHandle USER = UserHandle.of(0);
private static final int UID_O = 1111;
private static final int UID_P = 2222;
@@ -1495,11 +1496,13 @@
@Test
public void testCreateAndDeleteCanChannelsBypassDnd() throws Exception {
+ int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
+
// create notification channel that can't bypass dnd
// expected result: areChannelsBypassingDnd = false
// setNotificationPolicy isn't called since areChannelsBypassingDnd was already false
NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
resetZenModeHelper();
@@ -1508,18 +1511,18 @@
// expected result: areChannelsBypassingDnd = true
NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
channel2.setBypassDnd(true);
- mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, true);
+ mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true);
assertTrue(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
resetZenModeHelper();
// delete channels
- mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId());
+ mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel.getId());
assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND
verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
resetZenModeHelper();
- mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId());
+ mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel2.getId());
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
resetZenModeHelper();
@@ -1527,11 +1530,13 @@
@Test
public void testUpdateCanChannelsBypassDnd() throws Exception {
+ int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
+
// create notification channel that can't bypass dnd
// expected result: areChannelsBypassingDnd = false
// setNotificationPolicy isn't called since areChannelsBypassingDnd was already false
NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW);
- mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false);
+ mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, never()).setNotificationPolicy(any());
resetZenModeHelper();
@@ -1539,7 +1544,7 @@
// update channel so it CAN bypass dnd:
// expected result: areChannelsBypassingDnd = true
channel.setBypassDnd(true);
- mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, uid, channel, true);
assertTrue(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
resetZenModeHelper();
@@ -1547,7 +1552,7 @@
// update channel so it can't bypass dnd:
// expected result: areChannelsBypassingDnd = false
channel.setBypassDnd(false);
- mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true);
+ mHelper.updateNotificationChannel(PKG_N_MR1, uid, channel, true);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
resetZenModeHelper();
@@ -3333,6 +3338,17 @@
}
@Test
+ public void testDeleted_twice() throws Exception {
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
+ mAppOpsManager, mStatsEventBuilderFactory);
+
+ mHelper.createNotificationChannel(
+ PKG_P, UID_P, createNotificationChannel("id", "id", 2), true, false);
+ assertTrue(mHelper.deleteNotificationChannel(PKG_P, UID_P, "id"));
+ assertFalse(mHelper.deleteNotificationChannel(PKG_P, UID_P, "id"));
+ }
+
+ @Test
public void testDeleted_recentTime() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mLogger,
mAppOpsManager, mStatsEventBuilderFactory);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 5262465..d0bf63a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -190,7 +190,7 @@
ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
rule.configurationActivity = new ComponentName("a", "a");
- rule.component = new ComponentName("a", "b");
+ rule.component = new ComponentName("b", "b");
rule.conditionId = new Uri.Builder().scheme("hello").build();
rule.condition = new Condition(rule.conditionId, "", Condition.STATE_TRUE);
rule.enabled = true;
@@ -200,6 +200,7 @@
rule.modified = true;
rule.name = "name";
rule.snoozing = true;
+ rule.pkg = "b";
TypedXmlSerializer out = Xml.newFastSerializer();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -215,8 +216,7 @@
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
ZenModeConfig.ZenRule fromXml = ZenModeConfig.readRuleXml(parser);
- // read from backing component
- assertEquals("a", fromXml.pkg);
+ assertEquals("b", fromXml.pkg);
// always resets on reboot
assertFalse(fromXml.snoozing);
//should all match original
@@ -232,6 +232,55 @@
assertEquals(rule.zenMode, fromXml.zenMode);
}
+ @Test
+ public void testRuleXml_pkg_component() throws Exception {
+ String tag = "tag";
+
+ ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+ rule.configurationActivity = new ComponentName("a", "a");
+ rule.component = new ComponentName("b", "b");
+
+ TypedXmlSerializer out = Xml.newFastSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ out.setOutput(new BufferedOutputStream(baos), "utf-8");
+ out.startDocument(null, true);
+ out.startTag(null, tag);
+ ZenModeConfig.writeRuleXml(rule, out);
+ out.endTag(null, tag);
+ out.endDocument();
+
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), null);
+ parser.nextTag();
+ ZenModeConfig.ZenRule fromXml = ZenModeConfig.readRuleXml(parser);
+ assertEquals("b", fromXml.pkg);
+ }
+
+ @Test
+ public void testRuleXml_pkg_configActivity() throws Exception {
+ String tag = "tag";
+
+ ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+ rule.configurationActivity = new ComponentName("a", "a");
+
+ TypedXmlSerializer out = Xml.newFastSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ out.setOutput(new BufferedOutputStream(baos), "utf-8");
+ out.startDocument(null, true);
+ out.startTag(null, tag);
+ ZenModeConfig.writeRuleXml(rule, out);
+ out.endTag(null, tag);
+ out.endDocument();
+
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), null);
+ parser.nextTag();
+ ZenModeConfig.ZenRule fromXml = ZenModeConfig.readRuleXml(parser);
+ assertNull(fromXml.pkg);
+ }
+
private ZenModeConfig getMutedRingerConfig() {
ZenModeConfig config = new ZenModeConfig();
// Allow alarms, media
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 72c6028..00dbaf6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -1597,7 +1597,7 @@
new ComponentName("android", "ScheduleConditionProvider"),
ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test");
+ String id = mZenModeHelperSpy.addAutomaticZenRule("android", zenRule, "test");
assertTrue(id != null);
ZenModeConfig.ZenRule ruleInConfig = mZenModeHelperSpy.mConfig.automaticRules.get(id);
@@ -1617,12 +1617,12 @@
new ComponentName("android", "ScheduleConditionProvider"),
sharedUri,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id = mZenModeHelperSpy.addAutomaticZenRule(zenRule, "test");
+ String id = mZenModeHelperSpy.addAutomaticZenRule("android", zenRule, "test");
AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
new ComponentName("android", "ScheduleConditionProvider"),
sharedUri,
NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
- String id2 = mZenModeHelperSpy.addAutomaticZenRule(zenRule2, "test");
+ String id2 = mZenModeHelperSpy.addAutomaticZenRule("android", zenRule2, "test");
Condition condition = new Condition(sharedUri, "", Condition.STATE_TRUE);
mZenModeHelperSpy.setAutomaticZenRuleState(sharedUri, condition);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 38466eb..32a4774 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -270,9 +270,16 @@
@Test
public void testOnReportFullyDrawn() {
+ // Create an invisible event that should be cancelled after the next event starts.
+ onActivityLaunched(mTrampolineActivity);
+ mTrampolineActivity.mVisibleRequested = false;
+
mActivityOptions = ActivityOptions.makeBasic();
mActivityOptions.setSourceInfo(SourceInfo.TYPE_LAUNCHER, SystemClock.uptimeMillis() - 10);
- onActivityLaunched(mTopActivity);
+ onIntentStarted(mTopActivity.intent);
+ notifyActivityLaunched(START_SUCCESS, mTopActivity);
+ verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mTopActivity), anyInt());
+ verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mTrampolineActivity));
// The activity reports fully drawn before windows drawn, then the fully drawn event will
// be pending (see {@link WindowingModeTransitionInfo#pendingFullyDrawn}).
@@ -287,6 +294,10 @@
verifyAsync(mLaunchObserver).onReportFullyDrawn(eqProto(mTopActivity), anyLong());
verifyOnActivityLaunchFinished(mTopActivity);
verifyNoMoreInteractions(mLaunchObserver);
+
+ final ActivityMetricsLogger.TransitionInfoSnapshot fullyDrawnInfo = mActivityMetricsLogger
+ .logAppTransitionReportedDrawn(mTopActivity, false /* restoredFromBundle */);
+ assertWithMessage("Invisible event must be dropped").that(fullyDrawnInfo).isNull();
}
private void onActivityLaunchedTrampoline() {
@@ -480,6 +491,7 @@
@Test
public void testConsecutiveLaunchWithDifferentWindowingMode() {
mTopActivity.setWindowingMode(WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
+ mTrampolineActivity.mVisibleRequested = true;
onActivityLaunched(mTrampolineActivity);
mActivityMetricsLogger.notifyActivityLaunching(mTopActivity.intent,
mTrampolineActivity /* caller */, mTrampolineActivity.getUid());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 8216830..b7713a9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2699,6 +2699,85 @@
assertFalse("Starting window should not be present", activity.hasStartingWindow());
}
+ @Test
+ public void testSetVisibility_visibleToVisible() {
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setCreateTask(true).build();
+ // By default, activity is visible.
+ assertTrue(activity.isVisible());
+ assertTrue(activity.mVisibleRequested);
+ assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity));
+ assertFalse(activity.mDisplayContent.mClosingApps.contains(activity));
+
+ // Request the activity to be visible. Although the activity is already visible, app
+ // transition animation should be applied on this activity. This might be unnecessary, but
+ // until we verify no logic relies on this behavior, we'll keep this as is.
+ activity.setVisibility(true);
+ assertTrue(activity.isVisible());
+ assertTrue(activity.mVisibleRequested);
+ assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity));
+ assertFalse(activity.mDisplayContent.mClosingApps.contains(activity));
+ }
+
+ @Test
+ public void testSetVisibility_visibleToInvisible() {
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setCreateTask(true).build();
+ // By default, activity is visible.
+ assertTrue(activity.isVisible());
+ assertTrue(activity.mVisibleRequested);
+ assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity));
+ assertFalse(activity.mDisplayContent.mClosingApps.contains(activity));
+
+ // Request the activity to be invisible. Since the visibility changes, app transition
+ // animation should be applied on this activity.
+ activity.setVisibility(false);
+ assertTrue(activity.isVisible());
+ assertFalse(activity.mVisibleRequested);
+ assertFalse(activity.mDisplayContent.mOpeningApps.contains(activity));
+ assertTrue(activity.mDisplayContent.mClosingApps.contains(activity));
+ }
+
+ @Test
+ public void testSetVisibility_invisibleToVisible() {
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setCreateTask(true).setVisible(false).build();
+ // Activiby is invisible. However ATMS requests it to become visible, since this is a top
+ // activity.
+ assertFalse(activity.isVisible());
+ assertTrue(activity.mVisibleRequested);
+ assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity));
+ assertFalse(activity.mDisplayContent.mClosingApps.contains(activity));
+
+ // Request the activity to be visible. Since the visibility changes, app transition
+ // animation should be applied on this activity.
+ activity.setVisibility(true);
+ assertFalse(activity.isVisible());
+ assertTrue(activity.mVisibleRequested);
+ assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity));
+ assertFalse(activity.mDisplayContent.mClosingApps.contains(activity));
+ }
+
+ @Test
+ public void testSetVisibility_invisibleToInvisible() {
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setCreateTask(true).setVisible(false).build();
+ // Activiby is invisible. However ATMS requests it to become visible, since this is a top
+ // activity.
+ assertFalse(activity.isVisible());
+ assertTrue(activity.mVisibleRequested);
+ assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity));
+ assertFalse(activity.mDisplayContent.mClosingApps.contains(activity));
+
+ // Request the activity to be invisible. Since the activity is already invisible, no app
+ // transition should be applied on this activity.
+ activity.setVisibility(false);
+ assertFalse(activity.isVisible());
+ assertFalse(activity.mVisibleRequested);
+ assertFalse(activity.mDisplayContent.mOpeningApps.contains(activity));
+ assertFalse(activity.mDisplayContent.mClosingApps.contains(activity));
+ }
+
private void assertHasStartingWindow(ActivityRecord atoken) {
assertNotNull(atoken.mStartingSurface);
assertNotNull(atoken.mStartingData);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 2558259..741f33f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -18,6 +18,8 @@
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -407,16 +409,16 @@
}
@Test
- public void testSupportsMultiWindow_activityMinWidthHeight_smallerThanSupport() {
+ public void testSupportsMultiWindow_landscape_checkActivityMinWidth() {
// This is smaller than the min dimensions device support in multi window,
// the activity will be supported in multi window
final float density = mContext.getResources().getDisplayMetrics().density;
- final int supportedDimensions = (int) ((mAtm.mLargeScreenSmallestScreenWidthDp - 1)
+ final int supportedWidth = (int) (mAtm.mLargeScreenSmallestScreenWidthDp
* mAtm.mMinPercentageMultiWindowSupportWidth * density);
final ActivityInfo.WindowLayout windowLayout =
new ActivityInfo.WindowLayout(0, 0, 0, 0, 0,
- /* minWidth= */supportedDimensions,
- /* minHeight= */supportedDimensions);
+ /* minWidth= */ supportedWidth,
+ /* minHeight= */ 0);
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setCreateTask(true)
.setWindowLayout(windowLayout)
@@ -425,15 +427,48 @@
final Task task = activity.getTask();
final TaskDisplayArea tda = task.getDisplayArea();
tda.getConfiguration().smallestScreenWidthDp = mAtm.mLargeScreenSmallestScreenWidthDp - 1;
+ tda.getConfiguration().screenWidthDp = mAtm.mLargeScreenSmallestScreenWidthDp - 1;
+ tda.getConfiguration().orientation = ORIENTATION_LANDSCAPE;
- // Always check the activity min width/height.
- mAtm.mSupportsNonResizableMultiWindow = 1;
+ assertFalse(activity.supportsMultiWindow());
+ assertFalse(task.supportsMultiWindow());
+
+ tda.getConfiguration().screenWidthDp = (int) Math.ceil(
+ mAtm.mLargeScreenSmallestScreenWidthDp
+ / mAtm.mMinPercentageMultiWindowSupportWidth);
assertTrue(activity.supportsMultiWindow());
assertTrue(task.supportsMultiWindow());
+ }
- // The default config is relying on the screen size. Check for small screen
- mAtm.mSupportsNonResizableMultiWindow = 0;
+ @Test
+ public void testSupportsMultiWindow_portrait_checkActivityMinHeight() {
+ // This is smaller than the min dimensions device support in multi window,
+ // the activity will be supported in multi window
+ final float density = mContext.getResources().getDisplayMetrics().density;
+ final int supportedHeight = (int) (mAtm.mLargeScreenSmallestScreenWidthDp
+ * mAtm.mMinPercentageMultiWindowSupportHeight * density);
+ final ActivityInfo.WindowLayout windowLayout =
+ new ActivityInfo.WindowLayout(0, 0, 0, 0, 0,
+ /* minWidth= */ 0,
+ /* minHeight= */ supportedHeight);
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setCreateTask(true)
+ .setWindowLayout(windowLayout)
+ .setResizeMode(RESIZE_MODE_RESIZEABLE)
+ .build();
+ final Task task = activity.getTask();
+ final TaskDisplayArea tda = task.getDisplayArea();
+ tda.getConfiguration().smallestScreenWidthDp = mAtm.mLargeScreenSmallestScreenWidthDp - 1;
+ tda.getConfiguration().screenHeightDp = mAtm.mLargeScreenSmallestScreenWidthDp - 1;
+ tda.getConfiguration().orientation = ORIENTATION_PORTRAIT;
+
+ assertFalse(activity.supportsMultiWindow());
+ assertFalse(task.supportsMultiWindow());
+
+ tda.getConfiguration().screenHeightDp = (int) Math.ceil(
+ mAtm.mLargeScreenSmallestScreenWidthDp
+ / mAtm.mMinPercentageMultiWindowSupportHeight);
assertTrue(activity.supportsMultiWindow());
assertTrue(task.supportsMultiWindow());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 39a59c9..0394417 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -255,7 +255,6 @@
mController.setDeferredCancel(true /* deferred */, false /* screenshot */);
mController.cancelAnimationWithScreenshot(false /* screenshot */);
verify(mMockRunner).onAnimationCanceled(null /* taskSnapshot */);
- assertNull(mController.mRecentScreenshotAnimator);
// Simulate the app transition finishing
mController.mAppTransitionListener.onAppTransitionStartingLocked(false, 0, 0, 0);
@@ -271,7 +270,8 @@
assertEquals(activity.getTask().getTopVisibleActivity(), activity);
assertEquals(activity.findMainWindow(), win1);
- mController.addAnimation(activity.getTask(), false /* isRecentTaskInvisible */);
+ RecentsAnimationController.TaskAnimationAdapter adapter = mController.addAnimation(
+ activity.getTask(), false /* isRecentTaskInvisible */);
assertTrue(mController.isAnimatingTask(activity.getTask()));
spyOn(mWm.mTaskSnapshotController);
@@ -282,14 +282,9 @@
mController.setDeferredCancel(true /* deferred */, true /* screenshot */);
mController.cancelAnimationWithScreenshot(true /* screenshot */);
verify(mMockRunner).onAnimationCanceled(mMockTaskSnapshot /* taskSnapshot */);
- assertNotNull(mController.mRecentScreenshotAnimator);
- assertTrue(mController.mRecentScreenshotAnimator.isAnimating());
- // Assume IRecentsAnimationController#cleanupScreenshot called to finish screenshot
- // animation.
- spyOn(mController.mRecentScreenshotAnimator.mAnimatable);
- mController.mRecentScreenshotAnimator.cancelAnimation();
- verify(mController.mRecentScreenshotAnimator.mAnimatable).onAnimationLeashLost(any());
+ // Continue the animation (simulating a call to cleanupScreenshot())
+ mController.continueDeferredCancelAnimation();
verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false);
}
@@ -655,6 +650,59 @@
assertFalse(win1.mHasSurface);
}
+ @Test
+ public void testCancelForRotation_ReorderToTop() throws Exception {
+ mWm.setRecentsAnimationController(mController);
+ final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
+ final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
+ activity.addWindow(win1);
+
+ mController.addAnimation(activity.getTask(), false /* isRecentTaskInvisible */);
+ mController.setWillFinishToHome(true);
+ mController.cancelAnimationForDisplayChange();
+
+ verify(mMockRunner).onAnimationCanceled(any());
+ verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_TOP, false);
+ }
+
+ @Test
+ public void testCancelForRotation_ReorderToOriginalPosition() throws Exception {
+ mWm.setRecentsAnimationController(mController);
+ final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
+ final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
+ activity.addWindow(win1);
+
+ mController.addAnimation(activity.getTask(), false /* isRecentTaskInvisible */);
+ mController.setWillFinishToHome(false);
+ mController.cancelAnimationForDisplayChange();
+
+ verify(mMockRunner).onAnimationCanceled(any());
+ verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_ORIGINAL_POSITION, false);
+ }
+
+ @Test
+ public void testCancelForStartHome() throws Exception {
+ mWm.setRecentsAnimationController(mController);
+ final ActivityRecord activity = createActivityRecord(mDefaultDisplay);
+ final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1");
+ activity.addWindow(win1);
+
+ RecentsAnimationController.TaskAnimationAdapter adapter = mController.addAnimation(
+ activity.getTask(), false /* isRecentTaskInvisible */);
+ mController.setWillFinishToHome(true);
+
+ // Verify cancel is called with a snapshot and that we've created an overlay
+ spyOn(mWm.mTaskSnapshotController);
+ doReturn(mMockTaskSnapshot).when(mWm.mTaskSnapshotController).getSnapshot(anyInt(),
+ anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */);
+ mController.cancelAnimationForHomeStart();
+ verify(mMockRunner).onAnimationCanceled(any());
+
+ // Continue the animation (simulating a call to cleanupScreenshot())
+ mController.continueDeferredCancelAnimation();
+ verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_TOP, false);
+ }
+
private ActivityRecord createHomeActivity() {
final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService)
.setParentTask(mRootHomeTask)
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 9267285..9cf29d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -53,6 +54,7 @@
import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.refEq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -1012,12 +1014,26 @@
// Create another activity on top and the user id is 1
final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task)
.setUid(UserHandle.PER_USER_RANGE + 1).build();
+ doReturn(true).when(topActivity).okToShowLocked();
+ topActivity.intent.setAction(Intent.ACTION_MAIN);
// Make sure the listeners will be notified for putting the task to locked state
TaskChangeNotificationController controller = mAtm.getTaskChangeNotificationController();
spyOn(controller);
mWm.mRoot.lockAllProfileTasks(0);
verify(controller).notifyTaskProfileLocked(eq(taskId), eq(0));
+
+ // Create the work lock activity on top of the task
+ final ActivityRecord workLockActivity = new ActivityBuilder(mAtm).setTask(task)
+ .setUid(UserHandle.PER_USER_RANGE + 1).build();
+ doReturn(true).when(workLockActivity).okToShowLocked();
+ workLockActivity.intent.setAction(ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER);
+ doReturn(workLockActivity.mActivityComponent).when(mAtm).getSysUiServiceComponentLocked();
+
+ // Make sure the listener won't be notified again.
+ clearInvocations(controller);
+ mWm.mRoot.lockAllProfileTasks(0);
+ verify(controller, never()).notifyTaskProfileLocked(anyInt(), anyInt());
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index d2270b5..4872ec5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -401,6 +401,7 @@
assertFitted();
final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
+ final Rect currentAppBounds = mActivity.getWindowConfiguration().getAppBounds();
final Rect originalBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
final int notchHeight = 100;
@@ -428,8 +429,8 @@
// Because the display cannot rotate, the portrait activity will fit the short side of
// display with keeping portrait bounds [200, 0 - 700, 1000] in center.
assertEquals(newDisplayBounds.height(), currentBounds.height());
- assertEquals(currentBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
- currentBounds.width());
+ assertEquals(currentAppBounds.height() * newDisplayBounds.height()
+ / newDisplayBounds.width(), currentAppBounds.width());
assertFitted();
// The appBounds should be [200, 100 - 700, 1000].
final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
@@ -1534,30 +1535,6 @@
}
@Test
- public void testSandboxDisplayApis_unresizableAppNotSandboxed() {
- // Set up a display in landscape with an unresizable app.
- setUpDisplaySizeWithApp(2500, 1000);
- mActivity.mDisplayContent.setSandboxDisplayApis(false /* sandboxDisplayApis */);
- prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
- assertFitted();
-
- // Activity max bounds not be sandboxed since sandboxing is disabled.
- assertMaxBoundsInheritDisplayAreaBounds();
- }
-
- @Test
- public void testSandboxDisplayApis_unresizableAppSandboxed() {
- // Set up a display in landscape with an unresizable app.
- setUpDisplaySizeWithApp(2500, 1000);
- mActivity.mDisplayContent.setSandboxDisplayApis(true /* sandboxDisplayApis */);
- prepareUnresizable(mActivity, 1.5f, SCREEN_ORIENTATION_LANDSCAPE);
- assertFitted();
-
- // Activity max bounds should be sandboxed since sandboxing is enabled.
- assertActivityMaxBoundsSandboxed();
- }
-
- @Test
public void testResizableApp_notSandboxed() {
// Set up a display in landscape with a fully resizable app.
setUpDisplaySizeWithApp(2500, 1000);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 42ef086..a8e1753 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -484,7 +484,8 @@
mSupportsFreeformWindowManagement = true;
mSupportsPictureInPicture = true;
mDevEnableNonResizableMultiWindow = false;
- mMinPercentageMultiWindowSupportWidth = 0.3f;
+ mMinPercentageMultiWindowSupportHeight = 0.3f;
+ mMinPercentageMultiWindowSupportWidth = 0.5f;
mLargeScreenSmallestScreenWidthDp = 600;
mSupportsNonResizableMultiWindow = 0;
mRespectsActivityMinWidthHeightMultiWindow = 0;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index e9e2013..97afc16 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -214,8 +214,8 @@
spyOn(mDisplayContent);
spyOn(mDisplayContent.mInputMethodWindow);
when(task.getDisplayContent().shouldImeAttachedToApp()).thenReturn(true);
- // Intentionally set the IME window is in drawn state.
- doReturn(true).when(mDisplayContent.mInputMethodWindow).isDrawn();
+ // Intentionally set the IME window is in visible state.
+ doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible();
// Verify no NPE happens when calling createTaskSnapshot.
try {
final TaskSnapshot.Builder builder = new TaskSnapshot.Builder();
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 d93ebb3..0ebff1d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -60,6 +60,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
@@ -121,8 +122,8 @@
@Test
public void testRemoveContainer() {
- final Task taskController1 = createTask(mDisplayContent);
- final Task task = createTaskInRootTask(taskController1, 0 /* userId */);
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
task.removeIfPossible();
@@ -130,12 +131,14 @@
assertNull(task.getParent());
assertEquals(0, task.getChildCount());
assertNull(activity.getParent());
+ verify(mAtm.getLockTaskController(), atLeast(1)).clearLockedTask(task);
+ verify(mAtm.getLockTaskController(), atLeast(1)).clearLockedTask(rootTask);
}
@Test
public void testRemoveContainer_deferRemoval() {
- final Task taskController1 = createTask(mDisplayContent);
- final Task task = createTaskInRootTask(taskController1, 0 /* userId */);
+ final Task rootTask = createTask(mDisplayContent);
+ final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
doReturn(true).when(task).shouldDeferRemoval();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 111449d..5880899 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -805,6 +805,7 @@
private Bundle mIntentExtras;
private boolean mOnTop = false;
private ActivityInfo.WindowLayout mWindowLayout;
+ private boolean mVisible = true;
ActivityBuilder(ActivityTaskManagerService service) {
mService = service;
@@ -930,6 +931,11 @@
return this;
}
+ ActivityBuilder setVisible(boolean visible) {
+ mVisible = visible;
+ return this;
+ }
+
ActivityRecord build() {
SystemServicesTestRule.checkHoldsLock(mService.mGlobalLock);
try {
@@ -1012,9 +1018,10 @@
// root tasks (e.g. home root task).
mTask.moveToFront("createActivity");
}
- // Make visible by default...
- activity.mVisibleRequested = true;
- activity.setVisible(true);
+ if (mVisible) {
+ activity.mVisibleRequested = true;
+ activity.setVisible(true);
+ }
}
final WindowProcessController wpc;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 128602d..1b84927 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -2226,7 +2226,14 @@
}
@Override
- public long getLastTimeAnyComponentUsed(String packageName) {
+ public long getLastTimeAnyComponentUsed(String packageName, String callingPackage) {
+ if (!hasPermissions(
+ callingPackage, android.Manifest.permission.INTERACT_ACROSS_USERS)) {
+ throw new SecurityException("Caller doesn't have INTERACT_ACROSS_USERS permission");
+ }
+ if (!hasPermission(callingPackage)) {
+ throw new SecurityException("Don't have permission to query usage stats");
+ }
synchronized (mLock) {
// Truncate the returned milliseconds to the boundary of the last day before exact
// time for privacy reasons.
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index eac21b4..5183e5b 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -863,7 +863,12 @@
}
private void enforceCallingPermission(String permission) {
- PermissionUtil.checkPermissionForPreflight(mContext, mOriginatorIdentity, permission);
+ if (PermissionUtil.checkPermissionForPreflight(mContext, mOriginatorIdentity,
+ permission) != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Identity " + mOriginatorIdentity + " does not have permission "
+ + permission);
+ }
}
private void enforceDetectionPermissions(ComponentName detectionService) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 17303a4..beaca68 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -29,11 +29,7 @@
import android.content.Intent;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.SoundTrigger;
-import android.media.AudioAttributes;
import android.media.AudioFormat;
-import android.media.AudioManager;
-import android.media.AudioRecord;
-import android.media.MediaRecorder;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -66,12 +62,16 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
+import java.time.Duration;
+import java.time.Instant;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Function;
/**
* A class that provides the communication with the HotwordDetectionService.
@@ -81,33 +81,38 @@
// TODO (b/177502877): Set the Debug flag to false before shipping.
private static final boolean DEBUG = true;
- // Number of bytes per sample of audio (which is a short).
- private static final int BYTES_PER_SAMPLE = 2;
// TODO: These constants need to be refined.
private static final long VALIDATION_TIMEOUT_MILLIS = 3000;
- private static final long VOICE_INTERACTION_TIMEOUT_TO_OPEN_MIC_MILLIS = 2000;
- private static final int MAX_STREAMING_SECONDS = 10;
- private static final int MICROPHONE_BUFFER_LENGTH_SECONDS = 8;
- private static final int HOTWORD_AUDIO_LENGTH_SECONDS = 3;
private static final long MAX_UPDATE_TIMEOUT_MILLIS = 6000;
+ private static final Duration MAX_UPDATE_TIMEOUT_DURATION =
+ Duration.ofMillis(MAX_UPDATE_TIMEOUT_MILLIS);
private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool();
// TODO: This may need to be a Handler(looper)
private final ScheduledExecutorService mScheduledExecutorService =
Executors.newSingleThreadScheduledExecutor();
- private final AtomicBoolean mUpdateStateFinish = new AtomicBoolean(false);
+ private final AtomicBoolean mUpdateStateAfterStartFinished = new AtomicBoolean(false);
+ private final @NonNull ServiceConnectionFactory mServiceConnectionFactory;
final Object mLock;
final int mVoiceInteractionServiceUid;
final ComponentName mDetectionComponentName;
final int mUser;
final Context mContext;
- final @NonNull ServiceConnector<IHotwordDetectionService> mRemoteHotwordDetectionService;
- boolean mBound;
volatile HotwordDetectionServiceIdentity mIdentity;
+ private IHotwordRecognitionStatusCallback mCallback;
+ private IMicrophoneHotwordDetectionVoiceInteractionCallback mSoftwareCallback;
+ private Instant mLastRestartInstant;
+
+ private ScheduledFuture<?> mCancellationTaskFuture;
@GuardedBy("mLock")
private ParcelFileDescriptor mCurrentAudioSink;
+ @GuardedBy("mLock")
+ private boolean mValidatingDspTrigger = false;
+ @GuardedBy("mLock")
+ private boolean mPerformingSoftwareHotwordDetection;
+ private @NonNull ServiceConnection mRemoteHotwordDetectionService;
HotwordDetectionConnection(Object lock, Context context, int voiceInteractionServiceUid,
ComponentName serviceName, int userId, boolean bindInstantServiceAllowed,
@@ -121,50 +126,36 @@
final Intent intent = new Intent(HotwordDetectionService.SERVICE_INTERFACE);
intent.setComponent(mDetectionComponentName);
- mRemoteHotwordDetectionService = new ServiceConnector.Impl<IHotwordDetectionService>(
- mContext, intent, bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0, mUser,
- IHotwordDetectionService.Stub::asInterface) {
- @Override // from ServiceConnector.Impl
- protected void onServiceConnectionStatusChanged(IHotwordDetectionService service,
- boolean connected) {
- if (DEBUG) {
- Slog.d(TAG, "onServiceConnectionStatusChanged connected = " + connected);
- }
- synchronized (mLock) {
- mBound = connected;
- }
- }
+ mServiceConnectionFactory = new ServiceConnectionFactory(intent, bindInstantServiceAllowed);
- @Override
- protected long getAutoDisconnectTimeoutMs() {
- return -1;
- }
+ mRemoteHotwordDetectionService = mServiceConnectionFactory.create();
- @Override
- public void binderDied() {
- super.binderDied();
- Slog.w(TAG, "binderDied");
- try {
- callback.onError(-1);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to report onError status: " + e);
- }
- }
- };
- mRemoteHotwordDetectionService.connect();
if (callback == null) {
updateStateLocked(options, sharedMemory);
return;
}
- updateAudioFlinger();
- updateContentCaptureManager();
- updateStateWithCallbackLocked(options, sharedMemory, callback);
+ mCallback = callback;
+
+ mLastRestartInstant = Instant.now();
+ updateStateAfterProcessStart(options, sharedMemory);
+
+ // TODO(volnov): we need to be smarter here, e.g. schedule it a bit more often, but wait
+ // until the current session is closed.
+ mCancellationTaskFuture = mScheduledExecutorService.scheduleAtFixedRate(() -> {
+ if (DEBUG) {
+ Slog.i(TAG, "Time to restart the process, TTL has passed");
+ }
+
+ synchronized (mLock) {
+ restartProcessLocked();
+ }
+ }, 30, 30, TimeUnit.MINUTES);
}
- private void updateStateWithCallbackLocked(PersistableBundle options,
- SharedMemory sharedMemory, IHotwordRecognitionStatusCallback callback) {
+ private void updateStateAfterProcessStart(
+ PersistableBundle options, SharedMemory sharedMemory) {
if (DEBUG) {
- Slog.d(TAG, "updateStateWithCallbackLocked");
+ Slog.d(TAG, "updateStateAfterProcessStart");
}
mRemoteHotwordDetectionService.postAsync(service -> {
AndroidFuture<Void> future = new AndroidFuture<>();
@@ -183,21 +174,21 @@
mIdentity =
new HotwordDetectionServiceIdentity(uid, mVoiceInteractionServiceUid);
future.complete(null);
+ if (mUpdateStateAfterStartFinished.getAndSet(true)) {
+ Slog.w(TAG, "call callback after timeout");
+ return;
+ }
+ int status = bundle != null ? bundle.getInt(
+ KEY_INITIALIZATION_STATUS,
+ INITIALIZATION_STATUS_UNKNOWN)
+ : INITIALIZATION_STATUS_UNKNOWN;
+ // Add the protection to avoid unexpected status
+ if (status > HotwordDetectionService.getMaxCustomInitializationStatus()
+ && status != INITIALIZATION_STATUS_UNKNOWN) {
+ status = INITIALIZATION_STATUS_UNKNOWN;
+ }
try {
- if (mUpdateStateFinish.getAndSet(true)) {
- Slog.w(TAG, "call callback after timeout");
- return;
- }
- int status = bundle != null ? bundle.getInt(
- KEY_INITIALIZATION_STATUS,
- INITIALIZATION_STATUS_UNKNOWN)
- : INITIALIZATION_STATUS_UNKNOWN;
- // Add the protection to avoid unexpected status
- if (status > HotwordDetectionService.getMaxCustomInitializationStatus()
- && status != INITIALIZATION_STATUS_UNKNOWN) {
- status = INITIALIZATION_STATUS_UNKNOWN;
- }
- callback.onStatusReported(status);
+ mCallback.onStatusReported(status);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to report initialization status: " + e);
}
@@ -214,13 +205,13 @@
.whenComplete((res, err) -> {
if (err instanceof TimeoutException) {
Slog.w(TAG, "updateState timed out");
+ if (mUpdateStateAfterStartFinished.getAndSet(true)) {
+ return;
+ }
try {
- if (mUpdateStateFinish.getAndSet(true)) {
- return;
- }
- callback.onStatusReported(INITIALIZATION_STATUS_UNKNOWN);
+ mCallback.onStatusReported(INITIALIZATION_STATUS_UNKNOWN);
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to report initialization status: " + e);
+ Slog.w(TAG, "Failed to report initialization status UNKNOWN", e);
}
} else if (err != null) {
Slog.w(TAG, "Failed to update state: " + err);
@@ -230,27 +221,9 @@
});
}
- private void updateAudioFlinger() {
- // TODO: Consider using a proxy that limits the exposed API surface.
- IBinder audioFlinger = ServiceManager.getService("media.audio_flinger");
- if (audioFlinger == null) {
- throw new IllegalStateException("Service media.audio_flinger wasn't found.");
- }
- mRemoteHotwordDetectionService.post(service -> service.updateAudioFlinger(audioFlinger));
- }
-
- private void updateContentCaptureManager() {
- IBinder b = ServiceManager
- .getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
- IContentCaptureManager binderService = IContentCaptureManager.Stub.asInterface(b);
- mRemoteHotwordDetectionService.post(
- service -> service.updateContentCaptureManager(binderService,
- new ContentCaptureOptions(null)));
- }
-
private boolean isBound() {
synchronized (mLock) {
- return mBound;
+ return mRemoteHotwordDetectionService.isBound();
}
}
@@ -258,18 +231,25 @@
if (DEBUG) {
Slog.d(TAG, "cancelLocked");
}
- if (mBound) {
+ if (mRemoteHotwordDetectionService.isBound()) {
mRemoteHotwordDetectionService.unbind();
- mBound = false;
LocalServices.getService(PermissionManagerServiceInternal.class)
.setHotwordDetectionServiceProvider(null);
mIdentity = null;
}
+ mCancellationTaskFuture.cancel(/* may interrupt */ true);
}
void updateStateLocked(PersistableBundle options, SharedMemory sharedMemory) {
- mRemoteHotwordDetectionService.run(
- service -> service.updateState(options, sharedMemory, null /* callback */));
+ // Prevent doing the init late, so restart is handled equally to a clean process start.
+ // TODO(b/191742511): this logic needs a test
+ if (!mUpdateStateAfterStartFinished.get()
+ && Instant.now().minus(MAX_UPDATE_TIMEOUT_DURATION).isBefore(mLastRestartInstant)) {
+ updateStateAfterProcessStart(options, sharedMemory);
+ } else {
+ mRemoteHotwordDetectionService.run(
+ service -> service.updateState(options, sharedMemory, null /* callback */));
+ }
}
void startListeningFromMic(
@@ -278,7 +258,20 @@
if (DEBUG) {
Slog.d(TAG, "startListeningFromMic");
}
+ mSoftwareCallback = callback;
+ synchronized (mLock) {
+ if (mPerformingSoftwareHotwordDetection) {
+ Slog.i(TAG, "Hotword validation is already in progress, ignoring.");
+ return;
+ }
+ mPerformingSoftwareHotwordDetection = true;
+
+ startListeningFromMicLocked();
+ }
+ }
+
+ private void startListeningFromMicLocked() {
// TODO: consider making this a non-anonymous class.
IDspHotwordDetectionCallback internalCallback = new IDspHotwordDetectionCallback.Stub() {
@Override
@@ -286,15 +279,22 @@
if (DEBUG) {
Slog.d(TAG, "onDetected");
}
- callback.onDetected(result, null, null);
+ synchronized (mLock) {
+ if (mPerformingSoftwareHotwordDetection) {
+ mSoftwareCallback.onDetected(result, null, null);
+ mPerformingSoftwareHotwordDetection = false;
+ } else {
+ Slog.i(TAG, "Hotword detection has already completed");
+ }
+ }
}
@Override
public void onRejected(HotwordRejectedResult result) throws RemoteException {
if (DEBUG) {
- Slog.d(TAG, "onRejected");
+ Slog.wtf(TAG, "onRejected");
}
- // onRejected isn't allowed here
+ // onRejected isn't allowed here, and we are not expecting it.
}
};
@@ -315,6 +315,7 @@
if (DEBUG) {
Slog.d(TAG, "startListeningFromExternalSource");
}
+
handleExternalSourceHotwordDetection(
audioStream,
audioFormat,
@@ -326,18 +327,27 @@
if (DEBUG) {
Slog.d(TAG, "stopListening");
}
-
- mRemoteHotwordDetectionService.run(service -> service.stopDetection());
-
synchronized (mLock) {
- if (mCurrentAudioSink != null) {
- Slog.i(TAG, "Closing audio stream to hotword detector: stopping requested");
- bestEffortClose(mCurrentAudioSink);
- }
- mCurrentAudioSink = null;
+ stopListeningLocked();
}
}
+ private void stopListeningLocked() {
+ if (!mPerformingSoftwareHotwordDetection) {
+ Slog.i(TAG, "Hotword detection is not running");
+ return;
+ }
+ mPerformingSoftwareHotwordDetection = false;
+
+ mRemoteHotwordDetectionService.run(IHotwordDetectionService::stopDetection);
+
+ if (mCurrentAudioSink != null) {
+ Slog.i(TAG, "Closing audio stream to hotword detector: stopping requested");
+ bestEffortClose(mCurrentAudioSink);
+ }
+ mCurrentAudioSink = null;
+ }
+
void triggerHardwareRecognitionEventForTestLocked(
SoundTrigger.KeyphraseRecognitionEvent event,
IHotwordRecognitionStatusCallback callback) {
@@ -358,7 +368,14 @@
if (DEBUG) {
Slog.d(TAG, "onDetected");
}
- externalCallback.onKeyphraseDetected(recognitionEvent, result);
+ synchronized (mLock) {
+ if (mValidatingDspTrigger) {
+ mValidatingDspTrigger = false;
+ externalCallback.onKeyphraseDetected(recognitionEvent, result);
+ } else {
+ Slog.i(TAG, "Ignored hotword detected since trigger has been handled");
+ }
+ }
}
@Override
@@ -366,16 +383,26 @@
if (DEBUG) {
Slog.d(TAG, "onRejected");
}
- externalCallback.onRejected(result);
+ synchronized (mLock) {
+ if (mValidatingDspTrigger) {
+ mValidatingDspTrigger = false;
+ externalCallback.onRejected(result);
+ } else {
+ Slog.i(TAG, "Ignored hotword rejected since trigger has been handled");
+ }
+ }
}
};
- mRemoteHotwordDetectionService.run(
- service -> service.detectFromDspSource(
- recognitionEvent,
- recognitionEvent.getCaptureFormat(),
- VALIDATION_TIMEOUT_MILLIS,
- internalCallback));
+ synchronized (mLock) {
+ mValidatingDspTrigger = true;
+ mRemoteHotwordDetectionService.run(
+ service -> service.detectFromDspSource(
+ recognitionEvent,
+ recognitionEvent.getCaptureFormat(),
+ VALIDATION_TIMEOUT_MILLIS,
+ internalCallback));
+ }
}
private void detectFromDspSource(SoundTrigger.KeyphraseRecognitionEvent recognitionEvent,
@@ -391,7 +418,14 @@
if (DEBUG) {
Slog.d(TAG, "onDetected");
}
- externalCallback.onKeyphraseDetected(recognitionEvent, result);
+ synchronized (mLock) {
+ if (!mValidatingDspTrigger) {
+ Slog.i(TAG, "Ignoring #onDetected due to a process restart");
+ return;
+ }
+ mValidatingDspTrigger = false;
+ externalCallback.onKeyphraseDetected(recognitionEvent, result);
+ }
}
@Override
@@ -399,16 +433,88 @@
if (DEBUG) {
Slog.d(TAG, "onRejected");
}
- externalCallback.onRejected(result);
+ synchronized (mLock) {
+ if (!mValidatingDspTrigger) {
+ Slog.i(TAG, "Ignoring #onRejected due to a process restart");
+ return;
+ }
+ mValidatingDspTrigger = false;
+ externalCallback.onRejected(result);
+ }
}
};
- mRemoteHotwordDetectionService.run(
- service -> service.detectFromDspSource(
- recognitionEvent,
- recognitionEvent.getCaptureFormat(),
- VALIDATION_TIMEOUT_MILLIS,
- internalCallback));
+ synchronized (mLock) {
+ mValidatingDspTrigger = true;
+ mRemoteHotwordDetectionService.run(
+ service -> service.detectFromDspSource(
+ recognitionEvent,
+ recognitionEvent.getCaptureFormat(),
+ VALIDATION_TIMEOUT_MILLIS,
+ internalCallback));
+ }
+ }
+
+ void forceRestart() {
+ if (DEBUG) {
+ Slog.i(TAG, "Requested to restart the service internally. Performing the restart");
+ }
+ synchronized (mLock) {
+ restartProcessLocked();
+ }
+ }
+
+ private void restartProcessLocked() {
+ if (DEBUG) {
+ Slog.i(TAG, "Restarting hotword detection process");
+ }
+
+ ServiceConnection oldConnection = mRemoteHotwordDetectionService;
+
+ // TODO(volnov): this can be done after connect() has been successful.
+ if (mValidatingDspTrigger) {
+ // We're restarting the process while it's processing a DSP trigger, so report a
+ // rejection. This also allows the Interactor to startReco again
+ try {
+ mCallback.onRejected(new HotwordRejectedResult.Builder().build());
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call #rejected");
+ }
+ mValidatingDspTrigger = false;
+ }
+
+ mUpdateStateAfterStartFinished.set(false);
+ mLastRestartInstant = Instant.now();
+
+ // Recreate connection to reset the cache.
+ mRemoteHotwordDetectionService = mServiceConnectionFactory.create();
+
+ if (DEBUG) {
+ Slog.i(TAG, "Started the new process, issuing #onProcessRestarted");
+ }
+ try {
+ mCallback.onProcessRestarted();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to communicate #onProcessRestarted", e);
+ }
+
+ // Restart listening from microphone if the hotword process has been restarted.
+ if (mPerformingSoftwareHotwordDetection) {
+ Slog.i(TAG, "Process restarted: calling startRecognition() again");
+ startListeningFromMicLocked();
+ }
+
+ if (mCurrentAudioSink != null) {
+ Slog.i(TAG, "Closing external audio stream to hotword detector: process restarted");
+ bestEffortClose(mCurrentAudioSink);
+ mCurrentAudioSink = null;
+ }
+
+ if (DEBUG) {
+ Slog.i(TAG, "#onProcessRestarted called, unbinding from the old process");
+ }
+ oldConnection.ignoreConnectionStatusEvents();
+ oldConnection.unbind();
}
static final class SoundTriggerCallback extends IRecognitionStatusCallback.Stub {
@@ -462,139 +568,13 @@
}
}
- // TODO: figure out if we need to let the client configure some of the parameters.
- private static AudioRecord createAudioRecord(
- @NonNull SoundTrigger.KeyphraseRecognitionEvent recognitionEvent) {
- int sampleRate = recognitionEvent.getCaptureFormat().getSampleRate();
- return new AudioRecord(
- new AudioAttributes.Builder()
- .setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD).build(),
- recognitionEvent.getCaptureFormat(),
- getBufferSizeInBytes(
- sampleRate,
- MAX_STREAMING_SECONDS,
- recognitionEvent.getCaptureFormat().getChannelCount()),
- recognitionEvent.getCaptureSession());
- }
-
- @Nullable
- private AudioRecord createMicAudioRecord(AudioFormat audioFormat) {
- if (DEBUG) {
- Slog.i(TAG, "#createAudioRecord");
- }
- try {
- AudioRecord audioRecord = new AudioRecord(
- new AudioAttributes.Builder()
- .setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD).build(),
- audioFormat,
- getBufferSizeInBytes(
- audioFormat.getSampleRate(),
- MICROPHONE_BUFFER_LENGTH_SECONDS,
- audioFormat.getChannelCount()),
- AudioManager.AUDIO_SESSION_ID_GENERATE);
-
- if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
- Slog.w(TAG, "Failed to initialize AudioRecord");
- audioRecord.release();
- return null;
- }
-
- return audioRecord;
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, "Failed to create AudioRecord", e);
- return null;
- }
- }
-
- @Nullable
- private AudioRecord createFakeAudioRecord() {
- if (DEBUG) {
- Slog.i(TAG, "#createFakeAudioRecord");
- }
- try {
- AudioRecord audioRecord = new AudioRecord.Builder()
- .setAudioFormat(new AudioFormat.Builder()
- .setSampleRate(32000)
- .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
- .setChannelMask(AudioFormat.CHANNEL_IN_MONO).build())
- .setAudioAttributes(new AudioAttributes.Builder()
- .setInternalCapturePreset(MediaRecorder.AudioSource.HOTWORD).build())
- .setBufferSizeInBytes(
- AudioRecord.getMinBufferSize(32000,
- AudioFormat.CHANNEL_IN_MONO,
- AudioFormat.ENCODING_PCM_16BIT) * 2)
- .build();
-
- if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
- Slog.w(TAG, "Failed to initialize AudioRecord");
- audioRecord.release();
- return null;
- }
- return audioRecord;
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, "Failed to create AudioRecord", e);
- }
- return null;
- }
-
- /**
- * Returns the number of bytes required to store {@code bufferLengthSeconds} of audio sampled at
- * {@code sampleRate} Hz, using the format returned by DSP audio capture.
- */
- private static int getBufferSizeInBytes(
- int sampleRate, int bufferLengthSeconds, int intChannelCount) {
- return BYTES_PER_SAMPLE * sampleRate * bufferLengthSeconds * intChannelCount;
- }
-
- private static Pair<ParcelFileDescriptor, ParcelFileDescriptor> createPipe() {
- ParcelFileDescriptor[] fileDescriptors;
- try {
- fileDescriptors = ParcelFileDescriptor.createPipe();
- } catch (IOException e) {
- Slog.e(TAG, "Failed to create audio stream pipe", e);
- return null;
- }
-
- return Pair.create(fileDescriptors[0], fileDescriptors[1]);
- }
-
public void dump(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.print("mBound="); pw.println(mBound);
- }
-
- private interface AudioReader extends Closeable {
- int read(byte[] dest, int offset, int length) throws IOException;
-
- static AudioReader createFromInputStream(InputStream is) {
- return new AudioReader() {
- @Override
- public int read(byte[] dest, int offset, int length) throws IOException {
- return is.read(dest, offset, length);
- }
-
- @Override
- public void close() throws IOException {
- is.close();
- }
- };
- }
-
- static AudioReader createFromAudioRecord(AudioRecord record) {
- record.startRecording();
-
- return new AudioReader() {
- @Override
- public int read(byte[] dest, int offset, int length) throws IOException {
- return record.read(dest, offset, length);
- }
-
- @Override
- public void close() throws IOException {
- record.stop();
- record.release();
- }
- };
- }
+ pw.print(prefix);
+ pw.print("mBound=" + mRemoteHotwordDetectionService.isBound());
+ pw.print(", mValidatingDspTrigger=" + mValidatingDspTrigger);
+ pw.print(", mPerformingSoftwareHotwordDetection=" + mPerformingSoftwareHotwordDetection);
+ pw.print(", mRestartCount=" + mServiceConnectionFactory.mRestartCount);
+ pw.println(", mLastRestartInstant=" + mLastRestartInstant);
}
private void handleExternalSourceHotwordDetection(
@@ -605,8 +585,7 @@
if (DEBUG) {
Slog.d(TAG, "#handleExternalSourceHotwordDetection");
}
- AudioReader audioSource = AudioReader.createFromInputStream(
- new ParcelFileDescriptor.AutoCloseInputStream(audioStream));
+ InputStream audioSource = new ParcelFileDescriptor.AutoCloseInputStream(audioStream);
Pair<ParcelFileDescriptor, ParcelFileDescriptor> clientPipe = createPipe();
if (clientPipe == null) {
@@ -621,7 +600,7 @@
}
mAudioCopyExecutor.execute(() -> {
- try (AudioReader source = audioSource;
+ try (InputStream source = audioSource;
OutputStream fos =
new ParcelFileDescriptor.AutoCloseOutputStream(serviceAudioSink)) {
@@ -681,6 +660,150 @@
}));
}
+ private class ServiceConnectionFactory {
+ private final Intent mIntent;
+ private final int mBindingFlags;
+
+ private int mRestartCount = 0;
+
+ ServiceConnectionFactory(@NonNull Intent intent, boolean bindInstantServiceAllowed) {
+ mIntent = intent;
+ mBindingFlags = bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0;
+ }
+
+ ServiceConnection create() {
+ ServiceConnection connection =
+ new ServiceConnection(mContext, mIntent, mBindingFlags, mUser,
+ IHotwordDetectionService.Stub::asInterface, ++mRestartCount);
+ connection.connect();
+
+ updateAudioFlinger(connection);
+ updateContentCaptureManager(connection);
+ return connection;
+ }
+ }
+
+ private class ServiceConnection extends ServiceConnector.Impl<IHotwordDetectionService> {
+ private final Object mLock = new Object();
+
+ private final Intent mIntent;
+ private final int mBindingFlags;
+ private final int mInstanceNumber;
+
+ private boolean mRespectServiceConnectionStatusChanged = true;
+ private boolean mIsBound = false;
+
+ ServiceConnection(@NonNull Context context,
+ @NonNull Intent intent, int bindingFlags, int userId,
+ @Nullable Function<IBinder, IHotwordDetectionService> binderAsInterface,
+ int instanceNumber) {
+ super(context, intent, bindingFlags, userId, binderAsInterface);
+ this.mIntent = intent;
+ this.mBindingFlags = bindingFlags;
+ this.mInstanceNumber = instanceNumber;
+ }
+
+ @Override // from ServiceConnector.Impl
+ protected void onServiceConnectionStatusChanged(IHotwordDetectionService service,
+ boolean connected) {
+ if (DEBUG) {
+ Slog.d(TAG, "onServiceConnectionStatusChanged connected = " + connected);
+ }
+ synchronized (mLock) {
+ if (!mRespectServiceConnectionStatusChanged) {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignored onServiceConnectionStatusChanged event");
+ }
+ return;
+ }
+ mIsBound = connected;
+ }
+ }
+
+ @Override
+ protected long getAutoDisconnectTimeoutMs() {
+ return -1;
+ }
+
+ @Override
+ public void binderDied() {
+ super.binderDied();
+ synchronized (mLock) {
+ if (!mRespectServiceConnectionStatusChanged) {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignored #binderDied event");
+ }
+ return;
+ }
+
+ Slog.w(TAG, "binderDied");
+ try {
+ mCallback.onError(-1);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to report onError status: " + e);
+ }
+ }
+ }
+
+ @Override
+ protected boolean bindService(
+ @NonNull android.content.ServiceConnection serviceConnection) {
+ try {
+ return mContext.bindIsolatedService(
+ mIntent,
+ Context.BIND_AUTO_CREATE | mBindingFlags,
+ "hotword_detector_" + mInstanceNumber,
+ mExecutor,
+ serviceConnection);
+ } catch (IllegalArgumentException e) {
+ Slog.wtf(TAG, "Can't bind to the hotword detection service!", e);
+ return false;
+ }
+ }
+
+ boolean isBound() {
+ synchronized (mLock) {
+ return mIsBound;
+ }
+ }
+
+ void ignoreConnectionStatusEvents() {
+ synchronized (mLock) {
+ mRespectServiceConnectionStatusChanged = false;
+ }
+ }
+ }
+
+ private static Pair<ParcelFileDescriptor, ParcelFileDescriptor> createPipe() {
+ ParcelFileDescriptor[] fileDescriptors;
+ try {
+ fileDescriptors = ParcelFileDescriptor.createPipe();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to create audio stream pipe", e);
+ return null;
+ }
+
+ return Pair.create(fileDescriptors[0], fileDescriptors[1]);
+ }
+
+ private static void updateAudioFlinger(ServiceConnection connection) {
+ // TODO: Consider using a proxy that limits the exposed API surface.
+ IBinder audioFlinger = ServiceManager.getService("media.audio_flinger");
+ if (audioFlinger == null) {
+ throw new IllegalStateException("Service media.audio_flinger wasn't found.");
+ }
+ connection.post(service -> service.updateAudioFlinger(audioFlinger));
+ }
+
+ private static void updateContentCaptureManager(ServiceConnection connection) {
+ IBinder b = ServiceManager
+ .getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
+ IContentCaptureManager binderService = IContentCaptureManager.Stub.asInterface(b);
+ connection.post(
+ service -> service.updateContentCaptureManager(binderService,
+ new ContentCaptureOptions(null)));
+ }
+
private static void bestEffortClose(Closeable closeable) {
try {
closeable.close();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index bc812c2..162acba 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -795,6 +795,10 @@
Settings.Secure.ASSISTANT, null, userHandle);
}
+ void forceRestartHotwordDetector() {
+ mImpl.forceRestartHotwordDetector();
+ }
+
@Override
public void showSession(Bundle args, int flags) {
synchronized (this) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index ca30bc5..89c5a72 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -562,6 +562,14 @@
&& (serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) == 0;
}
+ void forceRestartHotwordDetector() {
+ if (mHotwordDetectionConnection == null) {
+ Slog.w(TAG, "Failed to force-restart hotword detection: no hotword detection active");
+ return;
+ }
+ mHotwordDetectionConnection.forceRestart();
+ }
+
public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!mValid) {
pw.print(" NOT VALID: ");
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
index 2e3ca01..cdd8f7b 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
@@ -54,6 +54,8 @@
return requestHide(pw);
case "disable":
return requestDisable(pw);
+ case "restart-detection":
+ return requestRestartDetection(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -74,6 +76,8 @@
pw.println("");
pw.println(" disable [true|false]");
pw.println(" Temporarily disable (when true) service");
+ pw.println(" restart-detection");
+ pw.println(" Force a restart of a hotword detection service");
pw.println("");
}
}
@@ -143,6 +147,16 @@
return 0;
}
+ private int requestRestartDetection(PrintWriter pw) {
+ Slog.i(TAG, "requestRestartDetection()");
+ try {
+ mService.forceRestartHotwordDetector();
+ } catch (Exception e) {
+ return handleError(pw, "requestRestartDetection()", e);
+ }
+ return 0;
+ }
+
private static int handleError(PrintWriter pw, String message, Exception e) {
Slog.e(TAG, "error calling " + message, e);
pw.printf("Error calling %s: %s\n", message, e);
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 1953af4..e000265 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1018,6 +1018,16 @@
// this magic number is a bug ID
public static final long ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION = 157233955L;
+ /**
+ * Enable READ_PHONE_NUMBERS or READ_PRIVILEGED_PHONE_STATE protections on
+ * {@link TelecomManager#getPhoneAccount(PhoneAccountHandle)}.
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+ // bug ID
+ public static final long ENABLE_GET_PHONE_ACCOUNT_PERMISSION_PROTECTION = 183407956L;
+
private static final String TAG = "TelecomManager";
@@ -1351,6 +1361,9 @@
* Return the {@link PhoneAccount} for a specified {@link PhoneAccountHandle}. Object includes
* resources which can be used in a user interface.
*
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_NUMBERS} for applications targeting API
+ * level 31+.
* @param account The {@link PhoneAccountHandle}.
* @return The {@link PhoneAccount} object.
*/
@@ -1358,7 +1371,7 @@
ITelecomService service = getTelecomService();
if (service != null) {
try {
- return service.getPhoneAccount(account);
+ return service.getPhoneAccount(account, mContext.getPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#getPhoneAccount", e);
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 18afde7..6f286d9 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -79,7 +79,7 @@
/**
* @see TelecomManager#getPhoneAccount
*/
- PhoneAccount getPhoneAccount(in PhoneAccountHandle account);
+ PhoneAccount getPhoneAccount(in PhoneAccountHandle account, String callingPackage);
/**
* @see TelecomManager#getAllPhoneAccountsCount
diff --git a/tests/Internal/src/android/app/WallpaperColorsTest.java b/tests/Internal/src/android/app/WallpaperColorsTest.java
index 45d3dade..9ffb236 100644
--- a/tests/Internal/src/android/app/WallpaperColorsTest.java
+++ b/tests/Internal/src/android/app/WallpaperColorsTest.java
@@ -20,6 +20,7 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.os.Parcel;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -106,4 +107,26 @@
// This would crash:
canvas.drawBitmap(image, 0, 0, new Paint());
}
+
+ /**
+ * Parcelled WallpaperColors object should equal the original.
+ */
+ @Test
+ public void testParcelUnparcel() {
+ Bitmap image = Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888);
+ WallpaperColors colors = WallpaperColors.fromBitmap(image);
+ Parcel parcel = Parcel.obtain();
+ colors.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ WallpaperColors reconstructed = new WallpaperColors(parcel);
+ parcel.recycle();
+ Assert.assertEquals("WallpaperColors recreated from Parcel should equal original",
+ colors, reconstructed);
+ Assert.assertEquals("getAllColors() on WallpaperColors recreated from Parcel should"
+ + "return the same as the original",
+ colors.getAllColors(), reconstructed.getAllColors());
+ Assert.assertEquals("getMainColors() on WallpaperColors recreated from Parcel should"
+ + "return the same as the original",
+ colors.getMainColors(), reconstructed.getMainColors());
+ }
}
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index c679d04..c563e06 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -52,6 +52,7 @@
data: [
":com.android.apex.apkrollback.test_v1",
":com.android.apex.cts.shim.v2_prebuilt",
+ ":StagedInstallTestApexV2_WrongSha",
":TestAppAv1",
],
test_suites: ["general-tests"],
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index e633c87..6a62304 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -179,6 +179,26 @@
assertThat(info.isStagedSessionFailed()).isTrue();
}
+ @Test
+ public void testApexActivationFailureIsCapturedInSession_Commit() throws Exception {
+ int sessionId = Install.single(TestApp.Apex1).setStaged().commit();
+ assertSessionReady(sessionId);
+ storeSessionId(sessionId);
+ }
+
+ @Test
+ public void testApexActivationFailureIsCapturedInSession_Verify() throws Exception {
+ int sessionId = retrieveLastSessionId();
+ assertSessionFailedWithMessage(sessionId, "has unexpected SHA512 hash");
+ }
+
+ private static void assertSessionFailedWithMessage(int sessionId, String msg) {
+ assertSessionState(sessionId, (session) -> {
+ assertThat(session.isStagedSessionFailed()).isTrue();
+ assertThat(session.getStagedSessionErrorMessage()).contains(msg);
+ });
+ }
+
private static void assertSessionReady(int sessionId) {
assertSessionState(sessionId,
(session) -> assertThat(session.isStagedSessionReady()).isTrue());
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index ccd63f9..5d7fdd1 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -56,6 +56,7 @@
@Rule
public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this);
private static final String SHIM_V2 = "com.android.apex.cts.shim.v2.apex";
+ private static final String APEX_WRONG_SHA = "com.android.apex.cts.shim.v2_wrong_sha.apex";
private static final String APK_A = "TestAppAv1.apk";
private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test";
@@ -322,6 +323,27 @@
runPhase("testFailStagedSessionIfStagingDirectoryDeleted_Verify");
}
+ @Test
+ public void testApexActivationFailureIsCapturedInSession() throws Exception {
+ // We initiate staging a normal apex update which passes pre-reboot verification.
+ // Then we replace the valid apex waiting in /data/app-staging with something
+ // that cannot be activated and reboot. The apex should fail to activate, which
+ // is what we want for this test.
+ runPhase("testApexActivationFailureIsCapturedInSession_Commit");
+ final String sessionId = getDevice().executeShellCommand(
+ "pm get-stagedsessions --only-ready --only-parent --only-sessionid").trim();
+ assertThat(sessionId).isNotEmpty();
+ // Now replace the valid staged apex with something invalid
+ getDevice().enableAdbRoot();
+ getDevice().executeShellCommand("rm /data/app-staging/session_" + sessionId + "/*");
+ final File invalidApexFile = mHostUtils.getTestFile(APEX_WRONG_SHA);
+ getDevice().pushFile(invalidApexFile,
+ "/data/app-staging/session_" + sessionId + "/base.apex");
+ getDevice().reboot();
+
+ runPhase("testApexActivationFailureIsCapturedInSession_Verify");
+ }
+
private List<String> getStagingDirectories() throws DeviceNotAvailableException {
String baseDir = "/data/app-staging";
try {
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 4ce78aa..dc338ae 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -20,6 +20,8 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -70,6 +72,14 @@
public static final String GATEWAY_CONNECTION_NAME_PREFIX = "gatewayConnectionName-";
private static int sGatewayConnectionConfigCount = 0;
+ private static VcnGatewayConnectionConfig buildTestConfig(
+ String gatewayConnectionName, IkeTunnelConnectionParams tunnelConnectionParams) {
+ return buildTestConfigWithExposedCaps(
+ new VcnGatewayConnectionConfig.Builder(
+ gatewayConnectionName, tunnelConnectionParams),
+ EXPOSED_CAPS);
+ }
+
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfig() {
return buildTestConfigWithExposedCaps(EXPOSED_CAPS);
@@ -83,10 +93,9 @@
TUNNEL_CONNECTION_PARAMS);
}
- // Public for use in VcnGatewayConnectionTest
- public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
- final VcnGatewayConnectionConfig.Builder builder =
- newBuilder().setRetryIntervalsMillis(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU);
+ private static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(
+ VcnGatewayConnectionConfig.Builder builder, int... exposedCaps) {
+ builder.setRetryIntervalsMillis(RETRY_INTERVALS_MS).setMaxMtu(MAX_MTU);
for (int caps : exposedCaps) {
builder.addExposedCapability(caps);
@@ -95,6 +104,11 @@
return builder.build();
}
+ // Public for use in VcnGatewayConnectionTest
+ public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
+ return buildTestConfigWithExposedCaps(newBuilder(), exposedCaps);
+ }
+
@Test
public void testBuilderRequiresNonNullGatewayConnectionName() {
try {
@@ -193,4 +207,46 @@
assertEquals(config, new VcnGatewayConnectionConfig(config.toPersistableBundle()));
}
+
+ private static IkeTunnelConnectionParams buildTunnelConnectionParams(String ikePsk) {
+ final IkeSessionParams ikeParams =
+ IkeSessionParamsUtilsTest.createBuilderMinimum()
+ .setAuthPsk(ikePsk.getBytes())
+ .build();
+ return TunnelConnectionParamsUtilsTest.buildTestParams(ikeParams);
+ }
+
+ @Test
+ public void testTunnelConnectionParamsEquals() throws Exception {
+ final String connectionName = "testTunnelConnectionParamsEquals.connectionName";
+ final String psk = "testTunnelConnectionParamsEquals.psk";
+
+ final IkeTunnelConnectionParams tunnelParams = buildTunnelConnectionParams(psk);
+ final VcnGatewayConnectionConfig config = buildTestConfig(connectionName, tunnelParams);
+
+ final IkeTunnelConnectionParams anotherTunnelParams = buildTunnelConnectionParams(psk);
+ final VcnGatewayConnectionConfig anotherConfig =
+ buildTestConfig(connectionName, anotherTunnelParams);
+
+ assertNotSame(tunnelParams, anotherTunnelParams);
+ assertEquals(tunnelParams, anotherTunnelParams);
+ assertEquals(config, anotherConfig);
+ }
+
+ @Test
+ public void testTunnelConnectionParamsNotEquals() throws Exception {
+ final String connectionName = "testTunnelConnectionParamsNotEquals.connectionName";
+
+ final IkeTunnelConnectionParams tunnelParams =
+ buildTunnelConnectionParams("testTunnelConnectionParamsNotEquals.pskA");
+ final VcnGatewayConnectionConfig config = buildTestConfig(connectionName, tunnelParams);
+
+ final IkeTunnelConnectionParams anotherTunnelParams =
+ buildTunnelConnectionParams("testTunnelConnectionParamsNotEquals.pskB");
+ final VcnGatewayConnectionConfig anotherConfig =
+ buildTestConfig(connectionName, anotherTunnelParams);
+
+ assertNotEquals(tunnelParams, anotherTunnelParams);
+ assertNotEquals(config, anotherConfig);
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
index a88f112..6940765 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
@@ -16,10 +16,16 @@
package com.android.server.vcn;
+import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -33,16 +39,20 @@
@SmallTest
public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnectionTestBase {
private long mFirstRetryInterval;
+ private VcnNetworkAgent mNetworkAgent;
@Before
public void setUp() throws Exception {
super.setUp();
mFirstRetryInterval = mConfig.getRetryIntervalsMillis()[0];
+ mNetworkAgent = mock(VcnNetworkAgent.class);
mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
mGatewayConnection.transitionTo(mGatewayConnection.mRetryTimeoutState);
mTestLooper.dispatchAll();
+
+ mGatewayConnection.setNetworkAgent(mNetworkAgent);
}
@Test
@@ -54,6 +64,9 @@
assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
verifyRetryTimeoutAlarmAndGetCallback(mFirstRetryInterval, true /* expectCanceled */);
+
+ assertNotNull(mGatewayConnection.getNetworkAgent());
+ verify(mNetworkAgent, never()).unregister();
}
@Test
@@ -65,6 +78,9 @@
assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
verifyRetryTimeoutAlarmAndGetCallback(mFirstRetryInterval, false /* expectCanceled */);
+
+ assertNotNull(mGatewayConnection.getNetworkAgent());
+ verify(mNetworkAgent, never()).unregister();
}
@Test
@@ -76,6 +92,9 @@
assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
verifyRetryTimeoutAlarmAndGetCallback(mFirstRetryInterval, true /* expectCanceled */);
+
+ assertNull(mGatewayConnection.getNetworkAgent());
+ verify(mNetworkAgent).unregister();
}
@Test
@@ -93,6 +112,9 @@
assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
verifyRetryTimeoutAlarmAndGetCallback(mFirstRetryInterval, true /* expectCanceled */);
+
+ assertNotNull(mGatewayConnection.getNetworkAgent());
+ verify(mNetworkAgent, never()).unregister();
}
@Test
@@ -108,6 +130,9 @@
assertNull(mGatewayConnection.getCurrentState());
assertTrue(mGatewayConnection.isQuitting());
+
+ assertNull(mGatewayConnection.getNetworkAgent());
+ verify(mNetworkAgent).unregister();
}
@Test
@@ -117,5 +142,8 @@
assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
assertFalse(mGatewayConnection.isQuitting());
+
+ assertNull(mGatewayConnection.getNetworkAgent());
+ verify(mNetworkAgent).unregister();
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index a4f95e0..83610e0 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -24,8 +24,12 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
+
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Matchers.eq;
@@ -191,4 +195,23 @@
verify(mDisconnectRequestAlarm).cancel();
}
+
+ @Test
+ public void testQuittingCleansUpPersistentState() {
+ final VcnIkeSession vcnIkeSession = mock(VcnIkeSession.class);
+ final VcnNetworkAgent vcnNetworkAgent = mock(VcnNetworkAgent.class);
+
+ mGatewayConnection.setIkeSession(vcnIkeSession);
+ mGatewayConnection.setNetworkAgent(vcnNetworkAgent);
+
+ mGatewayConnection.quitNow();
+ mTestLooper.dispatchAll();
+
+ assertNull(mGatewayConnection.getIkeSession());
+ verify(vcnIkeSession).kill();
+ assertNull(mGatewayConnection.getNetworkAgent());
+ verify(vcnNetworkAgent).unregister();
+
+ verifyWakeLockReleased();
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index f681ee1..5d2f9d7 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -242,6 +242,27 @@
verifyUpdateSubscriptionSnapshotNotifiesGatewayConnections(VCN_STATUS_CODE_SAFE_MODE);
}
+ @Test
+ public void testSubscriptionSnapshotUpdatesMobileDataState() {
+ final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+ startVcnGatewayWithCapabilities(requestListener, TEST_CAPS[0]);
+
+ // Expect mobile data enabled from setUp()
+ assertTrue(mVcn.isMobileDataEnabled());
+
+ final TelephonySubscriptionSnapshot updatedSnapshot =
+ mock(TelephonySubscriptionSnapshot.class);
+ doReturn(TEST_SUB_IDS_IN_GROUP)
+ .when(updatedSnapshot)
+ .getAllSubIdsInGroup(eq(TEST_SUB_GROUP));
+ doReturn(false).when(mTelephonyManager).isDataEnabled();
+
+ mVcn.updateSubscriptionSnapshot(updatedSnapshot);
+ mTestLooper.dispatchAll();
+
+ assertFalse(mVcn.isMobileDataEnabled());
+ }
+
private void triggerVcnRequestListeners(NetworkRequestListener requestListener) {
for (final int[] caps : TEST_CAPS) {
startVcnGatewayWithCapabilities(requestListener, caps);