diff options
585 files changed, 11385 insertions, 3101 deletions
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java index 4ef91b5e3bc1..b5e366255180 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 @@ public final class AppSearchSession implements Closeable { @NonNull Consumer<AppSearchResult<AppSearchSession>> callback) { try { mService.initialize( + mPackageName, mUserHandle, /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(), new IAppSearchResultCallback.Stub() { @@ -136,8 +137,6 @@ public final class AppSearchSession implements Closeable { * @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 @@ public final class AppSearchSession implements Closeable { 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 @@ public final class AppSearchSession implements Closeable { 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 @@ public final class AppSearchSession implements Closeable { setSchemaNoMigrations( request, schemaBundles, - schemasPackageAccessibleBundles, + schemasVisibleToPackagesBundles, callbackExecutor, callback); } else { setSchemaWithMigrations( request, schemaBundles, - schemasPackageAccessibleBundles, + schemasVisibleToPackagesBundles, workExecutor, callbackExecutor, callback); @@ -687,7 +686,9 @@ public final class AppSearchSession implements Closeable { 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 @@ public final class AppSearchSession implements Closeable { 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 @@ public final class AppSearchSession implements Closeable { mDatabaseName, schemaBundles, new ArrayList<>(request.getSchemasNotDisplayedBySystem()), - schemasPackageAccessibleBundles, + schemasVisibleToPackagesBundles, request.isForceOverride(), request.getVersion(), mUserHandle, @@ -761,7 +762,7 @@ public final class AppSearchSession implements Closeable { 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 @@ public final class AppSearchSession implements Closeable { // 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 @@ public final class AppSearchSession implements Closeable { mDatabaseName, schemaBundles, new ArrayList<>(request.getSchemasNotDisplayedBySystem()), - schemasPackageAccessibleBundles, + schemasVisibleToPackagesBundles, /*forceOverride=*/ false, request.getVersion(), mUserHandle, @@ -853,7 +854,7 @@ public final class AppSearchSession implements Closeable { 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 247eb08d698a..130e442c0000 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 @@ public class GlobalSearchSession implements Closeable { @NonNull Consumer<AppSearchResult<GlobalSearchSession>> callback) { try { mService.initialize( + mPackageName, mUserHandle, /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(), new IAppSearchResultCallback.Stub() { @@ -187,7 +188,9 @@ public class GlobalSearchSession implements Closeable { 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 eb5d22eaaea0..6dfa01f6e680 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 @@ public class SearchResults implements Closeable { 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 class SearchResults implements Closeable { 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 0b26e146a7a4..a2f545f791fd 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 @@ interface IAppSearchManager { * @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 @@ interface IAppSearchManager { 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 @@ interface IAppSearchManager { * 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 @@ interface IAppSearchManager { /** * 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 272e12db0124..d493a1c28a3a 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 @@ public final class AppSearchBatchResult<KeyType, ValueType> { 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 c57cf2e68993..b1cb132ee088 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 @@ public final class AppSearchResult<ValueType> { 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 237e6242b22a..0ee5e65ef775 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.NonNull; 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 com.android.internal.util.Preconditions; 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 @@ public final class AppSearchSchema { } @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 @@ public final class AppSearchSchema { @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 @@ public final class AppSearchSchema { 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 @@ public final class AppSearchSchema { 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 bcd341ec18a8..c905f95fe4c4 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.NonNull; 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 @@ import java.util.Set; 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 @@ public class GenericDocument { * * @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 @@ public class GenericDocument { @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"); - - 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()) - .append(",\n"); + builder.append("{\n"); + builder.increaseIndentLevel(); - builder.append(indent1) - .append("timeToLiveMillis: ") - .append(document.getTtlMillis()) + 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("properties: {\n"); + builder.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 @@ public class GenericDocument { * @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 @@ public class GenericDocument { * @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 @@ public class GenericDocument { * @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 @@ public class GenericDocument { * @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 @@ public class GenericDocument { * @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 @@ public class GenericDocument { * @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 @@ public class GenericDocument { 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 @@ public class GenericDocument { } 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 @@ public class GenericDocument { * 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 @@ public class GenericDocument { } 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 @@ public class GenericDocument { 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 0af3e7a8987a..b72ca9af42cb 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 @@ public final class SetSchemaRequest { /** 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 @@ public final class SetSchemaRequest { * <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 @@ public final class SetSchemaRequest { 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 000000000000..b494c3ce9337 --- /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/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index 400c24138060..481d51eaf099 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -60,8 +60,8 @@ import com.android.internal.annotations.GuardedBy; import com.android.server.LocalManagerRegistry; import com.android.server.SystemService; import com.android.server.appsearch.external.localstorage.stats.CallStats; +import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore; import com.android.server.appsearch.util.PackageUtil; -import com.android.server.appsearch.visibilitystore.VisibilityStore; import com.android.server.usage.StorageStatsManagerLocal; import com.android.server.usage.StorageStatsManagerLocal.StorageStatsAugmenter; @@ -220,9 +220,10 @@ public class AppSearchManagerService extends SystemService { } // 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( - mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); + userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); //TODO(b/145759910) clear visibility setting for package. instance.getAppSearchImpl().clearPackageData(packageName); instance.getLogger().removeCachedUidForPackage(packageName); @@ -243,11 +244,11 @@ public class AppSearchManagerService extends SystemService { 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( - mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); - List<PackageInfo> installedPackageInfos = mContext - .createContextAsUser(userHandle, /*flags=*/0) + userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); + List<PackageInfo> installedPackageInfos = userContext .getPackageManager() .getInstalledPackages(/*flags=*/0); Set<String> packagesToKeep = new ArraySet<>(installedPackageInfos.size()); @@ -327,13 +328,15 @@ public class AppSearchManagerService extends SystemService { 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 = + Map<String, List<PackageIdentifier>> schemasVisibleToPackages = new ArrayMap<>(schemasVisibleToPackagesBundles.size()); for (Map.Entry<String, List<Bundle>> entry : schemasVisibleToPackagesBundles.entrySet()) { @@ -343,7 +346,7 @@ public class AppSearchManagerService extends SystemService { packageIdentifiers.add( new PackageIdentifier(entry.getValue().get(i))); } - schemasPackageAccessible.put(entry.getKey(), packageIdentifiers); + schemasVisibleToPackages.put(entry.getKey(), packageIdentifiers); } instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); SetSchemaResponse setSchemaResponse = instance.getAppSearchImpl().setSchema( @@ -352,7 +355,7 @@ public class AppSearchManagerService extends SystemService { schemas, instance.getVisibilityStore(), schemasNotDisplayedBySystem, - schemasPackageAccessible, + schemasVisibleToPackages, forceOverride, schemaVersion); ++operationSuccessCount; @@ -401,8 +404,10 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { try { + Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); verifyUserUnlocked(callingUser); - verifyCallingPackage(callingUser, callingUid, packageName); + verifyCallingPackage(userContext, callingUser, callingUid, packageName); + verifyNotInstantApp(userContext, packageName); AppSearchUserInstance instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); GetSchemaResponse response = @@ -431,8 +436,10 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { try { + Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); verifyUserUnlocked(callingUser); - verifyCallingPackage(callingUser, callingUid, packageName); + verifyCallingPackage(userContext, callingUser, callingUid, packageName); + verifyNotInstantApp(userContext, packageName); AppSearchUserInstance instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); List<String> namespaces = @@ -468,8 +475,10 @@ public class AppSearchManagerService extends SystemService { 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<>(); instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); @@ -548,8 +557,10 @@ public class AppSearchManagerService extends SystemService { 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<>(); instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); @@ -627,8 +638,10 @@ public class AppSearchManagerService extends SystemService { 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); instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); SearchResultPage searchResultPage = instance.getAppSearchImpl().query( packageName, @@ -691,8 +704,10 @@ public class AppSearchManagerService extends SystemService { 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); instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); boolean callerHasSystemAccess = @@ -738,9 +753,11 @@ public class AppSearchManagerService extends SystemService { @Override public void getNextPage( + @NonNull String packageName, long nextPageToken, @NonNull UserHandle userHandle, @NonNull IAppSearchResultCallback callback) { + Objects.requireNonNull(packageName); Objects.requireNonNull(userHandle); Objects.requireNonNull(callback); @@ -750,7 +767,10 @@ public class AppSearchManagerService extends SystemService { // opened it 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); SearchResultPage searchResultPage = @@ -765,14 +785,19 @@ public class AppSearchManagerService extends SystemService { } @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); + verifyCallingPackage(userContext, callingUser, callingUid, packageName); + verifyNotInstantApp(userContext, packageName); AppSearchUserInstance instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); instance.getAppSearchImpl().invalidateNextPageToken(nextPageToken); @@ -803,7 +828,10 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { try { - verifyCallingPackage(callingUser, callingUid, packageName); + 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. @@ -849,7 +877,10 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { try { - verifyCallingPackage(callingUser, callingUid, packageName); + Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); + verifyUserUnlocked(callingUser); + verifyCallingPackage(userContext, callingUser, callingUid, packageName); + verifyNotInstantApp(userContext, packageName); AppSearchUserInstance instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); @@ -908,8 +939,10 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { try { + Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); verifyUserUnlocked(callingUser); - verifyCallingPackage(callingUser, callingUid, packageName); + verifyCallingPackage(userContext, callingUser, callingUid, packageName); + verifyNotInstantApp(userContext, packageName); AppSearchUserInstance instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); @@ -957,8 +990,10 @@ public class AppSearchManagerService extends SystemService { 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<>(); instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); @@ -1039,8 +1074,10 @@ public class AppSearchManagerService extends SystemService { 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); instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); instance.getAppSearchImpl().removeByQuery( packageName, @@ -1095,8 +1132,10 @@ public class AppSearchManagerService extends SystemService { UserHandle callingUser = handleIncomingUser(userHandle, callingUid); EXECUTOR.execute(() -> { try { + Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); verifyUserUnlocked(callingUser); - verifyCallingPackage(callingUser, callingUid, packageName); + verifyCallingPackage(userContext, callingUser, callingUid, packageName); + verifyNotInstantApp(userContext, packageName); AppSearchUserInstance instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); StorageInfo storageInfo = instance.getAppSearchImpl() @@ -1112,8 +1151,10 @@ public class AppSearchManagerService extends SystemService { @Override public void persistToDisk( + @NonNull String packageName, @NonNull UserHandle userHandle, @ElapsedRealtimeLong long binderCallStartTimeMillis) { + Objects.requireNonNull(packageName); Objects.requireNonNull(userHandle); long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); @@ -1125,7 +1166,10 @@ public class AppSearchManagerService extends SystemService { int operationSuccessCount = 0; int operationFailureCount = 0; try { + Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); verifyUserUnlocked(callingUser); + verifyCallingPackage(userContext, callingUser, callingUid, packageName); + verifyNotInstantApp(userContext, packageName); instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); instance.getAppSearchImpl().persistToDisk(PersistType.Code.FULL); ++operationSuccessCount; @@ -1157,24 +1201,30 @@ public class AppSearchManagerService extends SystemService { @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; AppSearchUserInstance instance = null; int operationSuccessCount = 0; int operationFailureCount = 0; try { + Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); verifyUserUnlocked(callingUser); + verifyCallingPackage(userContext, callingUser, callingUid, packageName); + verifyNotInstantApp(userContext, packageName); instance = mAppSearchUserInstanceManager.getOrCreateUserInstance( - mContext, callingUser, AppSearchConfig.getInstance(EXECUTOR)); + userContext, callingUser, AppSearchConfig.getInstance(EXECUTOR)); ++operationSuccessCount; invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { @@ -1204,14 +1254,15 @@ public class AppSearchManagerService extends SystemService { } 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"); @@ -1317,6 +1368,21 @@ public class AppSearchManagerService extends SystemService { + 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 @@ -1331,9 +1397,10 @@ public class AppSearchManagerService extends SystemService { try { verifyUserUnlocked(userHandle); + Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0); AppSearchUserInstance instance = mAppSearchUserInstanceManager.getOrCreateUserInstance( - mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); + userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); stats.dataSize += instance.getAppSearchImpl() .getStorageInfoForPackage(packageName).getSizeBytes(); } catch (Throwable t) { @@ -1359,9 +1426,10 @@ public class AppSearchManagerService extends SystemService { if (packagesForUid == null) { return; } + Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0); AppSearchUserInstance instance = mAppSearchUserInstanceManager.getOrCreateUserInstance( - mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); + userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); for (int i = 0; i < packagesForUid.length; i++) { stats.dataSize += instance.getAppSearchImpl() .getStorageInfoForPackage(packagesForUid[i]).getSizeBytes(); @@ -1387,9 +1455,10 @@ public class AppSearchManagerService extends SystemService { if (packagesForUser == null) { return; } + Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0); AppSearchUserInstance instance = mAppSearchUserInstanceManager.getOrCreateUserInstance( - mContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); + userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); for (int i = 0; i < packagesForUser.size(); i++) { String packageName = packagesForUser.get(i).packageName; stats.dataSize += instance.getAppSearchImpl() diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java index 7e743edaf3ee..56e2af5fa2e3 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java @@ -19,7 +19,7 @@ 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.VisibilityStore; +import com.android.server.appsearch.visibilitystore.VisibilityStoreImpl; import java.util.Objects; @@ -30,12 +30,12 @@ import java.util.Objects; public final class AppSearchUserInstance { private final PlatformLogger mLogger; private final AppSearchImpl mAppSearchImpl; - private final VisibilityStore mVisibilityStore; + private final VisibilityStoreImpl mVisibilityStore; AppSearchUserInstance( @NonNull PlatformLogger logger, @NonNull AppSearchImpl appSearchImpl, - @NonNull VisibilityStore visibilityStore) { + @NonNull VisibilityStoreImpl visibilityStore) { mLogger = Objects.requireNonNull(logger); mAppSearchImpl = Objects.requireNonNull(appSearchImpl); mVisibilityStore = Objects.requireNonNull(visibilityStore); @@ -52,7 +52,7 @@ public final class AppSearchUserInstance { } @NonNull - public VisibilityStore getVisibilityStore() { + 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 index cedc364f6072..e067d4bcdf72 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java @@ -30,7 +30,7 @@ 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.VisibilityStore; +import com.android.server.appsearch.visibilitystore.VisibilityStoreImpl; import java.io.File; import java.util.Map; @@ -89,25 +89,24 @@ public final class AppSearchUserInstanceManager { * <p>If no AppSearchUserInstance exists for the unlocked user, Icing will be initialized and * one will be created. * - * @param context The context + * @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 context, + @NonNull Context userContext, @NonNull UserHandle userHandle, @NonNull AppSearchConfig config) throws AppSearchException { - Objects.requireNonNull(context); + Objects.requireNonNull(userContext); Objects.requireNonNull(userHandle); Objects.requireNonNull(config); synchronized (mInstancesLocked) { AppSearchUserInstance instance = mInstancesLocked.get(userHandle); if (instance == null) { - Context userContext = context.createContextAsUser(userHandle, /*flags=*/ 0); instance = createUserInstance(userContext, userHandle, config); mInstancesLocked.put(userHandle, instance); } @@ -169,7 +168,7 @@ public final class AppSearchUserInstanceManager { InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder(); // Initialize the classes that make up AppSearchUserInstance - PlatformLogger logger = new PlatformLogger(userContext, userHandle, config); + PlatformLogger logger = new PlatformLogger(userContext, config); File appSearchDir = getAppSearchDir(userHandle); File icingDir = new File(appSearchDir, "icing"); @@ -178,7 +177,8 @@ public final class AppSearchUserInstanceManager { AppSearchImpl.create(icingDir, initStatsBuilder, new FrameworkOptimizeStrategy()); long prepareVisibilityStoreLatencyStartMillis = SystemClock.elapsedRealtime(); - VisibilityStore visibilityStore = VisibilityStore.create(appSearchImpl, userContext); + VisibilityStoreImpl visibilityStore = + VisibilityStoreImpl.create(appSearchImpl, userContext); long prepareVisibilityStoreLatencyEndMillis = SystemClock.elapsedRealtime(); initStatsBuilder 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 77a1bb402bd3..9dee179bd6f2 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 @@ -58,7 +58,7 @@ 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.visibilitystore.VisibilityStore; +import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore; import com.google.android.icing.IcingSearchEngine; import com.google.android.icing.proto.DeleteByQueryResultProto; @@ -346,11 +346,11 @@ public final class AppSearchImpl implements Closeable { * @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 visibilityStore If set, {@code schemasNotPlatformSurfaceable} and {@code - * schemasPackageAccessible} will be saved here if the schema is successfully applied. - * @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. @@ -366,8 +366,8 @@ public final class AppSearchImpl implements Closeable { @NonNull String databaseName, @NonNull List<AppSearchSchema> schemas, @Nullable VisibilityStore visibilityStore, - @NonNull List<String> schemasNotPlatformSurfaceable, - @NonNull Map<String, List<PackageIdentifier>> schemasPackageAccessible, + @NonNull List<String> schemasNotDisplayedBySystem, + @NonNull Map<String, List<PackageIdentifier>> schemasVisibleToPackages, boolean forceOverride, int version) throws AppSearchException { @@ -430,25 +430,25 @@ public final class AppSearchImpl implements Closeable { } if (visibilityStore != null) { - Set<String> prefixedSchemasNotPlatformSurfaceable = - new ArraySet<>(schemasNotPlatformSurfaceable.size()); - for (int i = 0; i < schemasNotPlatformSurfaceable.size(); i++) { - prefixedSchemasNotPlatformSurfaceable.add( - prefix + schemasNotPlatformSurfaceable.get(i)); + 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()); + Map<String, List<PackageIdentifier>> prefixedSchemasVisibleToPackages = + new ArrayMap<>(schemasVisibleToPackages.size()); for (Map.Entry<String, List<PackageIdentifier>> entry : - schemasPackageAccessible.entrySet()) { - prefixedSchemasPackageAccessible.put(prefix + entry.getKey(), entry.getValue()); + schemasVisibleToPackages.entrySet()) { + prefixedSchemasVisibleToPackages.put(prefix + entry.getKey(), entry.getValue()); } visibilityStore.setVisibility( packageName, databaseName, - prefixedSchemasNotPlatformSurfaceable, - prefixedSchemasPackageAccessible); + prefixedSchemasNotDisplayedBySystem, + prefixedSchemasVisibleToPackages); } return SetSchemaResponseToProtoConverter.toSetSchemaResponse( @@ -737,6 +737,9 @@ public final class AppSearchImpl implements Closeable { 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); } @@ -768,7 +771,7 @@ public final class AppSearchImpl implements Closeable { * @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. @@ -1465,7 +1468,6 @@ public final class AppSearchImpl implements Closeable { mOptimizeIntervalCountLocked = 0; mSchemaMapLocked.clear(); mNamespaceMapLocked.clear(); - if (initStatsBuilder != null) { initStatsBuilder .setHasReset(true) 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 000000000000..6e1e2d5f774b --- /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 56a546a2e8e1..9d789a894855 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 @@ public final class SetSchemaStats { */ @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 @@ public final class SetSchemaStats { 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 @@ public final class SetSchemaStats { 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 @@ public final class SetSchemaStats { @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 @@ public final class SetSchemaStats { 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 000000000000..fb89250cb096 --- /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/PlatformLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java index 31fead5e6314..322bd119b604 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.app.appsearch.exceptions.AppSearchException; 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 @@ import java.util.Random; 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 @@ public final class PlatformLogger implements AppSearchLogger { * 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 @@ public final class PlatformLogger implements AppSearchLogger { 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 53a1bedb780b..714ffb6c8b68 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.annotation.NonNull; 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 @@ public class PackageUtil { 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 index 9e36fd02569f..95905af76123 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 @@ import java.util.Set; * * 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 @@ class NotPlatformSurfaceableMap { * * <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 @@ class NotPlatformSurfaceableMap { * 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) { 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 1771b1ddc6b6..b0108704be34 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 @@ class VisibilityDocument extends GenericDocument { /** * 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 @@ class VisibilityDocument extends GenericDocument { */ 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 @@ class VisibilityDocument extends GenericDocument { } @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 @@ class VisibilityDocument extends GenericDocument { /** 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 index ae1ec56b4ee6..ce142d646d1c 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.os.UserHandle; 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 @@ import java.util.Set; * <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}. - */ - 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,11 +78,10 @@ public class VisibilityStore { 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 and initializes VisibilityStore. @@ -105,41 +90,41 @@ public class VisibilityStore { * @param userContext Context of the user that the call is being made as */ @NonNull - public static VisibilityStore create( + public static VisibilityStoreImpl create( @NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext) throws AppSearchException { - return new VisibilityStore(appSearchImpl, userContext); + return new VisibilityStoreImpl(appSearchImpl, userContext); } - private VisibilityStore(@NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext) + 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), + Arrays.asList(VisibilityDocument.SCHEMA, VisibleToPackagesDocument.SCHEMA), /*visibilityStore=*/ null, // Avoid recursive calls - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ SCHEMA_VERSION); } @@ -177,26 +162,25 @@ public class VisibilityStore { } // 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) { @@ -206,61 +190,50 @@ public class VisibilityStore { 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( @@ -269,9 +242,9 @@ public class VisibilityStore { 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); } @@ -287,17 +260,7 @@ public class VisibilityStore { == PackageManager.PERMISSION_GRANTED; } - /** - * Checks whether {@code prefixedSchema} can be searched over by the {@code callerUid}. - * - * @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 callerUid UID of the client making the globalQuery call. - * @param callerHasSystemAccess Whether the caller has been identified as having - * access to schemas marked system surfaceable by {@link - * #doesCallerHaveSystemAccess}. - */ + @Override public boolean isSchemaSearchableByCaller( @NonNull String packageName, @NonNull String databaseName, @@ -313,13 +276,13 @@ public class VisibilityStore { } if (callerHasSystemAccess - && mNotPlatformSurfaceableMap.isSchemaPlatformSurfaceable( + && 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); } /** @@ -331,13 +294,13 @@ public class VisibilityStore { * 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; 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 index 0b4e196fd0c4..8d503390eaaa 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 @@ import android.app.appsearch.PackageIdentifier; * * @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 @@ class PackageAccessibleDocument extends GenericDocument { .build()) .build(); - public PackageAccessibleDocument(@NonNull GenericDocument genericDocument) { + VisibleToPackagesDocument(@NonNull GenericDocument genericDocument) { super(genericDocument); } @@ -76,9 +76,9 @@ class PackageAccessibleDocument extends GenericDocument { 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 @@ class PackageAccessibleDocument extends GenericDocument { @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 index cff729aa4e8a..e2b51a75025c 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 @@ import java.util.Set; * * 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 @@ class PackageAccessibleMap { * * <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) { diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt index 78d39cc6deac..65551074f9c0 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 d6ce3ebc267f..7b74578adfc7 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 @@ public interface AppSearchSessionShim extends Closeable { * @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/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index 0e9efbcc98b8..ac2018707c75 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 @@ public class DeviceIdleController extends SystemService private long mNextIdlePendingDelay; private long mNextIdleDelay; private long mNextLightIdleDelay; + private long mNextLightIdleDelayFlex; private long mNextLightAlarmTime; private long mNextSensingTimeoutAlarmTime; @@ -886,16 +887,20 @@ public class DeviceIdleController extends SystemService */ 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService "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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 class DeviceIdleController extends SystemService 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 class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService /** * 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 class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService private void resetLightIdleManagementLocked() { mNextLightIdleDelay = 0; + mNextLightIdleDelayFlex = 0; mCurLightIdleBudget = 0; cancelLightAlarmLocked(); } @@ -3265,13 +3362,15 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService } } 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 @@ public class DeviceIdleController extends SystemService } 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 @@ public class DeviceIdleController extends SystemService // 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 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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/api/Android.bp b/api/Android.bp index a84e6a6cb031..2ea180ebf598 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -24,6 +24,41 @@ package { 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 " @@ -431,3 +466,41 @@ genrule { }, ], } + +// 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/api_versions_trimmer.py b/api/api_versions_trimmer.py new file mode 100755 index 000000000000..9afd95a3003a --- /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 000000000000..4eb929ea1b5d --- /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/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 96d59b80b479..9bd6c750fb13 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -4804,6 +4804,16 @@ public class AppOpsManager { 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 @@ public class AppOpsManager { @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 @@ public class AppOpsManager { * @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 @@ public class AppOpsManager { @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 @@ public class AppOpsManager { 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 @@ public class AppOpsManager { 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 @@ public class AppOpsManager { } 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 @@ public class AppOpsManager { } 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 @@ public class AppOpsManager { 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/LoadedApk.java b/core/java/android/app/LoadedApk.java index 9ed76c1c13d1..a2c9795204ad 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -546,6 +546,10 @@ public final class LoadedApk { 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 ba0bc555eb57..506dfe09f3fa 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -5606,14 +5606,24 @@ public class Notification implements Parcelable 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. */ diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java index cd82deb13e9b..7ef0a19ec44c 100644 --- a/core/java/android/app/WallpaperColors.java +++ b/core/java/android/app/WallpaperColors.java @@ -111,12 +111,15 @@ public final class WallpaperColors implements Parcelable { 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 @@ public final class WallpaperColors implements Parcelable { 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 @@ public final class WallpaperColors implements Parcelable { 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/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl index eb4c624be0b2..585eb61b6f57 100644 --- a/core/java/android/app/usage/IUsageStatsManager.aidl +++ b/core/java/android/app/usage/IUsageStatsManager.aidl @@ -68,5 +68,5 @@ interface IUsageStatsManager { 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 e8175c709d85..ac7a31874682 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -1287,7 +1287,7 @@ public final class UsageStatsManager { 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/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index ded5e6e27a68..5b72b76c3578 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -3054,6 +3054,9 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { 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/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl index b0ce6a55e9ba..12911d6e1232 100644 --- a/core/java/android/content/pm/IPackageInstaller.aidl +++ b/core/java/android/content/pm/IPackageInstaller.aidl @@ -62,6 +62,8 @@ interface IPackageInstaller { 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 c2ac80e7c98f..d3ed00608324 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -586,11 +586,6 @@ interface IPackageManager { 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 33a34be1b968..2ed00b5d2982 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1278,6 +1278,13 @@ public abstract class PackageManager { */ 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/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java index 4c44ba1fc9ef..5a7f21040d0a 100644 --- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java +++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java @@ -1164,7 +1164,7 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { 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 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { 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 @@ public class ParsingPackageImpl implements ParsingPackage, Parcelable { 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 6fd533355aad..dce242c9d87c 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.net.Uri; 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 @@ public class ParsingPackageUtils { } /** + * 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 4aed77ae641b..9d830ec7a227 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 @@ public abstract class ParsedComponent implements Parcelable { 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/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index 37469e916eef..08f5a8a582db 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -848,8 +848,10 @@ public final class Sensor { /** * 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 @@ -1002,9 +1004,11 @@ public final class Sensor { } /** - * @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 713b66abed21..572a8a81883a 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -46,6 +46,13 @@ import java.util.List; * 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; diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java index 2d46a4073809..e665d0fcc836 100644 --- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java @@ -208,7 +208,8 @@ public interface BiometricFingerprintConstants { FINGERPRINT_ACQUIRED_TOO_FAST, FINGERPRINT_ACQUIRED_VENDOR, FINGERPRINT_ACQUIRED_START, - FINGERPRINT_ACQUIRED_UNKNOWN}) + FINGERPRINT_ACQUIRED_UNKNOWN, + FINGERPRINT_ACQUIRED_IMMOBILE}) @Retention(RetentionPolicy.SOURCE) @interface FingerprintAcquired {} @@ -278,6 +279,14 @@ public interface BiometricFingerprintConstants { 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/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java index e24332a1885a..5b259f71feda 100644 --- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java @@ -212,9 +212,9 @@ public final class CameraExtensionCharacteristics { */ 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 = diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index a39bc4e0bee5..1c0ae281d9fe 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -16,6 +16,7 @@ package android.hardware.display; +import android.annotation.IntDef; import android.annotation.Nullable; import android.graphics.Point; import android.hardware.SensorManager; @@ -29,6 +30,9 @@ 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; /** @@ -37,6 +41,16 @@ import java.util.Objects; * @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. */ @@ -296,9 +310,10 @@ public abstract class DisplayManagerInternal { 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 specify. + * 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. * @@ -313,6 +328,15 @@ public abstract class DisplayManagerInternal { 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 @@ -613,4 +637,25 @@ public abstract class DisplayManagerInternal { 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 688f9f1174dd..0819835f5fb5 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -1417,6 +1417,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing 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 d8150e483fd6..653c622386d6 100644 --- a/core/java/android/hardware/input/InputDeviceVibrator.java +++ b/core/java/android/hardware/input/InputDeviceVibrator.java @@ -56,10 +56,10 @@ final class InputDeviceVibrator extends Vibrator { 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 662ebb356f4c..5c2855307509 100644 --- a/core/java/android/net/VpnManager.java +++ b/core/java/android/net/VpnManager.java @@ -389,6 +389,10 @@ public class VpnManager { /** * 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 86cd23d1e942..752ef3e39ab6 100644 --- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -352,6 +352,7 @@ public final class VcnGatewayConnectionConfig { public int hashCode() { return Objects.hash( mGatewayConnectionName, + mTunnelConnectionParams, mExposedCapabilities, Arrays.hashCode(mRetryIntervalsMs), mMaxMtu); @@ -365,6 +366,7 @@ public final class VcnGatewayConnectionConfig { 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/SystemVibrator.java b/core/java/android/os/SystemVibrator.java index fd8948cbeea8..0c3debb1b54e 100644 --- a/core/java/android/os/SystemVibrator.java +++ b/core/java/android/os/SystemVibrator.java @@ -26,6 +26,7 @@ import android.util.Log; 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 @@ public class SystemVibrator extends Vibrator { * <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 @@ public class SystemVibrator extends Vibrator { @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 @@ public class SystemVibrator extends Vibrator { @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 @@ public class SystemVibrator extends Vibrator { 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/Vibrator.java b/core/java/android/os/Vibrator.java index 12538e6cd46b..d7893e4bbefd 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -126,6 +126,7 @@ public abstract class Vibrator { // 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 abstract class Vibrator { public Vibrator() { mPackageName = ActivityThread.currentPackageName(); final Context ctx = ActivityThread.currentActivityThread().getSystemContext(); - loadVibrationIntensities(ctx); + loadVibrationConfig(ctx); } /** @@ -142,22 +143,28 @@ public abstract class Vibrator { */ 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 @@ public abstract class Vibrator { } /** + * 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 597df0811e20..486f9f139e0a 100644 --- a/core/java/android/os/VibratorInfo.java +++ b/core/java/android/os/VibratorInfo.java @@ -51,8 +51,11 @@ public class VibratorInfo implements Parcelable { 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 @@ public class VibratorInfo implements Parcelable { 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 @@ public class VibratorInfo implements Parcelable { /** * 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 @@ public class VibratorInfo implements Parcelable { 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 @@ public class VibratorInfo implements Parcelable { 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 class VibratorInfo implements Parcelable { 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 @@ public class VibratorInfo implements Parcelable { + ", 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 class VibratorInfo implements Parcelable { */ 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 class VibratorInfo implements Parcelable { */ 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 @@ public class VibratorInfo implements Parcelable { } 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 @@ public class VibratorInfo implements Parcelable { /** @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 @@ public class VibratorInfo implements Parcelable { /** 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 @@ public class VibratorInfo implements Parcelable { /** 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 setPwlePrimitiveDurationMax(int pwlePrimitiveDurationMax) { + mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax; + return this; + } + + /** Configure maximum number of primitives supported in a single PWLE composed effect. */ @NonNull - public Builder setSupportedPrimitives(int... supportedPrimitives) { - mSupportedPrimitives = supportedPrimitives; + 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 @@ public class VibratorInfo implements Parcelable { @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 c5d0cd40bc6d..4ef0e6e785e8 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -908,9 +908,32 @@ public final class PermissionManager { */ 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 @@ public final class PermissionManager { 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 d4e548e1df1e..791764b4342f 100644 --- a/core/java/android/permission/PermissionUsageHelper.java +++ b/core/java/android/permission/PermissionUsageHelper.java @@ -410,7 +410,9 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis 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/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index 0b11aeb1ed65..3b4f7e241852 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -124,7 +124,6 @@ public final class KeymasterDefs { 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 1188a3f1021e..74a735518120 100644 --- a/core/java/android/service/contentcapture/ActivityEvent.java +++ b/core/java/android/service/contentcapture/ActivityEvent.java @@ -55,12 +55,25 @@ public final class ActivityEvent implements Parcelable { */ 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 @@ public final class ActivityEvent implements Parcelable { * 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 @@ public final class ActivityEvent implements Parcelable { 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/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 67b97cee1b51..41374167cc56 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -263,6 +263,7 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { 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 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { 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 @@ public class AlwaysOnHotwordDetector extends AbstractHotwordDetector { 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 846f2f94d055..315392bf6a58 100644 --- a/core/java/android/service/voice/HotwordDetectedResult.java +++ b/core/java/android/service/voice/HotwordDetectedResult.java @@ -20,11 +20,18 @@ import android.annotation.IntDef; 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 @@ public final class HotwordDetectedResult implements Parcelable { 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 @@ public final class HotwordDetectedResult implements Parcelable { 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 @@ public final class HotwordDetectedResult implements Parcelable { 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 @@ public final class HotwordDetectedResult implements Parcelable { //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 @@ public final class HotwordDetectedResult implements Parcelable { && 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 @@ public final class HotwordDetectedResult implements Parcelable { 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 @@ public final class HotwordDetectedResult implements Parcelable { _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 @@ public final class HotwordDetectedResult implements Parcelable { /** @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 @@ public final class HotwordDetectedResult implements Parcelable { 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 @@ public final class HotwordDetectedResult implements Parcelable { } @Override - public HotwordDetectedResult createFromParcel(@NonNull android.os.Parcel in) { + public HotwordDetectedResult createFromParcel(@NonNull Parcel in) { return new HotwordDetectedResult(in); } }; @@ -745,10 +784,10 @@ public final class HotwordDetectedResult implements Parcelable { } @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 b66d93d6316e..567ee2f65565 100644 --- a/core/java/android/service/voice/HotwordDetectionService.java +++ b/core/java/android/service/voice/HotwordDetectionService.java @@ -76,6 +76,7 @@ public abstract class HotwordDetectionService extends Service { 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 @@ public abstract class HotwordDetectionService extends Service { @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 abstract class HotwordDetectionService extends Service { */ 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 204e7df89706..fb540b1622e6 100644 --- a/core/java/android/service/voice/SoftwareHotwordDetector.java +++ b/core/java/android/service/voice/SoftwareHotwordDetector.java @@ -122,7 +122,7 @@ class SoftwareHotwordDetector extends AbstractHotwordDetector { this.mCallback = callback; } - /** TODO: onDetected */ + /** Called when the detected result is valid. */ @Override public void onDetected( @Nullable HotwordDetectedResult hotwordDetectedResult, @@ -150,33 +150,45 @@ class SoftwareHotwordDetector extends AbstractHotwordDetector { 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 @@ class SoftwareHotwordDetector extends AbstractHotwordDetector { 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/view/CrossWindowBlurListeners.java b/core/java/android/view/CrossWindowBlurListeners.java index e307b969ef91..761a2b88dec2 100644 --- a/core/java/android/view/CrossWindowBlurListeners.java +++ b/core/java/android/view/CrossWindowBlurListeners.java @@ -73,14 +73,14 @@ public final class CrossWindowBlurListeners { 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 @@ public final class CrossWindowBlurListeners { } - 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 8080883c2b3e..145607ada4f4 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -1265,7 +1265,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } stateChanged |= getSourceConsumer(types.valueAt(j)).notifyAnimationFinished(); } - if (invokeCallback && runningAnimation.startDispatched) { + if (invokeCallback) { dispatchAnimationEnd(runningAnimation.runner.getAnimation()); } break; diff --git a/core/java/android/view/ScrollCaptureTarget.java b/core/java/android/view/ScrollCaptureTarget.java index 44017ed0d831..a8bb037af5f9 100644 --- a/core/java/android/view/ScrollCaptureTarget.java +++ b/core/java/android/view/ScrollCaptureTarget.java @@ -21,13 +21,10 @@ import static java.util.Objects.requireNonNull; 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 @@ public final class ScrollCaptureTarget { 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 @@ public final class ScrollCaptureTarget { } } - 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 f34cd8f9de50..4e66ceb76a60 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -233,6 +233,7 @@ public final class SurfaceControl implements Parcelable { 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 @@ public final class SurfaceControl implements Parcelable { @GuardedBy("mLock") private int mHeight; + private int mTransformHint; + private WeakReference<View> mLocalOwnerView; static GlobalTransactionWrapper sGlobalTransaction; @@ -605,6 +608,7 @@ public final class SurfaceControl implements Parcelable { mName = other.mName; mWidth = other.mWidth; mHeight = other.mHeight; + mTransformHint = other.mTransformHint; mLocalOwnerView = other.mLocalOwnerView; assignNativeObject(nativeCopyFromSurfaceControl(other.mNativeObject), callsite); } @@ -1467,6 +1471,7 @@ public final class SurfaceControl implements Parcelable { mName = in.readString8(); mWidth = in.readInt(); mHeight = in.readInt(); + mTransformHint = in.readInt(); long object = 0; if (in.readInt() != 0) { @@ -1485,6 +1490,7 @@ public final class SurfaceControl implements Parcelable { dest.writeString8(mName); dest.writeInt(mWidth); dest.writeInt(mHeight); + dest.writeInt(mTransformHint); if (mNativeObject == 0) { dest.writeInt(0); } else { @@ -3602,4 +3608,27 @@ public final class SurfaceControl implements Parcelable { 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 6b0bb9df5468..4f2cf6d9001e 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -30,7 +30,6 @@ import android.graphics.BLASTBufferQueue; 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 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall @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 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } 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 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } } mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius); - if (sizeChanged && !creating) { + if ((sizeChanged || hintChanged) && !creating) { setBufferSize(mTmpTransaction); } @@ -1081,17 +1081,18 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall || 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 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mSurfaceHeight = myHeight; mFormat = mRequestedFormat; mLastWindowVisibility = mWindowVisibility; + mTransformHint = viewRoot.getSurfaceTransformHint(); mScreenRect.left = mWindowSpaceLeft; mScreenRect.top = mWindowSpaceTop; @@ -1130,9 +1132,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } 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 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall 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 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall 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 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall 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/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index cf8c746d6ecb..573ae998305e 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -10382,4 +10382,8 @@ public final class ViewRootImpl implements ViewParent, }); return true; } + + int getSurfaceTransformHint() { + return mSurfaceControl.getTransformHint(); + } } diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java index d8cd6056de90..27821fd6608d 100644 --- a/core/java/android/view/ViewRootInsetsControllerHost.java +++ b/core/java/android/view/ViewRootInsetsControllerHost.java @@ -110,6 +110,10 @@ public class ViewRootInsetsControllerHost implements InsetsController.Host { @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 ab5b5ba51a6e..6576eea40496 100644 --- a/core/java/android/view/WindowInsetsAnimation.java +++ b/core/java/android/view/WindowInsetsAnimation.java @@ -360,6 +360,13 @@ public final class WindowInsetsAnimation { * 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 10ae69118f54..ce6d034c585e 100644 --- a/core/java/android/view/contentcapture/ContentCaptureEvent.java +++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java @@ -286,6 +286,15 @@ public final class ContentCaptureEvent implements Parcelable { 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/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index bcb914208958..7ec9d34bd364 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -368,7 +368,10 @@ public final class MainContentCaptureSession extends ContentCaptureSession { 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) { diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index bdd12063e6cf..c4540b0d8726 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -163,6 +163,17 @@ public class BaseInputConnection implements InputConnection { } /** + * 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 @@ public class BaseInputConnection implements InputConnection { // Note: sendCurrentText does nothing unless mFallbackMode is set sendCurrentText(); endBatchEdit(); + endComposingRegionEditInternal(); } return true; } @@ -734,6 +746,7 @@ public class BaseInputConnection implements InputConnection { // Note: sendCurrentText does nothing unless mFallbackMode is set sendCurrentText(); endBatchEdit(); + endComposingRegionEditInternal(); } return true; } diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java index 5ac878d88100..592993cc3d3e 100644 --- a/core/java/android/view/translation/UiTranslationController.java +++ b/core/java/android/view/translation/UiTranslationController.java @@ -424,7 +424,7 @@ public class UiTranslationController { 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/widget/TextView.java b/core/java/android/widget/TextView.java index 3c4fd5e93580..37374ef37ada 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -129,7 +129,6 @@ import android.text.method.TextKeyListener; 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.TranslationSpec; 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } 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 a7d5ee465299..e1b04f8957e5 100644 --- a/core/java/android/widget/TextViewTranslationCallback.java +++ b/core/java/android/widget/TextViewTranslationCallback.java @@ -56,26 +56,6 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { 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 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { */ @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); - } - } - } else { - if (DEBUG) { - // TODO(b/182433547): remove before S release - Log.w(TAG, "onShowTranslation(): no translated text."); + 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); } - return false; } return true; } @@ -126,7 +105,7 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { @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 4a3bf91645f2..148986a558e7 100644 --- a/core/java/android/window/SplashScreenView.java +++ b/core/java/android/window/SplashScreenView.java @@ -292,6 +292,7 @@ public final class SplashScreenView extends FrameLayout { 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/app/IHotwordRecognitionStatusCallback.aidl b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl index ec99c95a737c..d0214e6e0082 100644 --- a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl +++ b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl @@ -78,4 +78,7 @@ oneway interface IHotwordRecognitionStatusCallback { * @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/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java index af21f8183d3a..9ced6097804d 100644 --- a/core/java/com/android/internal/infra/ServiceConnector.java +++ b/core/java/com/android/internal/infra/ServiceConnector.java @@ -228,7 +228,7 @@ public interface ServiceConnector<I extends IInterface> { 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/os/AppZygoteInit.java b/core/java/com/android/internal/os/AppZygoteInit.java index 0e83e41a7423..f925afc2a921 100644 --- a/core/java/com/android/internal/os/AppZygoteInit.java +++ b/core/java/com/android/internal/os/AppZygoteInit.java @@ -91,7 +91,9 @@ class AppZygoteInit { } 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 4f2f973b51b1..dfd561a8cc30 100644 --- a/core/java/com/android/internal/os/BatterySipper.java +++ b/core/java/com/android/internal/os/BatterySipper.java @@ -23,7 +23,10 @@ import java.util.List; /** * 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 b20f50d62de4..608782a39f6b 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -59,7 +59,10 @@ import java.util.List; * * 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 945a6ab11856..dab3e9fa15fa 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -12141,6 +12141,15 @@ public class BatteryStatsImpl extends BatteryStats { } } } + + void reset() { + idleTimeMs = 0; + rxTimeMs = 0; + txTimeMs = 0; + energy = 0; + uidRxBytes.clear(); + uidTxBytes.clear(); + } } private final BluetoothActivityInfoCache mLastBluetoothActivityInfo @@ -12167,6 +12176,15 @@ public class BatteryStatsImpl extends BatteryStats { 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/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 0c9dded42bda..e4e28a926ed6 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -500,6 +500,36 @@ public final class Zygote { } /** + * 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 1a23cc11fca8..134b1587ee58 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.TransitionInflater; 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 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { 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 000000000000..b4d2fb94d245 --- /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 000000000000..178b2c111fd7 --- /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 000000000000..d3a2b856100f --- /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 a60cc0fb101f..f073c1c046c5 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 android.os.Message; 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { ? 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { ? 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 @@ public interface PooledLambda { 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 1646a07b8001..19f0816e3e48 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.FunctionalUtils; 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 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, } } } 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 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, */ 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 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, + ", i = " + i + ", j = " + j + ", k = " + k + + ", l = " + l + ")"); } r.mFunc = Objects.requireNonNull(func); @@ -560,6 +587,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, 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/ScrollCaptureViewSupport.java b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java index 8aa2d57e8ea6..9e09006f608d 100644 --- a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java +++ b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java @@ -198,12 +198,9 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa 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 +310,9 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa } 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 4a70f7416075..299cbe12b4d1 100644 --- a/core/java/com/android/internal/widget/CachingIconView.java +++ b/core/java/com/android/internal/widget/CachingIconView.java @@ -257,7 +257,7 @@ public class CachingIconView extends ImageView { 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 @@ public class CachingIconView extends ImageView { // 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 3d054a5e773c..02ffe8c5268e 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -99,6 +99,12 @@ public class EditableInputConnection extends BaseInputConnection } @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/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index 73c5460755b1..3f756d757706 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -1524,7 +1524,9 @@ public class LockPatternView extends View { 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 @@ public class LockPatternView extends View { 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 7d82d355e3eb..8e834a87784d 100644 --- a/core/java/com/android/server/AppWidgetBackupBridge.java +++ b/core/java/com/android/server/AppWidgetBackupBridge.java @@ -47,9 +47,9 @@ public class AppWidgetBackupBridge { : 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 class AppWidgetBackupBridge { } } - 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 bd0de2984b7f..a8dcbaffeeb5 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -86,6 +86,7 @@ public class SystemConfig { // 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 @@ public class SystemConfig { 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 @@ public class SystemConfig { return mWhitelistedStagedInstallers; } + public Set<String> getAllowedVendorApexes() { + return mAllowedVendorApexes; + } + public ArraySet<String> getAppDataIsolationWhitelistedApps() { return mAppDataIsolationWhitelistedApps; } @@ -484,7 +490,7 @@ public class SystemConfig { // 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 @@ public class SystemConfig { } // 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 @@ public class SystemConfig { // 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 @@ public class SystemConfig { (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 @@ public class SystemConfig { } 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 a2efbdd6f2f8..5453c4de6986 100644 --- a/core/java/com/android/server/WidgetBackupProvider.java +++ b/core/java/com/android/server/WidgetBackupProvider.java @@ -28,7 +28,7 @@ import java.util.List; 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_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index d528428a0e83..e9e79dc30c20 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -1775,6 +1775,16 @@ static jint nativeGetGPUContextPriority(JNIEnv* env, jclass clazz) { 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 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeCreateJankDataListenerWrapper }, {"nativeGetGPUContextPriority", "()I", (void*)nativeGetGPUContextPriority }, + {"nativeSetTransformHint", "(JI)V", + (void*)nativeSetTransformHint }, // clang-format on }; diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 4a1a2728b987..502849e4824a 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 @@ void zygote::ZygoteFailure(JNIEnv* env, __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 @@ pid_t zygote::ForkCommon(JNIEnv* env, bool is_system_server, __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 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( 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 @@ int zygote::forkApp(JNIEnv* env, } 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 @@ static jint com_android_internal_os_Zygote_nativeCurrentTaggingLevel(JNIEnv* env #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 @@ static const JNINativeMethod gMethods[] = { (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 7fa627b3f809..6f5cc5314d0b 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -52,7 +52,6 @@ static const char* kPathAllowlist[] = { static const char kFdPath[] = "/proc/self/fd"; -// static FileDescriptorAllowlist* FileDescriptorAllowlist::Get() { if (instance_ == nullptr) { instance_ = new FileDescriptorAllowlist(); @@ -169,8 +168,8 @@ class FileDescriptorInfo { // 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 @@ class FileDescriptorInfo { 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 @@ class FileDescriptorInfo { 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 @@ void FileDescriptorInfo::DetachSocket(fail_fn_t fail_fn) const { } } -// 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 @@ void FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore, fail_fn_ 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 @@ void FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore, fail_fn_ 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; +} + +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); - RestatInternal(open_fds, 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 @@ FileDescriptorTable::FileDescriptorTable( : 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 @@ void FileDescriptorTable::RestatInternal(std::set<int>& open_fds, fail_fn_t fail } } -// 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 14c318e8e84a..a28ebf17d334 100644 --- a/core/jni/fd_utils.h +++ b/core/jni/fd_utils.h @@ -69,6 +69,9 @@ private: 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 @@ class FileDescriptorTable { 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 @@ class FileDescriptorTable { 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 d341c4b2f0a8..64c2a322548f 100644 --- a/core/proto/android/server/apphibernationservice.proto +++ b/core/proto/android/server/apphibernationservice.proto @@ -39,4 +39,5 @@ message GlobalLevelHibernationStatesProto { 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/layout/splash_screen_view.xml b/core/res/res/layout/splash_screen_view.xml index e6d724f1ecb2..0b7b49cdea83 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 803ba649bd29..a285cf7d6edf 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> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index e5ef55a2459b..71a07f357b59 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -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> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 86a09ee9643b..661001b5448b 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -597,6 +597,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> @@ -613,6 +615,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> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 5b2149e92b83..15190e779bd2 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -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> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index 54dc9322417e..25ae5768cd8a 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Barmaq izi doğrulandı"</string> @@ -601,6 +603,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> @@ -1624,7 +1627,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> @@ -1695,15 +1698,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> @@ -1720,7 +1723,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> @@ -2137,7 +2140,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 ff2289f192bf..ff17bab81413 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -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> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index b6ba47a47b1c..a3eebb6b3635 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -591,6 +591,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> @@ -607,6 +609,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> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 9ab5dfec650a..38fdc499a592 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -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> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 2a9ecf66d4e9..16ab2247be83 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -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,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -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 2d7003b6e4ab..474e861c7aac 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 lagano 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> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 330713d9a6b9..3afb3cb8a01b 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"L\'empremta digital s\'ha autenticat"</string> @@ -601,6 +603,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> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index c4eb66ea1a3b..07d1060594f2 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,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Otisk byl ověřen"</string> @@ -607,6 +609,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> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 270848758947..1099af6341ab 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeraftrykket blev godkendt"</string> @@ -601,6 +603,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> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 03b9904658d4..000d93ae4d30 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingerabdruck wurde authentifiziert"</string> @@ -601,6 +603,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index fce9d98005c8..0dec36bae4e2 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -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> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 805f344ed20b..dc7528bc3b85 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> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 86ddb1f7cb72..db3d9f5c1e3a 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> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index da1c49a93603..c2ca3140f5a0 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> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index eeedf2109517..994946fac6de 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> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index 5a2d3f340cd1..2fe89de65078 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> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index d3614f1b6df9..e20574c68e24 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -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> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 72571596b524..03d852991509 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Se ha autenticado la huella digital"</string> @@ -601,6 +603,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> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 1ae8730a90e4..efac8bcb9d56 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,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Sõrmejälg autenditi"</string> @@ -601,6 +603,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> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index de6e5e769e56..708d7d674d63 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Autentifikatu da hatz-marka"</string> @@ -601,6 +603,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> @@ -1053,8 +1056,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> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index cc2223cacbb6..0667fed4f013 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -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> @@ -906,12 +909,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> @@ -1669,16 +1672,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> @@ -1862,7 +1865,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 1b28ca1e5f05..2ae93b823536 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Sormenjälki tunnistettu"</string> @@ -601,6 +603,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> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index fa3737843761..143f3be59622 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Empreinte digitale authentifiée"</string> @@ -601,6 +603,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 0ee3c889fdb2..829c07cb97c7 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Empreinte digitale authentifiée"</string> @@ -601,6 +603,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> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 5e10fdb8eb45..22a25c9ccc92 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Autenticouse a impresión dixital"</string> @@ -601,6 +603,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> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 4157e1fd32eb..702e169f995d 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -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,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -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-hi/strings.xml b/core/res/res/values-hi/strings.xml index 8fe4ebfee33e..8c668dcf1de8 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -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> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 630ba2bece23..eb90708a9948 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> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index f945d395a6e5..77db9e600f7c 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Ujjlenyomat hitelesítve"</string> @@ -601,6 +603,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> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index d3057ca30809..0a18bfa80232 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/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> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index abe36081900e..58cfdd78da49 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Sidik jari diautentikasi"</string> @@ -601,6 +603,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> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 6be07b0056b0..210be916cd09 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingrafar staðfest"</string> @@ -601,6 +603,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> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index d4f2fdb08395..7d08000bc97e 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> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 7c9d97ecdd76..8b3aecea68c0 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -591,6 +591,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> @@ -607,6 +609,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> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 39681875a345..7b62919560ef 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -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> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index e941b5e48806..8d9b99c4b5de 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -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> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 1324d2d4ce60..80bddc7b6925 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -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> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index df0b08299bb1..aaa188b2fe62 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -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> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index a8e5b6403a4d..fc7fa3bb05b6 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -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> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index cf9ade76183c..14b8514826c9 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -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> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 5479f119c2ba..ccfdef8a56ed 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -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> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index ee2f058393df..e89dba7595e0 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> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 8ebb09ede62c..ee9e61d6fbb9 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -591,6 +591,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Piršto antspaudas autentifikuotas"</string> @@ -607,6 +609,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> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 9b86c4d7fc7f..1a90a81e2992 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -588,6 +588,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Pirksta nospiedums tika autentificēts."</string> @@ -604,6 +606,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> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index d0e11f1e8039..e0283074419f 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -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> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 22fac7ed594f..1b2e5950427a 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -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> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 1060f64fdb1c..d21ce23b4ac5 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -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> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index bf5f8e7a827e..f1b21c04246f 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/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,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index ccc0828daafb..bcc502a4dac0 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Cap jari disahkan"</string> @@ -601,6 +603,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> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 7797e0875c2e..97db71550f54 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -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> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 395dc1da2f03..bb29d8e84094 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeravtrykket er godkjent"</string> @@ -601,6 +603,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> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 130adcdcf6c9..c712fbd5007c 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,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,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 9449ac1cfb91..cc75bd61c458 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> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index b609827b9918..f2fbbd02116c 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -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,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 2efcc76bae52..180468025a72 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -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,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 49890fc64d27..06c7779e7746 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -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> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index f4c0cdbcd2aa..8729940ad51b 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -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> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index f1c847647ae6..543abc30fac3 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> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index f4c0cdbcd2aa..8729940ad51b 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -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> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index c50f2e3a99c0..8e005e60a55e 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,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Amprentă autentificată"</string> @@ -604,6 +606,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> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index dde5ebc88d59..2d177cc3ded2 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -591,6 +591,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> @@ -607,6 +609,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> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index 13df5e2db962..e6d795618995 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -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> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 4f8329a5e985..18dcef3dbb59 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -591,6 +591,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Odtlačok prsta bol overený"</string> @@ -607,6 +609,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> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index fb6fc9ae4b16..0f13454ebdad 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -591,6 +591,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Pristnost prstnega odtisa je preverjena"</string> @@ -607,6 +609,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> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 646cc854a631..2d45dd32b386 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Gjurma e gishtit u vërtetua"</string> @@ -601,6 +603,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> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 98ddb39e1680..6b96632479f6 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -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> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index f155f3cd0c36..58fcd9da730e 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeravtrycket har autentiserats"</string> @@ -601,6 +603,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> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 8ca0ac0f1aa1..8b34ba2a3500 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> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 4a50960dd1e9..adb8c4764312 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -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> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index d910224af035..dac4c515d8b7 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -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,8 @@ <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> + <!-- no translation found for fingerprint_error_bad_calibration (374406495079531135) --> + <skip /> <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> @@ -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 3ecb1dddd916..55e5685b95e5 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 3f9ae3af84ea..b9ab264eec7d 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -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> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index edc7427cfdc5..7678869e2a15 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Na-authenticate ang fingerprint"</string> @@ -601,6 +603,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> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index e5f9fe99de82..2f9a8b616880 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> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 964c07719f4e..c04e3f0e947e 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> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 7f5046b77277..07d683bd4d80 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -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> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index d8769b24eca4..a64c1a860b0e 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Barmoq izi tekshirildi"</string> @@ -601,6 +603,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> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index b276ff15552b..669adefd42e0 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Đã xác thực vân tay"</string> @@ -601,6 +603,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> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index b745661b42c7..ec8a7751e231 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -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> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index fd0bd9f1be77..c91a42677e64 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -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> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 310663f005d6..89b48d68b0c7 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -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> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index ae01829085f3..671f5f7020ca 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -585,6 +585,8 @@ <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> + <!-- no translation found for fingerprint_acquired_immobile (1621891895241888048) --> + <skip /> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_authenticated" msgid="2024862866860283100">"Izigxivizo zeminwe zigunyaziwe"</string> @@ -601,6 +603,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> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 3c47366a59db..7c7a89385c5e 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. @@ -3350,6 +3354,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> @@ -5003,6 +5011,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 d4ddab1ec502..302bd94c1973 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> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 960bd639960e..94c65a04e244 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1918,6 +1918,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" /> @@ -2013,6 +2014,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" /> @@ -2538,6 +2540,7 @@ <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" /> <!-- Fingerprint config --> <java-symbol type="integer" name="config_fingerprintMaxTemplatesPerUser"/> @@ -4402,5 +4405,7 @@ <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" /> </resources> 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 de0670b08ffd..228a061ae3c8 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 @@ package android.app.appsearch; 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 @@ public class AppSearchResultTest { @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 6fad4b8dcdac..000000000000 --- 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 000000000000..057ecbd9dfe9 --- /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/os/VibratorInfoTest.java b/core/tests/coretests/src/android/os/VibratorInfoTest.java index 8c7d10c7a5ef..6e07fa264c1c 100644 --- a/core/tests/coretests/src/android/os/VibratorInfoTest.java +++ b/core/tests/coretests/src/android/os/VibratorInfoTest.java @@ -87,14 +87,14 @@ public class VibratorInfoTest { 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 class VibratorInfoTest { 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 @@ public class VibratorInfoTest { } @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 @@ public class VibratorInfoTest { 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 @@ public class VibratorInfoTest { assertNotEquals(complete, completeWithComposeControl); VibratorInfo completeWithNoEffects = completeBuilder - .setSupportedEffects() - .setSupportedPrimitives() + .setSupportedEffects(new int[0]) .build(); assertNotEquals(complete, completeWithNoEffects); @@ -289,13 +311,8 @@ public class VibratorInfoTest { .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 @@ public class VibratorInfoTest { .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(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(empty, emptyWithKnownSupport); + assertNotEquals(unknownBrakingSupport, knownEmptyBrakingSupport); } @Test @@ -334,8 +356,7 @@ public class VibratorInfoTest { 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 8f9168b58184..0ece793c8125 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.ArgumentMatchers.isNull; 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 @@ public class VibratorTest { } @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 @@ public class VibratorTest { } @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 @@ public class VibratorTest { } @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/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index 89d2b743a619..72a145fa6a05 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -151,7 +151,7 @@ public class AndroidKeyStoreProvider extends Provider { * 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/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 70f03d234a56..f28ee820eb35 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/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index 7e673c6e32c2..b5c54023c492 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 @@ -482,7 +482,9 @@ public class OneHandedController implements RemoteCallable<OneHandedController> @VisibleForTesting void notifyExpandNotification() { - mMainExecutor.execute(() -> mEventCallback.notifyExpandNotification()); + if (mEventCallback != null) { + mMainExecutor.execute(() -> mEventCallback.notifyExpandNotification()); + } } @VisibleForTesting 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 1a365fee3b13..9986154b051d 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 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener { } void onAnimationProgress(float linearProgress) { - if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()) { + if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid() + || !mSplashScreenView.isAttachedToWindow()) { return; } @@ -267,15 +268,20 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener { return; } final SurfaceControl.Transaction tx = mTransactionPool.acquire(); - tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId()); - - SyncRtSurfaceTransactionApplier.SurfaceParams - params = new SyncRtSurfaceTransactionApplier.SurfaceParams - .Builder(mFirstWindowSurface) - .withWindowCrop(null) - .withMergeTransaction(tx) - .build(); - mApplier.scheduleApply(params); + if (mSplashScreenView.isAttachedToWindow()) { + tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId()); + + 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 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener { 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 46db35a6e29f..670af963230a 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 @@ public class StartingSurfaceDrawer { // 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/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 47789b7490ee..950900337918 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 @@ -435,4 +435,17 @@ public class OneHandedControllerTest extends OneHandedTestCase { 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()); + } } diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index c770150650e2..45a4f6c9c70d 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -331,6 +331,8 @@ public: 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; diff --git a/libs/hwui/effects/StretchEffect.cpp b/libs/hwui/effects/StretchEffect.cpp index 43f805d906a5..17cd3ceb577c 100644 --- a/libs/hwui/effects/StretchEffect.cpp +++ b/libs/hwui/effects/StretchEffect.cpp @@ -186,6 +186,7 @@ static const SkString stretchShader = SkString(R"( 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 @@ sk_sp<SkShader> StretchEffect::getShader(float width, float height, 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 @@ sk_sp<SkShader> StretchEffect::getShader(float width, float height, 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 25777c278a11..3eab9f05ebe5 100644 --- a/libs/hwui/effects/StretchEffect.h +++ b/libs/hwui/effects/StretchEffect.h @@ -113,6 +113,10 @@ public: return !isEmpty(); } + void clear() { + mBuilder = nullptr; + } + private: static sk_sp<SkRuntimeEffect> getStretchEffect(); mutable SkVector mStretchDirection{0, 0}; diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java index 4cd3616566c2..ecdd4b616e0f 100644 --- a/location/java/android/location/GnssMeasurement.java +++ b/location/java/android/location/GnssMeasurement.java @@ -214,6 +214,25 @@ public final class GnssMeasurement implements Parcelable { * * <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); @@ -1039,9 +1058,6 @@ public final class GnssMeasurement implements Parcelable { * 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 c9e4e0a9cb92..5d5c0fc6265d 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -122,6 +122,9 @@ interface ILocationManager 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 9ea8048ad476..0970c1c76a36 100644 --- a/location/java/android/location/LastLocationRequest.java +++ b/location/java/android/location/LastLocationRequest.java @@ -34,12 +34,15 @@ import java.util.Objects; 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 @@ public final class LastLocationRequest implements Parcelable { } /** + * 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 @@ public final class LastLocationRequest implements Parcelable { 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 @@ public final class LastLocationRequest implements Parcelable { @Override public void writeToParcel(@NonNull Parcel parcel, int flags) { parcel.writeBoolean(mHiddenFromAppOps); + parcel.writeBoolean(mAdasGnssBypass); parcel.writeBoolean(mLocationSettingsIgnored); } @@ -99,12 +128,13 @@ public final class LastLocationRequest implements Parcelable { } 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 @@ public final class LastLocationRequest implements Parcelable { 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 final class LastLocationRequest implements Parcelable { public static final class Builder { private boolean mHiddenFromAppOps; + private boolean mAdasGnssBypass; private boolean mLocationSettingsIgnored; /** @@ -138,6 +172,7 @@ public final class LastLocationRequest implements Parcelable { */ public Builder() { mHiddenFromAppOps = false; + mAdasGnssBypass = false; mLocationSettingsIgnored = false; } @@ -146,6 +181,7 @@ public final class LastLocationRequest implements Parcelable { */ public Builder(@NonNull LastLocationRequest lastLocationRequest) { mHiddenFromAppOps = lastLocationRequest.mHiddenFromAppOps; + mAdasGnssBypass = lastLocationRequest.mAdasGnssBypass; mLocationSettingsIgnored = lastLocationRequest.mLocationSettingsIgnored; } @@ -164,6 +200,25 @@ public final class LastLocationRequest implements Parcelable { } /** + * 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 final class LastLocationRequest implements Parcelable { 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 ae44c5e34521..526b84e85e38 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -315,6 +315,33 @@ public class LocationManager { 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 @@ public class LocationManager { } /** + * 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/LocationRequest.java b/location/java/android/location/LocationRequest.java index a3842a1ffd0a..b48e59676ac1 100644 --- a/location/java/android/location/LocationRequest.java +++ b/location/java/android/location/LocationRequest.java @@ -194,6 +194,7 @@ public final class LocationRequest implements Parcelable { 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 @@ public final class LocationRequest implements Parcelable { 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 @@ public final class LocationRequest implements Parcelable { float minUpdateDistanceMeters, long maxUpdateDelayMillis, boolean hiddenFromAppOps, + boolean adasGnssBypass, boolean locationSettingsIgnored, boolean lowPower, WorkSource workSource) { @@ -302,8 +304,9 @@ public final class LocationRequest implements Parcelable { mMinUpdateDistanceMeters = minUpdateDistanceMeters; mMaxUpdateDelayMillis = maxUpdateDelayMillis; mHideFromAppOps = hiddenFromAppOps; - mLowPower = lowPower; + mAdasGnssBypass = adasGnssBypass; mLocationSettingsIgnored = locationSettingsIgnored; + mLowPower = lowPower; mWorkSource = Objects.requireNonNull(workSource); } @@ -339,15 +342,15 @@ public final class LocationRequest implements Parcelable { 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 @@ public final class LocationRequest implements Parcelable { } /** + * 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 @@ public final class LocationRequest implements Parcelable { } /** + * 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 @@ public final class LocationRequest implements Parcelable { /* 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 @@ public final class LocationRequest implements Parcelable { 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 @@ public final class LocationRequest implements Parcelable { && 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 @@ public final class LocationRequest implements Parcelable { 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 @@ public final class LocationRequest implements Parcelable { 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 @@ public final class LocationRequest implements Parcelable { mMinUpdateDistanceMeters = 0; mMaxUpdateDelayMillis = 0; mHiddenFromAppOps = false; + mAdasGnssBypass = false; mLocationSettingsIgnored = false; mLowPower = false; mWorkSource = null; @@ -925,6 +960,7 @@ public final class LocationRequest implements Parcelable { 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 final class LocationRequest implements Parcelable { 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 @@ public final class LocationRequest implements Parcelable { } /** + * 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 @@ public final class LocationRequest implements Parcelable { 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 b72d36519e72..4f33a529a812 100644 --- a/location/java/android/location/provider/ProviderRequest.java +++ b/location/java/android/location/provider/ProviderRequest.java @@ -44,12 +44,19 @@ public final class ProviderRequest implements Parcelable { 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 @@ public final class ProviderRequest implements Parcelable { @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 @@ public final class ProviderRequest implements Parcelable { } /** + * 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 @@ public final class ProviderRequest implements Parcelable { } /** + * 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 @@ public final class ProviderRequest implements Parcelable { /* quality= */ in.readInt(), /* maxUpdateDelayMillis= */ in.readLong(), /* lowPower= */ in.readBoolean(), + /* adasGnssBypass= */ in.readBoolean(), /* locationSettingsIgnored= */ in.readBoolean(), /* workSource= */ in.readTypedObject(WorkSource.CREATOR)); } @@ -176,6 +207,7 @@ public final class ProviderRequest implements Parcelable { parcel.writeInt(mQuality); parcel.writeLong(mMaxUpdateDelayMillis); parcel.writeBoolean(mLowPower); + parcel.writeBoolean(mAdasGnssBypass); parcel.writeBoolean(mLocationSettingsIgnored); parcel.writeTypedObject(mWorkSource, flags); } @@ -198,6 +230,7 @@ public final class ProviderRequest implements Parcelable { && mQuality == that.mQuality && mMaxUpdateDelayMillis == that.mMaxUpdateDelayMillis && mLowPower == that.mLowPower + && mAdasGnssBypass == that.mAdasGnssBypass && mLocationSettingsIgnored == that.mLocationSettingsIgnored && mWorkSource.equals(that.mWorkSource); } @@ -229,8 +262,11 @@ public final class ProviderRequest implements Parcelable { 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 @@ public final class ProviderRequest implements Parcelable { * 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 @@ public final class ProviderRequest implements Parcelable { } /** + * 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 @@ public final class ProviderRequest implements Parcelable { mQuality, mMaxUpdateDelayMillis, mLowPower, + mAdasGnssBypass, mLocationSettingsIgnored, mWorkSource); } diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index 860d88afe4a2..d8f48c2cf0c6 100755 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java @@ -101,6 +101,8 @@ public class MtpDatabase implements AutoCloseable { 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 @@ public class MtpDatabase implements AutoCloseable { 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 @@ public class MtpDatabase implements AutoCloseable { } context.deleteDatabase(devicePropertiesName); } + mHostType = ""; + mSkipThumbForHost = false; } @VisibleForNative @@ -672,12 +677,24 @@ public class MtpDatabase implements AutoCloseable { @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 @@ public class MtpDatabase implements AutoCloseable { 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 @@ public class MtpDatabase implements AutoCloseable { 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 @@ public class MtpDatabase implements AutoCloseable { 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 e8b04edb2e1b..ec925918d4e6 100644 --- a/media/java/android/mtp/MtpDevice.java +++ b/media/java/android/mtp/MtpDevice.java @@ -170,6 +170,18 @@ public final class MtpDevice { } /** + * 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 @@ public final class MtpDevice { 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 0304ee386ace..88514515eabf 100644 --- a/media/java/android/mtp/MtpDeviceInfo.java +++ b/media/java/android/mtp/MtpDeviceInfo.java @@ -31,6 +31,7 @@ public class MtpDeviceInfo { private String mSerialNumber; private int[] mOperationsSupported; private int[] mEventsSupported; + private int[] mDevicePropertySupported; // only instantiated via JNI private MtpDeviceInfo() { @@ -144,6 +145,21 @@ public class MtpDeviceInfo { } /** + * 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 @@ public class MtpDeviceInfo { } /** + * 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 ffed4747d3ea..a77bc9fe0570 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -1131,6 +1131,7 @@ static const PropertyTableEntry kDevicePropertyTable[] = { { 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 @@ MtpProperty* MtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) { 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 3d2b00fec26c..ac89fecd9150 100644 --- a/media/jni/android_mtp_MtpDevice.cpp +++ b/media/jni/android_mtp_MtpDevice.cpp @@ -72,6 +72,7 @@ static jfieldID field_deviceInfo_version; 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 @@ static void initializeJavaIDs(JNIEnv* env) { 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 @@ android_mtp_MtpDevice_get_device_info(JNIEnv *env, jobject thiz) } } + 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 @@ static const JNINativeMethod gMethods[] = { {"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/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java index 478297f2a3c9..843793ad98f8 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.Calendar; 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 @@ public final class ObexHelper { } 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 a5a75f55f553..4cef0b33df4f 100644 --- a/obex/javax/obex/ObexTransport.java +++ b/obex/javax/obex/ObexTransport.java @@ -81,6 +81,8 @@ public interface ObexTransport { * 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/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-bn/strings.xml new file mode 100644 index 000000000000..e0dfcf24700c --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-bn/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">"বাতিল করুন"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-de/strings.xml new file mode 100644 index 000000000000..006301b2e94e --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-de/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">"Schließen"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-gu/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-gu/strings.xml new file mode 100644 index 000000000000..1fe4c5c0c946 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-gu/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">"છોડી દો"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-mr/strings.xml new file mode 100644 index 000000000000..4bd44859ec8f --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-mr/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">"डिसमिस करा"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-ne/strings.xml new file mode 100644 index 000000000000..15102541bb87 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-ne/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">"हटाउनुहोस्"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-or/strings.xml new file mode 100644 index 000000000000..36e7d3bdb216 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-or/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">"ଖାରଜ କରନ୍ତୁ"</string> +</resources> diff --git a/packages/SettingsLib/BannerMessagePreference/res/values-te/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-te/strings.xml new file mode 100644 index 000000000000..22a6f59f091c --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-te/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">"విస్మరించు"</string> +</resources> 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 7d9b4d78ce2a..6acd9ff07965 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 @@ -33,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" @@ -59,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 2a72a1ad65db..63d397c69353 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 0e7e595a3f04..000000000000 --- 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 4ae120ae286d..dbcecf1fe5b9 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java @@ -35,10 +35,18 @@ import com.google.android.material.resources.TextAppearanceConfig; * 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 SettingsTransitionActivity 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) { @@ -48,6 +56,12 @@ public class CollapsingToolbarBaseActivity extends SettingsTransitionActivity { 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); @@ -107,14 +121,43 @@ public class CollapsingToolbarBaseActivity extends SettingsTransitionActivity { 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(); @@ -127,4 +170,39 @@ public class CollapsingToolbarBaseActivity extends SettingsTransitionActivity { }); 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 c4c74ffc719b..faa73ff52d56 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,31 @@ import android.widget.Toolbar; 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 CollapsingToolbarLayout mCollapsingToolbarLayout; + @Nullable + private AppBarLayout mAppBarLayout; @NonNull private Toolbar mToolbar; @NonNull private FrameLayout mContentFrameLayout; + private boolean mIsToolbarCollapsed; @Nullable @Override @@ -48,6 +58,13 @@ public abstract class CollapsingToolbarBaseFragment extends Fragment { final View view = inflater.inflate(R.layout.collapsing_toolbar_base_layout, container, false); mCollapsingToolbarLayout = view.findViewById(R.id.collapsing_toolbar); + mAppBarLayout = view.findViewById(R.id.app_bar); + 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 +77,31 @@ public abstract class CollapsingToolbarBaseFragment extends Fragment { 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 app bar. + */ + @Nullable + public AppBarLayout getAppBarLayout() { + return mAppBarLayout; + } + /** * Return the collapsing toolbar layout. */ @@ -75,4 +117,56 @@ public abstract class CollapsingToolbarBaseFragment extends Fragment { 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/FooterPreference/res/values-bn/strings.xml b/packages/SettingsLib/FooterPreference/res/values-bn/strings.xml new file mode 100644 index 000000000000..c58142d72618 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-bn/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">"আরও জানুন"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-de/strings.xml b/packages/SettingsLib/FooterPreference/res/values-de/strings.xml new file mode 100644 index 000000000000..fe885aa6f091 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-de/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">"Weitere Informationen"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-mr/strings.xml b/packages/SettingsLib/FooterPreference/res/values-mr/strings.xml new file mode 100644 index 000000000000..45387200bdc8 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-mr/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">"अधिक जाणून घ्या"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-ne/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ne/strings.xml new file mode 100644 index 000000000000..ecfec36337ee --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-ne/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">"थप जान्नुहोस्"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-or/strings.xml b/packages/SettingsLib/FooterPreference/res/values-or/strings.xml new file mode 100644 index 000000000000..e7924d646861 --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-or/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">"ଅଧିକ ଜାଣନ୍ତୁ"</string> +</resources> 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 304c3439a60d..d993e4465343 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.view.View; 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 @@ import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter; * 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 class SettingsSpinnerPreference extends Preference { 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 class SettingsSpinnerPreference extends Preference { public SettingsSpinnerPreference(Context context, AttributeSet attrs) { super(context, attrs); setLayoutResource(R.layout.settings_spinner_preference); - setSelectable(false); + setOnPreferenceClickListener(this); } /** @@ -76,6 +77,13 @@ public class SettingsSpinnerPreference extends Preference { 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 @@ public class SettingsSpinnerPreference extends Preference { 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/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml index 9610c9443184..46f1e030af23 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/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 89bb9e8bfe01..fa3f34c96ad2 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Stel gassessie terug"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gas"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Neem \'n foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Kies \'n prent"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index bc1bd1633e4c..7b4c832fd1d0 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"ቅጽል ስም"</string> <string name="guest_new_guest" msgid="3482026122932643557">"እንግዳን አክል"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"እንግዳን አስወግድ"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"እንግዳን ዳግም አስጀምር"</string> <string name="guest_nickname" msgid="6332276931583337261">"እንግዳ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ፎቶ አንሳ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ምስል ይምረጡ"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index f04aaac87b6d..611f2c44f565 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"উপনাম"</string> <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ কৰক"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি আঁতৰাওক"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"অতিথিৰ ছেশ্বন ৰিছেট কৰক"</string> <string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string> <string name="user_image_take_photo" msgid="467512954561638530">"এখন ফট’ তোলক"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"এখন প্ৰতিচ্ছবি বাছনি কৰক"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index b7cf11ae3754..c50a22d18605 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Qonaq sessiyasını sıfırlayın"</string> <string name="guest_nickname" msgid="6332276931583337261">"Qonaq"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Foto çəkin"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Şəkil seçin"</string> @@ -576,7 +575,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 97c281ad2d67..e26d0650c8cc 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -567,8 +567,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Resetuj sesiju gosta"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Slikaj"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 09e584318173..7899fa687ddc 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Добавяне на гост"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Премахване на госта"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Нулиране на сесията като гост"</string> <string name="guest_nickname" msgid="6332276931583337261">"Гост"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Правене на снимка"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Избиране на изображение"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 88abe33d1f18..9eb20f90735f 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -567,8 +567,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Poništi sesiju gosta"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Snimite fotografiju"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberite sliku"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index f499ab6cc7c0..d3590bf9e8d2 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Restableix el convidat"</string> <string name="guest_nickname" msgid="6332276931583337261">"Convidat"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fes una foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Tria una imatge"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 4d07eff0e858..5c664b987101 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -568,8 +568,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Resetovat hosta"</string> <string name="guest_nickname" msgid="6332276931583337261">"Host"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Pořídit fotku"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrat obrázek"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 98f53011d2ca..59bfab8ddea8 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Ψευδώνυμο"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Προσθήκη επισκέπτη"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Κατάργηση επισκέπτη"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Επαναφορά περιόδου επισκέπτη"</string> <string name="guest_nickname" msgid="6332276931583337261">"Επισκέπτης"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Λήψη φωτογραφίας"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Επιλογή εικόνας"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 6ff48541e4e8..26c7b2c7a760 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Restablecer invitado"</string> <string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Hacer foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Seleccionar una imagen"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index ef7b7db2bb9b..194aeaff4b15 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Lähtesta külastajaseanss"</string> <string name="guest_nickname" msgid="6332276931583337261">"Külaline"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Pildistage"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Valige pilt"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 93506105d964..33e2314dd887 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,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Berrezarri gonbidatuentzako saioa"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gonbidatua"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Atera argazki bat"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Aukeratu irudi bat"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 964162b34357..a11a00a5f65d 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Nollaa vieras"</string> <string name="guest_nickname" msgid="6332276931583337261">"Vieras"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ota kuva"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Valitse kuva"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 73f98ec23ccf..dba912ea3920 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Restablecer sesión de convidado"</string> <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escoller imaxe"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 5620d946b999..bc74092efa8d 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"प्रचलित नाम"</string> <string name="guest_new_guest" msgid="3482026122932643557">"मेहमान जोड़ें"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"मेहमान हटाएं"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"मेहमान के तौर पर ब्राउज़ करने का सेशन रीसेट करें"</string> <string name="guest_nickname" msgid="6332276931583337261">"मेहमान"</string> <string name="user_image_take_photo" msgid="467512954561638530">"फ़ोटो खींचें"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"कोई इमेज चुनें"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 0ff35f890407..d18d34cb0037 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -567,8 +567,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Poništi gostujuću sesiju"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiraj"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 9bd4d1263abd..3ad3590ca728 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Vendég munkamenet visszaállítása"</string> <string name="guest_nickname" msgid="6332276931583337261">"Vendég"</string> <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> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 662a67361b33..da92e7d4517f 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Endurstilla gestastillingu"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gestur"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Taka mynd"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Velja mynd"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 2c2d11fecda4..6f7565e3fb02 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Reimposta sessione Ospite"</string> <string name="guest_nickname" msgid="6332276931583337261">"Ospite"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Scatta una foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Scegli un\'immagine"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index ddae3c72babb..70dd9e74929b 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Лақап ат"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Қонақ қосу"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты жою"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Қонақ сеансын әдепкі күйге қайтару"</string> <string name="guest_nickname" msgid="6332276931583337261">"Қонақ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Фотосуретке түсіру"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Сурет таңдау"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index d970e9590c36..26595f5cc067 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"ឈ្មោះហៅក្រៅ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"បញ្ចូលភ្ញៀវ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ដកភ្ញៀវចេញ"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"កំណត់ភ្ញៀវឡើងវិញ"</string> <string name="guest_nickname" msgid="6332276931583337261">"ភ្ញៀវ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ថតរូប"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ជ្រើសរើសរូបភាព"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 8bad32006594..b36ef8d2a2da 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"ಅಡ್ಡ ಹೆಸರು"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ಅತಿಥಿಯನ್ನು ಸೇರಿಸಿ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕಿ"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"ಅತಿಥಿಯನ್ನು ಮರುಹೊಂದಿಸಿ"</string> <string name="guest_nickname" msgid="6332276931583337261">"ಅತಿಥಿ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ಫೋಟೋ ತೆಗೆದುಕೊಳ್ಳಿ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ಚಿತ್ರವನ್ನು ಆರಿಸಿ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 3383602753f1..ad0ad1fb28bf 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -568,8 +568,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Iš naujo nustatyti svečią"</string> <string name="guest_nickname" msgid="6332276931583337261">"Svečias"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotografuoti"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pasirinkti vaizdą"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index d655afc7ae35..586de6fcfdf7 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -567,8 +567,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Atiestatīt viesa sesiju"</string> <string name="guest_nickname" msgid="6332276931583337261">"Viesis"</string> <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> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index bd7f074f01a0..0d0f345af7c3 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Прекар"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Додајте гостин"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Отстрани гостин"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Ресетирајте го гостинот"</string> <string name="guest_nickname" msgid="6332276931583337261">"Гостин"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Фотографирајте"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Одберете слика"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 1e80ccd0f3ab..655be1a74c31 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"വിളിപ്പേര്"</string> <string name="guest_new_guest" msgid="3482026122932643557">"അതിഥിയെ ചേർക്കുക"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"അതിഥിയെ നീക്കം ചെയ്യുക"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"അതിഥിയെ റീസെറ്റ് ചെയ്യുക"</string> <string name="guest_nickname" msgid="6332276931583337261">"അതിഥി"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ഒരു ഫോട്ടോ എടുക്കുക"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ഒരു ചിത്രം തിരഞ്ഞെടുക്കുക"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 40b65efe275e..07ed369b8d36 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Хоч"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Зочин нэмэх"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Зочин хасах"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Зочныг шинэчлэх"</string> <string name="guest_nickname" msgid="6332276931583337261">"Зочин"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Зураг авах"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Зураг сонгох"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 6a46edee6ce5..fe8e8375a65a 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"टोपणनाव"</string> <string name="guest_new_guest" msgid="3482026122932643557">"अतिथी जोडा"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथी काढून टाका"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"अतिथी सेशन रीसेट करा"</string> <string name="guest_nickname" msgid="6332276931583337261">"अतिथी"</string> <string name="user_image_take_photo" msgid="467512954561638530">"फोटो काढा"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"इमेज निवडा"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 7156a8ef6548..b383ad84cadb 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"နာမည်ပြောင်"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ဧည့်သည့် ထည့်ရန်"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ဧည့်သည်ကို ဖယ်ထုတ်ရန်"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"ဧည့်သည်ကို ပြင်ဆင်သတ်မှတ်ရန်"</string> <string name="guest_nickname" msgid="6332276931583337261">"ဧည့်သည်"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ဓာတ်ပုံရိုက်ရန်"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ပုံရွေးရန်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index bb93a29feaa5..84c231faed46 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Tilbakestill gjest"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gjest"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ta et bilde"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Velg et bilde"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index a33bbf3e2dd9..1837eabf2370 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Gastsessie resetten"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gast"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Foto maken"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Afbeelding kiezen"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 5f410a86691b..bd77d881560f 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -568,8 +568,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Resetuj sesję gościa"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gość"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Zrób zdjęcie"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Wybierz obraz"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 3c8d928a7213..e39b58bb8344 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Redefinir sessão de visitante"</string> <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index fc98aa732606..a5c0d1d58d40 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Repor convidado"</string> <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 3c8d928a7213..e39b58bb8344 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Redefinir sessão de visitante"</string> <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 4db2f315a452..e1b0390fbace 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -567,8 +567,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Resetați sesiunea pentru invitați"</string> <string name="guest_nickname" msgid="6332276931583337261">"Invitat"</string> <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> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 0bcdc230c089..1e5fce5f3bc9 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -568,8 +568,7 @@ <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Добавить гостя"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Удалить аккаунт гостя"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Сбросить гостевой сеанс"</string> <string name="guest_nickname" msgid="6332276931583337261">"Гость"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Сделать снимок"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбрать фото"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index ef12ad36c5e1..09c0bf993f34 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -568,8 +568,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Obnoviť reláciu hosťa"</string> <string name="guest_nickname" msgid="6332276931583337261">"Hosť"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Odfotiť"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrať obrázok"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index cbdfefcd424a..b698c19ff8b8 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -568,8 +568,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Ponastavi gosta"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiranje"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Izberi sliko"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 3859be937d47..b3c23b667a42 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -567,8 +567,7 @@ <string name="user_nickname" msgid="262624187455825083">"Надимак"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Додај госта"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Уклони госта"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Ресетуј сесију госта"</string> <string name="guest_nickname" msgid="6332276931583337261">"Гост"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Сликај"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Одабери слику"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 7dcca0d2b611..124c063bea95 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Återställ gästsession"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gäst"</string> <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> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index b7aff721c405..7f67b88aa837 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,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"ชื่อเล่น"</string> <string name="guest_new_guest" msgid="3482026122932643557">"เพิ่มผู้ใช้ชั่วคราว"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"นำผู้ใช้ชั่วคราวออก"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"รีเซ็ตผู้เข้าร่วม"</string> <string name="guest_nickname" msgid="6332276931583337261">"ผู้ใช้ชั่วคราว"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ถ่ายรูป"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"เลือกรูปภาพ"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 6d1f6abbdf96..cc7db29f3d32 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"I-reset ang bisita"</string> <string name="guest_nickname" msgid="6332276931583337261">"Bisita"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Kumuha ng larawan"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pumili ng larawan"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 82e481bd0613..2cb10763fcc9 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -568,8 +568,7 @@ <string name="user_nickname" msgid="262624187455825083">"Псевдонім"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Додати гостя"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Видалити гостя"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Скинути сеанс у режимі \"Гість\""</string> <string name="guest_nickname" msgid="6332276931583337261">"Гість"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Зробити фотографію"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Вибрати зображення"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 7f4e30aa33c4..3e11124597d9 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Mehmon seansini tiklash"</string> <string name="guest_nickname" msgid="6332276931583337261">"Mehmon"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Suratga olish"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Rasm tanlash"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 2fe791a5a47c..0fa97e2011ce 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -566,8 +566,7 @@ <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> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Setha kabusha isivakashi"</string> <string name="guest_nickname" msgid="6332276931583337261">"Isihambeli"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Thatha isithombe"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Khetha isithombe"</string> 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 b0c5314a2ec0..53a382a9ebf6 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 @@ public class SettingsSpinnerPreferenceTest { 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/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 8c092ae37222..604310a9e905 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/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml index 149b31323ab3..0a036c811867 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 af43cf7a293f..a41cce7b74bf 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 b307544f5e3c..0692aeff0e0e 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 52fa2d85d074..cd98ef60994d 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 000000000000..fc3b4ed6eecb --- /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/SystemUI/res/anim/tv_bottom_sheet_enter.xml b/packages/SystemUI/res/anim/tv_bottom_sheet_enter.xml new file mode 100644 index 000000000000..cace36d68b43 --- /dev/null +++ b/packages/SystemUI/res/anim/tv_bottom_sheet_enter.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<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/SystemUI/res/anim/tv_bottom_sheet_exit.xml b/packages/SystemUI/res/anim/tv_bottom_sheet_exit.xml new file mode 100644 index 000000000000..f7efe7cd2584 --- /dev/null +++ b/packages/SystemUI/res/anim/tv_bottom_sheet_exit.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<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 000000000000..e6ceeb919232 --- /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 000000000000..4a510ae6cab8 --- /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/SystemUI/res/anim/tv_privacy_chip_fade_in.xml b/packages/SystemUI/res/anim/tv_privacy_chip_fade_in.xml new file mode 100644 index 000000000000..701489a28450 --- /dev/null +++ b/packages/SystemUI/res/anim/tv_privacy_chip_fade_in.xml @@ -0,0 +1,25 @@ +<?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="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 000000000000..fa134717ba6e --- /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/SystemUI/res/color/bottom_sheet_button_background_color.xml b/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml new file mode 100644 index 000000000000..9b0bae09b326 --- /dev/null +++ b/packages/SystemUI/res/color/bottom_sheet_button_background_color.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. + --> +<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/SystemUI/res/color/bottom_sheet_button_text_color.xml b/packages/SystemUI/res/color/bottom_sheet_button_text_color.xml new file mode 100644 index 000000000000..05248f17097b --- /dev/null +++ b/packages/SystemUI/res/color/bottom_sheet_button_text_color.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. + --> +<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/drawable/tv_rect_shadow_rounded.xml b/packages/SystemUI/res/drawable/bottom_sheet_background.xml index 93f8724b22a9..87850a0d00ec 100644 --- a/packages/SystemUI/res/drawable/tv_rect_shadow_rounded.xml +++ b/packages/SystemUI/res/drawable/bottom_sheet_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_background_color"/> + <corners android:radius="@dimen/bottom_sheet_corner_radius"/> </shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.xml b/packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.xml new file mode 100644 index 000000000000..cd2aa9c751d9 --- /dev/null +++ b/packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> + <solid android:color="@color/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/bottom_sheet_button_background.xml b/packages/SystemUI/res/drawable/bottom_sheet_button_background.xml new file mode 100644 index 000000000000..585a6bc771f7 --- /dev/null +++ b/packages/SystemUI/res/drawable/bottom_sheet_button_background.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> + <solid android:color="@color/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/interpolator/tv_privacy_chip_collapse_interpolator.xml b/packages/SystemUI/res/interpolator/tv_privacy_chip_collapse_interpolator.xml new file mode 100644 index 000000000000..429812484674 --- /dev/null +++ b/packages/SystemUI/res/interpolator/tv_privacy_chip_collapse_interpolator.xml @@ -0,0 +1,22 @@ +<?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. + --> + +<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" + 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/SystemUI/res/interpolator/tv_privacy_chip_expand_interpolator.xml b/packages/SystemUI/res/interpolator/tv_privacy_chip_expand_interpolator.xml new file mode 100644 index 000000000000..ed44715130e9 --- /dev/null +++ b/packages/SystemUI/res/interpolator/tv_privacy_chip_expand_interpolator.xml @@ -0,0 +1,22 @@ +<?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. + --> + +<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" + 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/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml index b563633f24fe..16c03e124794 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/tv_bottom_sheet.xml b/packages/SystemUI/res/layout/tv_bottom_sheet.xml new file mode 100644 index 000000000000..b69cdc76ae40 --- /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 dff148b2c570..6218a5e6ea82 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/transition/tv_privacy_chip_collapse.xml b/packages/SystemUI/res/transition/tv_privacy_chip_collapse.xml new file mode 100644 index 000000000000..f22e8ef9d005 --- /dev/null +++ b/packages/SystemUI/res/transition/tv_privacy_chip_collapse.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"> + <fade android:fadingMode="fade_in" /> + <changeBounds/> +</transitionSet> diff --git a/packages/SystemUI/res/transition/tv_privacy_chip_expand.xml b/packages/SystemUI/res/transition/tv_privacy_chip_expand.xml new file mode 100644 index 000000000000..059ebc84ea7e --- /dev/null +++ b/packages/SystemUI/res/transition/tv_privacy_chip_expand.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"> + <changeBounds/> + <fade android:fadingMode="fade_out" /> +</transitionSet> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 4736adbfb62f..ae8de876bfeb 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"ተጠቃሚ አክል"</string> <string name="user_new_user_name" msgid="2019166282704195789">"አዲስ ተጠቃሚ"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"እንግዳ ይወገድ?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"እንግዳ ዳግም ይጀምር?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"በዚህ ክፍለ-ጊዜ ውስጥ ያሉ ሁሉም መተግበሪያዎች እና ውሂብ ይሰረዛሉ።"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"አስወግድ"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"ዳግም አስጀምር"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"እንኳን በደህና ተመለሱ እንግዳ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ክፍለ-ጊዜዎን መቀጠል ይፈልጋሉ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"እንደገና ጀምር"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index b493d897fd22..2881a04fbb50 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -488,12 +488,10 @@ <string name="user_add_user" msgid="4336657383006913022">"إضافة مستخدم"</string> <string name="user_new_user_name" msgid="2019166282704195789">"مستخدم جديد"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"هل تريد إزالة جلسة الضيف؟"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"هل تريد إعادة ضبط جلسة الضيف؟"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"سيتم حذف كل التطبيقات والبيانات في هذه الجلسة."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"إزالة"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"إعادة الضبط"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"مرحبًا بك مجددًا في جلسة الضيف"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"هل تريد متابعة جلستك؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"البدء من جديد"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index c3b96a40023d..9502454d1c8c 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -1035,7 +1035,7 @@ <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> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index fe89267456a9..345dcaa8d46f 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -482,12 +482,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Dodaj korisnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Želite li da uklonite gosta?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Želite li da resetujete sesiju gosta?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji će biti izbrisani."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Resetuj"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Dobro došli nazad, goste!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li da nastavite sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni iz početka"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 2e98ad94383a..5afd979d34f5 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -484,12 +484,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Дадаць карыстальніка"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Новы карыстальнік"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Выдаліць госця?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Скінуць гасцявы сеанс?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усе праграмы і даныя гэтага сеанса будуць выдалены."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Выдаліць"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Скінуць"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"З вяртаннем, госць!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Хочаце працягнуць сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Пачаць зноў"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 9f71f43605ae..c035553e3e99 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"ব্যবহারকারী জুড়ুন"</string> <string name="user_new_user_name" msgid="2019166282704195789">"নতুন ব্যবহারকারী"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"অতিথি সরাবেন?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"গেস্ট সেশন রিসেট করবেন?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই সেশনের সব অ্যাপ ও ডেটা মুছে ফেলা হবে।"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"সরান"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"রিসেট করুন"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"অতিথি, আপনি ফিরে আসায় আপনাকে স্বাগত!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"আপনি কি আপনার সেশনটি চালিয়ে যেতে চান?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আবার শুরু করুন"</string> @@ -1038,8 +1036,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> - <!-- no translation found for accessibility_floating_button_switch_migration_tooltip (6248529129221218770) --> - <skip /> + <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> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 17dbbe95a010..ae7ac13cf220 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -482,12 +482,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Dodaj korisnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ukloniti gosta?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Poništiti sesiju gosta?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci iz ove sesije će se izbrisati."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Poništi"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Zdravo! Lijepo je opet vidjeti goste."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 2428d55fa1e6..14d391655791 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Afegeix un usuari"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Usuari nou"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vols suprimir el convidat?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Vols restablir el convidat?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Totes les aplicacions i les dades d\'aquesta sessió se suprimiran."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Suprimeix"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Restableix"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Benvingut de nou, convidat."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vols continuar amb la sessió?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Torna a començar"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index d8e0a6a1d446..ae0f0f3836a2 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -484,12 +484,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Přidat uživatele"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nový uživatel"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Odstranit hosta?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Resetovat hosta?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Veškeré aplikace a data v této relaci budou vymazána."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Odstranit"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Resetovat"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Vítejte zpět v relaci hosta!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relaci pokračovat?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začít znovu"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index ca44f0a350d7..78a56cd7c9e9 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Tilføj bruger"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Ny bruger"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vil du fjerne gæsten?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Vil du nulstille gæsten?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps og data i denne session slettes."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Fjern"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Nulstil"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkommen tilbage, gæst!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsætte din session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start forfra"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 274ca3a32a2c..672ffd04188f 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Nutzer hinzufügen"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Neuer Nutzer"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gast entfernen?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Gastsitzung zurücksetzen?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle Apps und Daten in dieser Sitzung werden gelöscht."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Entfernen"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Zurücksetzen"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Willkommen zurück im Gastmodus"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Möchtest du deine Sitzung fortsetzen?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Neu starten"</string> @@ -1038,8 +1036,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> - <!-- no translation found for accessibility_floating_button_switch_migration_tooltip (6248529129221218770) --> - <skip /> + <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> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 7b135a3e32da..3662da203380 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Προσθήκη χρήστη"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Νέος χρήστης"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Κατάργηση επισκέπτη;"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Επαναφορά περιόδου επισκέπτη;"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Όλες οι εφαρμογές και τα δεδομένα αυτής της περιόδου σύνδεσης θα διαγραφούν."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Κατάργηση"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Επαναφορά"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Kαλώς ορίσατε ξανά!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Θέλετε να συνεχίσετε την περίοδο σύνδεσής σας;"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Έναρξη από την αρχή"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 0479ad0fcb85..13b76534dd90 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Agregar usuario"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Usuario nuevo"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"¿Quitar invitado?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"¿Quieres restablecer el usuario?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán las aplicaciones y los datos de esta sesión."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Quitar"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Restablecer"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"¡Hola de nuevo, invitado!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres retomar la sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 65648153e8f9..171afee61822 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Añadir usuario"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nuevo usuario"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"¿Quitar invitado?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"¿Restablecer invitado?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán todas las aplicaciones y datos de esta sesión."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Quitar"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Restablecer"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Hola de nuevo, invitado"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres continuar con la sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 662282106d1d..497845707e46 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Lisa kasutaja"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Uus kasutaja"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Kas eemaldada külaline?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Kas lähtestada külastajaseanss?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Seansi kõik rakendused ja andmed kustutatakse."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eemalda"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Lähtesta"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Tere tulemast tagasi, külaline!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Kas soovite seansiga jätkata?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Alusta uuesti"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 6d8a8fdfad0c..40f39989c83b 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Gehitu erabiltzaile bat"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Erabiltzaile berria"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gonbidatua kendu nahi duzu?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Gonbidatuentzako saioa berrezarri nahi duzu?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Saioko aplikazio eta datu guztiak ezabatuko dira."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Kendu"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Berrezarri"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Ongi etorri berriro, gonbidatu hori!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Saioarekin jarraitu nahi duzu?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Hasi berriro"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index ce3e676db760..f17fd4180718 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"افزودن کاربر"</string> <string name="user_new_user_name" msgid="2019166282704195789">"کاربر جدید"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"مهمان حذف شود؟"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"جلسه مهمان بازنشانی شود؟"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"همه برنامهها و دادههای این جلسه حذف خواهد شد."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"حذف"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"بازنشانی"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"مهمان گرامی، بازگشتتان را خوش آمد میگوییم!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"آیا میخواهید جلسهتان را ادامه دهید؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"شروع مجدد"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 90f47ef42a5a..3546ea0f02d3 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Lisää käyttäjä"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Uusi käyttäjä"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Poistetaaanko vieras?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Nollataanko vieras?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Kaikki sovellukset ja tämän istunnon tiedot poistetaan."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Poista"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Nollaa"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Tervetuloa takaisin!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Haluatko jatkaa istuntoa?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Aloita alusta"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 030428034545..39d564fbcbfb 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Ajouter un utilisateur"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nouvel utilisateur"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Supprimer l\'invité?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Réinitialiser la session Invité?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Supprimer"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Réinitialiser"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bienvenue à nouveau dans la session Invité"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recommencer"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index c4022611f897..4f0670161a1a 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Ajouter un utilisateur"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nouvel utilisateur"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Supprimer l\'invité ?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Réinitialiser la session Invité ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Supprimer"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Réinitialiser"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bienvenue à nouveau dans la session Invité"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la dernière session ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Non, nouvelle session"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index e7b3e925d5e2..0641cef02334 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Engadir usuario"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuario"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Queres quitar o convidado?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Queres restablecer a sesión de convidado?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Eliminaranse todas as aplicacións e datos desta sesión."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Quitar"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Restablecer"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Benvido de novo, convidado"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Queres continuar coa túa sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Comezar de novo"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index a99558e4d84e..25caa5347e85 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -1036,8 +1036,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> - <!-- no translation found for accessibility_floating_button_switch_migration_tooltip (6248529129221218770) --> - <skip /> + <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> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 4196b1f41d86..3d65fa44d61f 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"उपयोगकर्ता जोड़ें"</string> <string name="user_new_user_name" msgid="2019166282704195789">"नया उपयोगकर्ता"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"क्या आप मेहमान को हटाना चाहते हैं?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"क्या आप मेहमान के तौर पर ब्राउज़ करने का सेशन रीसेट करना चाहते हैं?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"इस सत्र के सभी ऐप्लिकेशन और डेटा को हटा दिया जाएगा."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"निकालें"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"रीसेट करें"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"मेहमान, आपका फिर से स्वागत है!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"क्या आप अपना सत्र जारी रखना चाहते हैं?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"फिर से शुरू करें"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 6f7aee23f190..abbbae7985d4 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -482,12 +482,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Dodavanje korisnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ukloniti gosta?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Poništiti gostujuću sesiju?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji bit će izbrisani."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Poništi"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Dobro došli natrag, gostu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 800c9dc36a07..15106bb4c157 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Felhasználó hozzáadása"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Új felhasználó"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Eltávolítja a vendég munkamenetet?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Visszaállítja a vendég munkamenetet?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"A munkamenetben található összes alkalmazás és adat törlődni fog."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eltávolítás"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Visszaállítás"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Örülünk, hogy visszatért, vendég!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Folytatja a munkamenetet?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Újrakezdés"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index deeeb209e1e3..2e2141ee075c 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Ավելացնել օգտատեր"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Նոր օգտատեր"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Հեռացնե՞լ հյուրին"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Վերակայե՞լ հյուրի աշխատաշրջանը"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Այս աշխատաշրջանի բոլոր ծրագրերն ու տվյալները կջնջվեն:"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Հեռացնել"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Վերակայել"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Բարի վերադարձ, հյուր"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Շարունակե՞լ աշխատաշրջանը։"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Վերսկսել"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index a6cb798a188e..53a3165776fd 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Tambahkan pengguna"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Pengguna baru"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Hapus tamu?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Reset sesi tamu?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua aplikasi dan data di sesi ini akan dihapus."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Hapus"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Reset"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Selamat datang kembali, tamu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Lanjutkan sesi Anda?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulai ulang"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 01bad1c1ce90..151564aee0d4 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Bæta notanda við"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nýr notandi"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Fjarlægja gest?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Endurstilla gestastillingu?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Öllum forritum og gögnum í þessari lotu verður eytt."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Fjarlægja"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Endurstilla"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkominn aftur, gestur!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Viltu halda áfram með lotuna?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Byrja upp á nýtt"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 066cd9344e91..454b5bba4a49 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Aggiungi utente"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nuovo utente"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Rimuovere l\'ospite?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Reimpostare sessione Ospite?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tutte le app e i dati di questa sessione verranno eliminati."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Rimuovi"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Reimposta"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Ti ridiamo il benvenuto alla sessione Ospite."</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vuoi continuare la sessione?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Ricomincia"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index b2f0fd259702..3dedff995e0e 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -484,12 +484,10 @@ <string name="user_add_user" msgid="4336657383006913022">"הוספת משתמש"</string> <string name="user_new_user_name" msgid="2019166282704195789">"משתמש חדש"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"להסיר אורח?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"לאפס את הגלישה כאורח?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"כל האפליקציות והנתונים בסשן הזה יימחקו."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"הסרה"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"איפוס"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"שמחים לראותך שוב!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"האם ברצונך להמשיך בפעילות באתר?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"סשן חדש"</string> @@ -725,7 +723,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> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index b1736534ffd1..dacdf2146540 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"ユーザーを追加"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新しいユーザー"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ゲストを削除しますか?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"ゲストをリセットしますか?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"このセッションでのアプリとデータはすべて削除されます。"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"削除"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"リセット"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"おかえりなさい、ゲストさん"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"セッションを続行しますか?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"最初から開始"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 09d5f9f6e3af..8366b2f48927 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Пайдаланушы қосу"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Жаңа пайдаланушы"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Қонақты жою керек пе?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Қонақ сеансы бастапқы күйге қайтарылсын ба?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Осы сеанстағы барлық қолданбалар мен деректер жойылады."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Алып тастау"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Бастапқы күйге қайтару"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Қош келдіңіз, қонақ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансты жалғастыру керек пе?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Қайта бастау"</string> @@ -668,7 +666,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> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 065a49951dfb..3baa4e1411f1 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"បញ្ចូលអ្នកប្រើ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"អ្នកប្រើថ្មី"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ដកភ្ញៀវចេញឬ?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"កំណត់ភ្ញៀវឡើងវិញឬ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"កម្មវិធី និងទិន្នន័យទាំងអស់ក្នុងវគ្គនេះនឹងត្រូវលុប។"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ដកចេញ"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"កំណត់ឡើងវិញ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"សូមស្វាគមន៍ការត្រឡប់មកវិញ, ភ្ញៀវ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"តើអ្នកចង់បន្តវគ្គរបស់អ្នកទេ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ចាប់ផ្ដើមសាជាថ្មី"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 0b21c0f37010..7f57da18de82 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಿ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ಹೊಸ ಬಳಕೆದಾರರು"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕುವುದೇ?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"ಅತಿಥಿಯನ್ನು ಮರುಹೊಂದಿಸಬೇಕೆ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ಈ ಸೆಷನ್ನಲ್ಲಿನ ಎಲ್ಲ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ತೆಗೆದುಹಾಕಿ"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"ಮರುಹೊಂದಿಸಿ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ಮತ್ತೆ ಸುಸ್ವಾಗತ, ಅತಿಥಿ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ನಿಮ್ಮ ಸೆಷನ್ ಮುಂದುವರಿಸಲು ಇಚ್ಚಿಸುವಿರಾ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ಪ್ರಾರಂಭಿಸಿ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 80415d531d39..ab80f7f3acdc 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"사용자 추가"</string> <string name="user_new_user_name" msgid="2019166282704195789">"신규 사용자"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"게스트를 삭제하시겠습니까?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"게스트를 초기화하시겠습니까?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"삭제"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"초기화"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"게스트 세션 진행"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"세션을 계속 진행하시겠습니까?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"다시 시작"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 2474fe1f25b5..037a765870d4 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Колдонуучу кошуу"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Жаңы колдонуучу"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Конокту алып саласызбы?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Конок сеансын баштапкы абалга келтиресизби?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Бул сеанстагы бардык колдонмолор жана маалыматтар өчүрүлөт."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Өчүрүү"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Баштапкы абалга келтирүү"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Кайтып келишиңиз менен!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансыңызды улантасызбы?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Кайра баштоо"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 106f7f20a5ba..4cdc8a5fcb4e 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"ເພີ່ມຜູ້ໃຊ້"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ຜູ່ໃຊ້ໃໝ່"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ລຶບແຂກບໍ?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"ຣີເຊັດແຂກບໍ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ແອັບຯແລະຂໍ້ມູນທັງໝົດໃນເຊດຊັນນີ້ຈະຖືກລຶບອອກ."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ລຶບ"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"ຣີເຊັດ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ຍິນດີຕ້ອນຮັບກັບມາ, ຜູ້ຢ້ຽມຢາມ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ທ່ານຕ້ອງການສືບຕໍ່ເຊດຊັນຂອງທ່ານບໍ່?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ເລີ່ມຕົ້ນໃຫມ່"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 44dc8bb6aa30..ce64e4c3d765 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -484,12 +484,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Pridėti naudotoją"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Naujas naudotojas"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Pašalinti svečią?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Nustatyti svečią iš naujo?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bus ištrintos visos šios sesijos programos ir duomenys."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Pašalinti"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Nustatyti iš naujo"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Sveiki sugrįžę, svety!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Ar norite tęsti sesiją?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Pradėti iš naujo"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index b846be4491f3..518a28b6d483 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -482,12 +482,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Lietotāja pievienošana"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Jauns lietotājs"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vai noņemt viesi?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Vai atiestatīt viesa sesiju?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tiks dzēstas visas šīs sesijas lietotnes un dati."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Noņemt"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Atiestatīt"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Laipni lūdzam atpakaļ, viesi!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vai vēlaties turpināt savu sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Sākt no sākuma"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 535da7a98f87..90f775bcd310 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Додај корисник"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Нов корисник"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Да се отстрани гостинот?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Да се ресетира гостинот?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Сите апликации и податоци во сесијата ќе се избришат."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Отстрани"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Ресетирај"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добре дојде пак, гостине!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Дали сакате да продолжите со сесијата?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни одново"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 74d087c73d39..6923fef29d4b 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"ഉപയോക്താവിനെ ചേര്ക്കുക"</string> <string name="user_new_user_name" msgid="2019166282704195789">"പുതിയ ഉപയോക്താവ്"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"അതിഥിയെ നീക്കംചെയ്യണോ?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"അതിഥിയെ റീസെറ്റ് ചെയ്യണോ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ഈ സെഷനിലെ എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കും."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"നീക്കംചെയ്യുക"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"റീസെറ്റ് ചെയ്യുക"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"അതിഥി, വീണ്ടും സ്വാഗതം!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"നിങ്ങളുടെ സെഷൻ തുടരണോ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"പുനരാംരംഭിക്കുക"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 8dae31d1c7f2..976c2dd420c8 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Хэрэглэгч нэмэх"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Шинэ хэрэглэгч"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Зочныг хасах уу?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Зочныг шинэчлэх үү?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Энэ харилцан үйлдлийн бүх апп болон дата устах болно."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Хасах"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Шинэчлэх"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Эргэн тавтай морилно уу!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Та үргэлжлүүлэхийг хүсэж байна уу?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Дахин эхлүүлэх"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 3aae0640802f..f649ca485d83 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"वापरकर्ता जोडा"</string> <string name="user_new_user_name" msgid="2019166282704195789">"नवीन वापरकर्ता"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"अतिथी काढायचे?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"अतिथीला रीसेट करायचे आहे का?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"या सत्रातील सर्व अॅप्स आणि डेटा हटवला जाईल."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"काढा"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"रीसेट करा"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"अतिथी, तुमचे पुन्हा स्वागत आहे!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"तुम्ही तुमचे सत्र सुरू ठेवू इच्छिता?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"येथून सुरू करा"</string> @@ -1038,8 +1036,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> - <!-- no translation found for accessibility_floating_button_switch_migration_tooltip (6248529129221218770) --> - <skip /> + <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> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index f3838c4ce802..50342920080c 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Tambah pengguna"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Pengguna baharu"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Alih keluar tetamu?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Tetapkan semula tetamu?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua apl dan data dalam sesi ini akan dipadam."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Alih keluar"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Tetapkan semula"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Selamat kembali, tetamu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Adakah anda ingin meneruskan sesi anda?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulakan semula"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 9a8fc20fd6c4..efe716e9d04a 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"အသုံးပြုသူ ထည့်ရန်"</string> <string name="user_new_user_name" msgid="2019166282704195789">"အသုံးပြုသူ အသစ်"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ဧည့်သည်ကို ဖယ်မလား။"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"ဧည့်သည်ကို ပြင်ဆင်သတ်မှတ်မလား။"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ဒီချိတ်ဆက်မှု ထဲက အက်ပ်များ အားလုံး နှင့် ဒေတာကို ဖျက်ပစ်မည်။"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ဖယ်ထုတ်ပါ"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"ပြင်ဆင်သတ်မှတ်ရန်"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ဧည့်သည်ကို ပြန်လည် ကြိုဆိုပါသည်။"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"သင်၏ စက်ရှင်ကို ဆက်လုပ်လိုပါသလား။"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ပြန်စပါ"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 71ae67261280..2f07a3e56ffd 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Legg til brukere"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Ny bruker"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vil du fjerne gjesten?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Vil du tilbakestille gjesten?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle appene og all informasjon i denne økten slettes."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Fjern"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Tilbakestill"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkommen tilbake, gjest!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsette økten?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start på nytt"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 0006f7f824a3..10311b048e2f 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> @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"प्रयोगकर्ता थप्नुहोस्"</string> <string name="user_new_user_name" msgid="2019166282704195789">"नयाँ प्रयोगकर्ता"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"अतिथि हटाउने हो?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"अतिथि सत्र रिसेट गर्ने हो?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"यो सत्रमा भएका सबै एपहरू र डेटा मेटाइने छ।"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"हटाउनुहोस्"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"रिसेट गर्नुहोस्"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"तपाईंलाई फेरि स्वागत छ, अतिथि"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"तपाईं आफ्नो सत्र जारी गर्न चाहनुहुन्छ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"सुरु गर्नुहोस्"</string> @@ -1038,8 +1036,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> - <!-- no translation found for accessibility_floating_button_switch_migration_tooltip (6248529129221218770) --> - <skip /> + <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> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 944753ad570b..8bd41deb9499 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Gebruiker toevoegen"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nieuwe gebruiker"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gast verwijderen?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Gastsessie resetten?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps en gegevens in deze sessie worden verwijderd."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Verwijderen"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Resetten"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welkom terug, gast!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Wil je doorgaan met je sessie?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Opnieuw starten"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 1dcb1434bc29..8fede329cff3 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଯୋଗ କରନ୍ତୁ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ଅତିଥିଙ୍କୁ କାଢ଼ିଦେବେ?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"ଅତିଥିଙ୍କୁ ରିସେଟ୍ କରିବେ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ଏହି ସେସନର ସମସ୍ତ ଆପ୍ ଓ ଡାଟା ଡିଲିଟ୍ ହୋଇଯିବ।"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"କାଢ଼ିଦିଅନ୍ତୁ"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"ରିସେଟ୍ କରନ୍ତୁ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ପୁଣି ସ୍ୱାଗତ, ଅତିଥି!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ଆପଣ ନିଜର ସେସନ୍ ଜାରି ରଖିବାକୁ ଚାହାଁନ୍ତି କି?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ଆରମ୍ଭ କରନ୍ତୁ"</string> @@ -1038,8 +1036,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> - <!-- no translation found for accessibility_floating_button_switch_migration_tooltip (6248529129221218770) --> - <skip /> + <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> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 0f687a142544..da10cb48b364 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰੋ"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ਕੀ ਮਹਿਮਾਨ ਹਟਾਉਣਾ ਹੈ?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"ਕੀ ਗੈਸਟ ਨੂੰ ਰੀਸੈੱਟ ਕਰਨਾ ਹੈ?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ਇਸ ਸੈਸ਼ਨ ਵਿੱਚ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਜਾਏਗਾ।"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ਹਟਾਓ"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"ਰੀਸੈੱਟ ਕਰੋ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ਮਹਿਮਾਨ, ਫਿਰ ਤੁਹਾਡਾ ਸੁਆਗਤ ਹੈ!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ਕੀ ਤੁਸੀਂ ਆਪਣਾ ਸੈਸ਼ਨ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ਮੁੜ-ਸ਼ੁਰੂ ਕਰੋ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index e822d936eca4..704d38459735 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -484,12 +484,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Dodaj użytkownika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nowy użytkownik"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Usunąć gościa?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Zresetować sesję gościa?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wszystkie aplikacje i dane w tej sesji zostaną usunięte."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Usuń"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Resetuj"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Witaj ponownie, Gościu!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcesz kontynuować sesję?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Rozpocznij nową"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index ad41e5535e6d..e4dbca5eb8e3 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Adicionar usuário"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuário"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover visitante?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Redefinir sessão de visitante?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Redefinir"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Você voltou, visitante!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 738e91644681..a9cb3dc88fa1 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Adicionar utilizador"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo utilizador"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover o convidado?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Pretende repor a sessão de convidado?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todas as apps e dados desta sessão serão eliminados."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Repor"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bem-vindo de volta, convidado!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Pretende continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index ad41e5535e6d..e4dbca5eb8e3 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Adicionar usuário"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuário"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover visitante?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Redefinir sessão de visitante?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Redefinir"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Você voltou, visitante!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 3f9a576156d5..42c8dbbf17e3 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -482,12 +482,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Adăugați un utilizator"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Utilizator nou"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ștergeți invitatul?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Resetați invitatul?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toate aplicațiile și datele din această sesiune vor fi șterse."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ștergeți"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Resetați"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bine ați revenit în sesiunea pentru invitați!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vreți să continuați sesiunea?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Începeți din nou"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 67091b957467..e324c35312d8 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -484,12 +484,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Добавить пользователя"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Новый пользователь"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Удалить аккаунт гостя?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Сбросить гостевой сеанс?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Все приложения и данные этого профиля будут удалены."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Удалить"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Сбросить"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Рады видеть вас снова!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Продолжить сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Начать заново"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 3a8b51c49117..6c82ad58a25f 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"පරිශීලකයෙක් එක් කරන්න"</string> <string name="user_new_user_name" msgid="2019166282704195789">"නව පරිශීලකයා"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"අමුත්තාන් ඉවත් කරන්නද?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"ආගන්තුකයා යළි සකසන්නද?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"මෙම සැසියේ සියළුම යෙදුම් සහ දත්ත මකාවී."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ඉවත් කරන්න"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"යළි සකසන්න"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"නැවත සාදරයෙන් පිළිගනිමු, අමුත්තා!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"ඔබගේ සැසිය දිගටම කරගෙන යෑමට ඔබට අවශ්යද?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"යළි මුල සිට අරඹන්න"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 19a871e461d3..31bea134ab32 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -484,12 +484,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Pridať používateľa"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nový používateľ"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Odstrániť hosťa?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Chcete resetovať reláciu hosťa?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Všetky aplikácie a údaje v tejto relácii budú odstránené."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Odstrániť"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Resetovať"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Hosť, vitajte späť!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relácii pokračovať?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začať odznova"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index c58343a9fd9e..420e8cb195ca 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -484,12 +484,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Dodajanje uporabnika"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nov uporabnik"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Želite odstraniti gosta?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Želite ponastaviti gosta?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Vse aplikacije in podatki v tej seji bodo izbrisani."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Odstrani"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Ponastavi"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Znova pozdravljeni, gost!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite nadaljevati sejo?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začni znova"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 334b2ed0a603..e4238d27047d 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Shto përdorues"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Përdorues i ri"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Të hiqet i ftuari?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Të rivendoset vizitori?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Të gjitha aplikacionet dhe të dhënat në këtë sesion do të fshihen."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Hiq"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Rivendos"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Mirë se erdhe, i ftuar!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Dëshiron ta vazhdosh sesionin tënd?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Fillo nga e para"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index e4a1bea6f998..4e82fd739dae 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -482,12 +482,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Додај корисника"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Нови корисник"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Желите ли да уклоните госта?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Желите ли да ресетујете сесију госта?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Све апликације и подаци у овој сесији ће бити избрисани."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Уклони"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Ресетуј"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добро дошли назад, госте!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Желите ли да наставите сесију?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни из почетка"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 3ceef49a21e7..390a7bc268e3 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Lägg till användare"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Ny användare"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vill du ta bort gästen?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Vill du återställa gästsessionen?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alla appar och data i denna session kommer att raderas."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ta bort"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Återställ"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Välkommen tillbaka som gäst!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vill du fortsätta sessionen?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Börja om"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 0a4e05923f89..99bf399b3c1b 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Ongeza mtumiaji"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Mtumiaji mpya"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ungependa kumwondoa mgeni?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Ungependa kubadilisha kipindi cha mgeni?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Data na programu zote katika kipindi hiki zitafutwa."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ondoa"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Badilisha"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Karibu tena mgeni!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Je, unataka kuendelea na kipindi chako?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Anza upya"</string> diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index 9f8e6364e55d..da80b85b38bf 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 5dc158195ddc..7e3b412072f0 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"பயனரைச் சேர்"</string> <string name="user_new_user_name" msgid="2019166282704195789">"புதியவர்"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"கெஸ்ட்டை அகற்றவா?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"விருந்தினர் அமர்வை மீட்டமைக்கவா?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"இந்த அமர்வின் எல்லா ஆப்ஸும் தரவும் நீக்கப்படும்."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"அகற்று"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"மீட்டமை"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"நல்வரவு!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"உங்கள் அமர்வைத் தொடர விருப்பமா?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"மீண்டும் தொடங்கு"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 348c37ae2e60..1f7146f98d75 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"యూజర్ను జోడించండి"</string> <string name="user_new_user_name" msgid="2019166282704195789">"కొత్త వినియోగదారు"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"గెస్ట్ను తీసివేయాలా?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"గెస్ట్ సెషన్ను రీసెట్ చేయాలా?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్లోని అన్ని యాప్లు మరియు డేటా తొలగించబడతాయి."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"తీసివేయి"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"రీసెట్ చేయండి"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"గెస్ట్కు తిరిగి స్వాగతం!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"మీరు మీ సెషన్ని కొనసాగించాలనుకుంటున్నారా?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"మొదటి నుండి ప్రారంభించు"</string> @@ -1038,8 +1036,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> - <!-- no translation found for accessibility_floating_button_switch_migration_tooltip (6248529129221218770) --> - <skip /> + <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> diff --git a/packages/SystemUI/res/values-television/colors.xml b/packages/SystemUI/res/values-television/colors.xml index e5f3b4768fe2..566f6b229bc9 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/dimens.xml b/packages/SystemUI/res/values-television/dimens.xml index 7626db93dd76..c258fcc4273a 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 587497edfdfb..b265d7812229 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 00217fb39b93..0fb789863bd9 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 d6eec3fd4fcf..b21d22faf958 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"เพิ่มผู้ใช้"</string> <string name="user_new_user_name" msgid="2019166282704195789">"ผู้ใช้ใหม่"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ต้องการนำผู้ใช้ชั่วคราวออกไหม"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"รีเซ็ตผู้เข้าร่วมไหม"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ระบบจะลบแอปและข้อมูลทั้งหมดในเซสชันนี้"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"นำออก"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"รีเซ็ต"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"ยินดีต้อนรับผู้เข้าร่วมกลับมาอีกครั้ง"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"คุณต้องการอยู่ในเซสชันต่อไปไหม"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"เริ่มต้นใหม่"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 0a7bdacc69c8..5b48488f18fe 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Magdagdag ng user"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Bagong user"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Alisin ang bisita?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"I-reset ang session ng bisita?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ide-delete ang lahat ng app at data sa session na ito."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Alisin"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"I-reset"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome ulit, bisita!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Gusto mo bang ipagpatuloy ang iyong session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Magsimulang muli"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 80e29fb5df6d..8a3d467fbe58 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Kullanıcı ekle"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Yeni kullanıcı"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Misafir oturumu kaldırılsın mı?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Misafir oturumu sıfırlansın mı?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu oturumdaki tüm uygulamalar ve veriler silinecek."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Kaldır"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Sıfırla"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Misafir kullanıcı, tekrar hoşgeldiniz"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Oturumunuza devam etmek istiyor musunuz?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Baştan başla"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 930bb0586753..efece98d635e 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"صارف کو شامل کریں"</string> <string name="user_new_user_name" msgid="2019166282704195789">"نیا صارف"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"مہمان کو ہٹائیں؟"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"مہمان کو ری سیٹ کریں؟"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"اس سیشن میں موجود سبھی ایپس اور ڈیٹا کو حذف کر دیا جائے گا۔"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ہٹائیں"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"ری سیٹ کریں"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"مہمان، پھر سے خوش آمدید!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"کیا آپ اپنا سیشن جاری رکھنا چاہتے ہیں؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"دوبارہ شروع کریں"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 241cfb222a18..c337d08102fc 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Thêm người dùng"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Người dùng mới"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Xóa phiên khách?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Đặt lại phiên khách?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tất cả ứng dụng và dữ liệu trong phiên này sẽ bị xóa."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Xóa"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Đặt lại"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Chào mừng bạn trở lại!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Bạn có muốn tiếp tục phiên của mình không?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Bắt đầu lại"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 25cc331d993b..9592b2d6aa5c 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"添加用户"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新用户"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"要移除访客吗?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"要重置访客会话吗?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"此会话中的所有应用和数据都将被删除。"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"重置"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"访客,欢迎回来!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"要继续您的会话吗?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新开始"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 5c58081f5035..b9ea4b0159e3 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"加入使用者"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新使用者"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"移除訪客?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"要重設訪客工作階段嗎?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會被刪除。"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"重設"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"訪客您好,歡迎回來!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"您要繼續您的工作階段嗎?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 5c4c3b15e997..7d82bf5a723b 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"新增使用者"</string> <string name="user_new_user_name" msgid="2019166282704195789">"新使用者"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"移除訪客?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"要重設訪客工作階段嗎?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會遭到刪除。"</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"重設"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"訪客你好,歡迎回來!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"你要繼續這個工作階段嗎?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 647102ac4895..a44c6ff9b744 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Engeza umsebenzisi"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Umsebenzisi omusha"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Susa isivakashi?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Setha kabusha isimenywa?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Zonke izinhlelo zokusebenza nedatha kulesi sikhathi zizosuswa."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Susa"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Setha kabusha"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Siyakwamukela futhi, sivakashi!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Ingabe ufuna ukuqhubeka ngesikhathi sakho?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Qala phansi"</string> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 9678affa96a7..2a1bee5344ec 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -445,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 3e4684c9b2d6..7525a9b1c19c 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -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. --> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index ef6212d4f354..a28d1747f2fe 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -205,6 +205,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } } + 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 123c0e63d7d1..2096c310744d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -187,10 +187,11 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } /** - * 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/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java index 7cd43eff8e2a..cff6cf1f53f1 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 @@ package com.android.systemui.accessibility.floatingmenu; 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 @@ public class AccessibilityFloatingMenuController implements 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 @@ public class AccessibilityFloatingMenuController implements @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 5bb55222e09b..e891e5b64b3d 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 @@ public class AccessibilityFloatingMenuView extends FrameLayout mListView = listView; mWindowManager = context.getSystemService(WindowManager.class); + mLastConfiguration = new Configuration(getResources().getConfiguration()); mAdapter = new AccessibilityTargetAdapter(mTargets); mUiHandler = createUiHandler(); mPosition = position; @@ -243,7 +245,6 @@ public class AccessibilityFloatingMenuView extends FrameLayout } }); - mLastConfiguration = new Configuration(getResources().getConfiguration()); initListView(); updateStrokeWith(getResources().getConfiguration().uiMode, mAlignment); @@ -567,8 +568,10 @@ public class AccessibilityFloatingMenuView extends FrameLayout 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 @@ public class AccessibilityFloatingMenuView extends FrameLayout 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 @@ public class AccessibilityFloatingMenuView extends FrameLayout @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 @@ public class AccessibilityFloatingMenuView extends FrameLayout updateRadiusWith(mSizeType, mRadiusType, mTargets.size()); updateScrollModeWith(hasExceededMaxLayoutHeight()); setSystemGestureExclusion(); - - mLastConfiguration.setTo(newConfig); } @VisibleForTesting @@ -756,11 +757,11 @@ public class AccessibilityFloatingMenuView extends FrameLayout } 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 @@ public class AccessibilityFloatingMenuView extends FrameLayout 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 @@ public class AccessibilityFloatingMenuView extends FrameLayout 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 @@ public class AccessibilityFloatingMenuView extends FrameLayout } 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/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index ec930b0c41d1..11412f41f578 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -67,6 +67,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator; 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; @@ -111,6 +112,7 @@ public class UdfpsController implements DozeReceiver { @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. @@ -507,6 +509,7 @@ public class UdfpsController implements DozeReceiver { @NonNull FalsingManager falsingManager, @NonNull PowerManager powerManager, @NonNull AccessibilityManager accessibilityManager, + @NonNull LockscreenShadeTransitionController lockscreenShadeTransitionController, @NonNull ScreenLifecycle screenLifecycle, @Nullable Vibrator vibrator, @NonNull Optional<UdfpsHbmProvider> hbmProvider) { @@ -529,6 +532,7 @@ public class UdfpsController implements DozeReceiver { mFalsingManager = falsingManager; mPowerManager = powerManager; mAccessibilityManager = accessibilityManager; + mLockscreenShadeTransitionController = lockscreenShadeTransitionController; mHbmProvider = hbmProvider.orElse(null); screenLifecycle.addObserver(mScreenObserver); mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON; @@ -716,6 +720,7 @@ public class UdfpsController implements DozeReceiver { mFgExecutor, mDumpManager, mKeyguardViewMediator, + mLockscreenShadeTransitionController, this ); case IUdfpsOverlayController.REASON_AUTH_BP: diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java index 00888dfe48d3..35ca470df523 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.R; 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 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @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 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud private boolean mFaceDetectRunning; private boolean mHintShown; private int mStatusBarState; + private float mTransitionToFullShadeProgress; /** * hidden amount of pin/pattern/password bouncer @@ -81,12 +84,14 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @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 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud updatePauseAuth(); mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor); + mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(this); } @Override @@ -127,6 +133,9 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud 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 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud } } + /** + * 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/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java index 2dbf30fdd289..de8ed7013ab2 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.people.widget.LaunchConversationActivity; 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 @@ public abstract class DefaultActivityBinder { @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 746621dfda27..d85c9a718871 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 @@ public class DependencyProvider { @Main Handler mainHandler, UiEventLogger uiEventLogger, NavigationBarOverlayController navBarOverlayController, - ConfigurationController configurationController) { + ConfigurationController configurationController, + UserTracker userTracker) { return new NavigationBarController(context, windowManager, assistManagerLazy, @@ -256,7 +257,8 @@ public class DependencyProvider { 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 053d75d96720..954ba797e164 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.service.dreams.IDreamManager; 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 @@ public class FrameworkServicesModule { } @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 7b34e52c16e8..455f3c0d6ac2 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -71,7 +71,6 @@ public class DozeTriggers implements DozeMachine.Part { * Assuming that the screen should start on. */ private static boolean sWakeDisplaySensorState = true; - private Runnable mQuickPickupDozeCancellable; private static final int PROXIMITY_TIMEOUT_DELAY_MS = 500; @@ -279,14 +278,14 @@ public class DozeTriggers implements DozeMachine.Part { 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 */); @@ -388,11 +387,7 @@ public class DozeTriggers implements DozeMachine.Part { */ 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) -> { @@ -405,27 +400,13 @@ public class DozeTriggers implements DozeMachine.Part { // 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); @@ -433,15 +414,6 @@ public class DozeTriggers implements DozeMachine.Part { } } - 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) { @@ -481,7 +453,6 @@ public class DozeTriggers implements DozeMachine.Part { mDozeSensors.requestTemporaryDisable(); break; case FINISH: - cancelQuickPickupDelayableDoze(); mBroadcastReceiver.unregister(mBroadcastDispatcher); mDozeHost.removeCallback(mHostCallback); mDockManager.removeListener(mDockEventListener); @@ -510,16 +481,6 @@ public class DozeTriggers implements DozeMachine.Part { } } - /** - * 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; - } - } private void checkTriggersAtInit() { if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index 3251ab2e4d50..3957a60c5b45 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 @@ import javax.inject.Provider 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. @@ -237,7 +238,7 @@ class MediaCarouselController @Inject constructor( 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 { @@ -266,7 +267,7 @@ class MediaCarouselController @Inject constructor( } 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 { @@ -384,7 +385,7 @@ class MediaCarouselController @Inject constructor( data: SmartspaceMediaData, shouldPrioritize: Boolean ) { - Log.d(TAG, "Updating smartspace target in carousel") + if (DEBUG) Log.d(TAG, "Updating smartspace target in carousel") if (MediaPlayerData.getMediaPlayer(key, null) != null) { Log.w(TAG, "Skip adding smartspace target in carousel") return diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index ed6f5505cbd1..df1b07f3e0b2 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -453,7 +453,8 @@ class MediaDataManager( 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) @@ -709,17 +710,19 @@ class MediaDataManager( override fun onSmartspaceTargetsUpdated(targets: List<Parcelable>) { 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) @@ -728,13 +731,12 @@ class MediaDataManager( 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 @@ -883,7 +885,7 @@ class MediaDataManager( 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) { @@ -893,7 +895,7 @@ class MediaDataManager( 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/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt index 3d1b4fbde56a..186f961ff1b6 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt @@ -660,15 +660,11 @@ class MediaHierarchyManager @Inject constructor( 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 } 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 0d5faff65aab..1d6d1f2e4885 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 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { 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 class MediaOutputAdapter extends MediaOutputBaseAdapter { public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) { super.onCreateViewHolder(viewGroup, viewType); - return new MediaDeviceViewHolder(mHolderView); } @@ -66,7 +65,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { 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 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { // 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 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { @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 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { } @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); @@ -160,6 +162,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { 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 +189,16 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { 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) 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 bcef43c93be4..0890841eb4fb 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 @@ public abstract class MediaOutputBaseAdapter extends Context mContext; View mHolderView; boolean mIsDragging; + int mCurrentActivePosition; public MediaOutputBaseAdapter(MediaOutputController controller) { mController = controller; mIsDragging = false; + mCurrentActivePosition = -1; } @Override @@ -99,6 +101,10 @@ public abstract class MediaOutputBaseAdapter extends return mIsAnimating; } + int getCurrentActivePosition() { + return mCurrentActivePosition; + } + /** * ViewHolder for binding device view. */ @@ -136,7 +142,7 @@ public abstract class MediaOutputBaseAdapter extends 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 @@ public abstract class MediaOutputBaseAdapter extends } 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 @@ public abstract class MediaOutputBaseAdapter extends } void initSessionSeekbar() { + disableSeekBar(); mSeekBar.setMax(mController.getSessionVolumeMax()); mSeekBar.setMin(0); final int currentVolume = mController.getSessionVolume(); @@ -330,5 +340,10 @@ public abstract class MediaOutputBaseAdapter extends 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 8a9a6e694658..cdcdf9a1d4de 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 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements 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 8fee9253a421..5293c8850267 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 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback { || 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 24e076bb22f1..968c3506f39f 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 @@ public class MediaOutputGroupAdapter extends MediaOutputBaseAdapter { 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 @@ public class MediaOutputGroupAdapter extends MediaOutputBaseAdapter { } @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 da09793580bb..711bb56dd95a 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.DarkIconDispatcher; 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 @@ public class NavigationBar implements View.OnAttachStateChangeListener, 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 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private boolean mTransientShown; private int mNavBarMode = NAV_BAR_MODE_3BUTTON; - private int mA11yBtnMode; private LightBarController mLightBarController; private AutoHideController mAutoHideController; @@ -459,7 +460,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, SystemActions systemActions, @Main Handler mainHandler, NavigationBarOverlayController navbarOverlayController, - UiEventLogger uiEventLogger) { + UiEventLogger uiEventLogger, + UserTracker userTracker) { mContext = context; mWindowManager = windowManager; mAccessibilityManager = accessibilityManager; @@ -484,10 +486,10 @@ public class NavigationBar implements View.OnAttachStateChangeListener, 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 @@ public class NavigationBar implements View.OnAttachStateChangeListener, 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 @@ public class NavigationBar implements View.OnAttachStateChangeListener, // 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 @@ public class NavigationBar implements View.OnAttachStateChangeListener, .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 @@ public class NavigationBar implements View.OnAttachStateChangeListener, @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 8b5a537ba242..53592101c3ea 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.model.SysUiState; 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 @@ public class NavigationBarController implements Callbacks, private final TaskbarDelegate mTaskbarDelegate; private int mNavMode; private boolean mIsTablet; + private final UserTracker mUserTracker; /** A displayId - nav bar maps. */ @VisibleForTesting @@ -151,7 +153,8 @@ public class NavigationBarController implements Callbacks, @Main Handler mainHandler, UiEventLogger uiEventLogger, NavigationBarOverlayController navBarOverlayController, - ConfigurationController configurationController) { + ConfigurationController configurationController, + UserTracker userTracker) { mContext = context; mWindowManager = windowManager; mAssistManagerLazy = assistManagerLazy; @@ -184,6 +187,7 @@ public class NavigationBarController implements Callbacks, mNavigationModeController.addListener(this); mTaskbarDelegate = new TaskbarDelegate(mOverviewProxyService); mIsTablet = isTablet(mContext.getResources().getConfiguration()); + mUserTracker = userTracker; } @Override @@ -361,7 +365,8 @@ public class NavigationBarController implements Callbacks, mSystemActions, mHandler, mNavBarOverlayController, - mUiEventLogger); + mUiEventLogger, + mUserTracker); mNavigationBars.put(displayId, navBar); View navigationBarView = navBar.createView(savedState); 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 000000000000..e5479badcb0a --- /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 5ab7bd88e49b..e4f5cde37f1d 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.IntDef; 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.view.WindowManager; 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 @@ import javax.inject.Inject; * 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 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl @IntDef(prefix = {"STATE_"}, value = { STATE_NOT_SHOWN, STATE_APPEARING, - STATE_SHOWN, + STATE_EXPANDED, + STATE_COLLAPSED, STATE_DISAPPEARING }) public @interface State { @@ -76,46 +84,58 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl 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 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl } @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 class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl 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 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl 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 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl 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 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl // and then onAnimationEnd(...). We, however, only want to proceed here if the // animation ended "naturally". if (!mCancelled) { - onAnimationFinished(); + onIconAnimationFinished(); } } }); @@ -289,19 +403,37 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl 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 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl 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 edfbed04f70d..6660081006cd 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 @@ public class QSContainerImpl extends FrameLayout { // 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 2d0d87dcadee..bcce87a51097 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.R; 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 @@ public class QSDetail extends LinearLayout { private boolean mSwitchState; private QSFooter mFooter; + private NotificationsQuickSettingsContainer mContainer; + public QSDetail(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } @@ -115,6 +118,10 @@ public class QSDetail extends LinearLayout { mClipper = new QSDetailClipper(this); } + public void setContainer(NotificationsQuickSettingsContainer container) { + mContainer = container; + } + /** */ public void setQsPanel(QSPanelController panelController, QuickStatusBarHeader header, QSFooter footer) { @@ -242,6 +249,9 @@ public class QSDetail extends LinearLayout { } 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/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 7b8a6a0a8d0e..c28c649b0306 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 class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca public void setContainer(ViewGroup container) { if (container instanceof NotificationsQuickSettingsContainer) { mQSCustomizerController.setContainer((NotificationsQuickSettingsContainer) container); + mQSDetail.setContainer((NotificationsQuickSettingsContainer) container); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 0bb0a3f7bad4..c70eaffcaeb6 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 @@ public class QSPanel extends LinearLayout implements Tunable { 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/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java index cbdcad5cf385..76076f6c2761 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 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader mView.onAttach(mIconManager, mQSExpansionPathInterpolator); mDemoModeController.addCallback(mDemoModeReceiver); - mHeaderQsPanelController.setContentMargins(0, 0); } @Override @@ -253,6 +252,10 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader 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/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java index 730702ec8685..51cc32ad39c1 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.Bitmap; 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 @@ class ImageTileSet { private static final String TAG = "ImageTileSet"; - private CallbackRegistry<OnBoundsChangedListener, ImageTileSet, Rect> mOnBoundsListeners; private CallbackRegistry<OnContentChangedListener, ImageTileSet, Rect> mContentListeners; @Inject @@ -54,14 +54,6 @@ class ImageTileSet { 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 @@ class ImageTileSet { } 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 @@ class ImageTileSet { 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 @@ class ImageTileSet { } } - 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 @@ class ImageTileSet { 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 @@ class ImageTileSet { } 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/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index 669046591170..3c830cc374ab 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -453,14 +453,6 @@ public class ScreenshotView extends FrameLayout implements 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); @@ -491,6 +483,20 @@ public class ScreenshotView extends FrameLayout implements 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 = @@ -534,13 +540,6 @@ public class ScreenshotView extends FrameLayout implements } }); - toCorner.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - mScreenshotPreview.setVisibility(View.VISIBLE); - } - }); - mScreenshotFlash.setAlpha(0f); mScreenshotFlash.setVisibility(View.VISIBLE); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java index 94e314948779..ce6e46937c25 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 @@ import javax.inject.Inject; 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 @@ public class ScrollCaptureClient { 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 @@ public class ScrollCaptureClient { 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 @@ public class ScrollCaptureClient { 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 @@ public class ScrollCaptureClient { 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 @@ public class ScrollCaptureClient { } @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 bbcfdbd99bef..4c1f6a19b96c 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 @@ public class ScrollCaptureController { 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 @@ public class ScrollCaptureController { 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 @@ public class ScrollCaptureController { + " - " + 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 @@ public class ScrollCaptureController { : 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 06c1c6f8cefa..e6d48676dc03 100644 --- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt @@ -218,7 +218,12 @@ class SensorUseStartedActivity @Inject constructor( } 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 000000000000..9d101effa99f --- /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/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt index ce796d9789cf..89dda9c52651 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt @@ -22,6 +22,7 @@ import android.app.ActivityManager import android.content.res.Resources 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 +38,7 @@ import javax.inject.Inject @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 +99,8 @@ open class BlurUtils @Inject constructor( * @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() } override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index 4a4e990728f3..6f4a73ec4516 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.ExpandHelper 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 @@ class LockscreenShadeTransitionController @Inject constructor( 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 @@ class LockscreenShadeTransitionController @Inject constructor( // 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/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index 89005513d3d0..f03a9a8f3589 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -96,6 +96,15 @@ class NotificationShadeDepthController @Inject constructor( var globalActionsSpring = DepthAnimation() var showingHomeControls: Boolean = false + @VisibleForTesting + 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) { if (field == value) return @@ -186,13 +195,16 @@ class NotificationShadeDepthController @Inject constructor( var blur = max(shadeRadius.toInt(), globalActionsRadius) // Make blur be 0 if it is necessary to stop blur effect. - if (scrimsVisible) { + if (scrimsVisible || !blurUtils.supportsBlursOnWindows()) { blur = 0 } + val zoomOut = blurUtils.ratioOfBlurRadius(blur) + + // Brightness slider removes blur, but doesn't affect zooms + blur = (blur * (1f - brightnessMirrorSpring.ratio)).toInt() val opaque = scrimsVisible && !ignoreShadeBlurUntilHidden blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur, opaque) - val zoomOut = blurUtils.ratioOfBlurRadius(blur) try { if (root.isAttachedToWindow && root.windowToken != null) { wallpaperManager.setWallpaperZoomOut(root.windowToken, zoomOut) @@ -260,6 +272,7 @@ class NotificationShadeDepthController @Inject constructor( shadeSpring.finishIfRunning() shadeAnimation.finishIfRunning() globalActionsSpring.finishIfRunning() + brightnessMirrorSpring.finishIfRunning() } } @@ -425,6 +438,7 @@ class NotificationShadeDepthController @Inject constructor( 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") } 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 f1479a149a41..f19cf5d8d9c7 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 @@ class ExpandAnimationParameters( ) 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/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt index c248670c48db..1bbef2562d21 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 @@ class NotificationLaunchAnimatorController( 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 @@ class NotificationLaunchAnimatorController( 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/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 9d56e9b6f855..93166f39ad62 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.PackageInfo; 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.RemoteInputController; 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 @@ public class ExpandableNotificationRow extends ActivatableNotificationView 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 @@ public class ExpandableNotificationRow extends ActivatableNotificationView 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 class ExpandableNotificationRow extends ActivatableNotificationView 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); } @@ -1105,8 +1109,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView 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; }; } @@ -1821,10 +1824,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView void onGutsClosed() { updateContentAccessibilityImportanceForGuts(true /* isEnabled */); - if (mSnoozedMenuItems != null && mSnoozedMenuItems.size() > 0) { - mMenuRow.setMenuItems(mSnoozedMenuItems); - mSnoozedMenuItems = null; - } + mIsSnoozed = false; } /** @@ -2036,7 +2036,22 @@ public class ExpandableNotificationRow extends ActivatableNotificationView 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) { @@ -2065,13 +2080,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView 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) { @@ -2468,7 +2482,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView 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) { @@ -2981,7 +2996,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView 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(); @@ -2997,7 +3012,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView isExpanded = isExpanded(); } } - if (expandable) { + if (expandable && !mIsSnoozed) { if (isExpanded) { info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE); } else { @@ -3085,6 +3100,26 @@ public class ExpandableNotificationRow extends ActivatableNotificationView 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/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java index 754de580cd61..0f615aa9356f 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 @@ -240,10 +240,10 @@ public class NotificationBackgroundView extends View { 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 26606cda5582..197920f7be19 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,8 +72,6 @@ public class AmbientState { private boolean mUnlockHintRunning; private boolean mQsCustomizerShowing; private int mIntrinsicPadding; - private int mExpandAnimationTopChange; - private ExpandableNotificationRow mExpandingNotification; private float mHideAmount; private boolean mAppearing; private float mPulseHeight = MAX_PULSE_HEIGHT; @@ -518,22 +516,6 @@ public class AmbientState { 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/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java index 2a2e733f78a1..7a5c18896e5f 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 @@ public interface NotificationListContainer extends 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 f0201cb3482d..4e6d376919e9 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 @@ -77,6 +77,7 @@ import com.android.settingslib.Utils; 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; @@ -90,6 +91,7 @@ import com.android.systemui.statusbar.StatusBarState; 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; @@ -111,6 +113,7 @@ import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController 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; @@ -138,6 +141,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable // 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", true /* default */); private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f; private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f; @@ -421,6 +427,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable 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<>(); @@ -467,6 +476,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable 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; @@ -489,6 +504,26 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable 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; @@ -896,7 +931,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable 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); @@ -906,6 +944,21 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable 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() { int newRadius = getResources().getDimensionPixelSize(R.dimen.notification_corner_radius); if (mCornerRadius != newRadius) { @@ -966,6 +1019,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable 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 @@ -2117,7 +2171,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @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; @@ -2624,7 +2678,17 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable */ @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; } @@ -2633,8 +2697,17 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable 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; @@ -2656,7 +2729,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable /** * 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) { @@ -2675,7 +2748,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable ((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 @@ -2726,6 +2799,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable * @return the amount of scrolling needed to start clipping notifications. */ private int getScrollAmountToScrollBoundary() { + if (mShouldUseSplitNotificationShade) { + return mSidePaddings; + } return mTopPadding - mQsScrollBoundaryPosition; } @@ -2869,7 +2945,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @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(); } @@ -2879,10 +2964,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) public void applyExpandAnimationParams(ExpandAnimationParameters params) { - mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange()); - - // Disable clipping for launches + // Modify the clipping for launching notifications + mLaunchAnimationParams = params; setLaunchingNotification(params != null); + updateLaunchedNotificationClipPath(); requestChildrenUpdate(); } @@ -5330,7 +5415,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable return; } mLaunchingNotification = launching; - updateUseRoundedRectClipping(); + 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(); } /** @@ -5340,22 +5433,97 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable // 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 = !mLaunchingNotification && mIsExpanded && qsAllowsClipping; + 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) { + 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. */ @@ -5370,6 +5538,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } /** + * @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 495eda772219..e71f7dbb008b 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 @@ -1533,6 +1533,11 @@ public class NotificationStackScrollLayoutController { } @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/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt index 54ef623e95ab..b148eeba2cf5 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 @@ class ConfigurationControllerImpl(context: Context) : ConfigurationController { 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 @@ class ConfigurationControllerImpl(context: Context) : ConfigurationController { 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 @@ class ConfigurationControllerImpl(context: Context) : ConfigurationController { 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/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 3e4177d32a34..fa5011e8802f 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 @@ public class KeyguardClockPositionAlgorithm { 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 @@ public class KeyguardClockPositionAlgorithm { * 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 @@ public class KeyguardClockPositionAlgorithm { mNotificationStackHeight = notificationStackHeight; mPanelExpansion = panelExpansion; mHeight = parentHeight; - mKeyguardStatusHeight = keyguardStatusHeight; + mKeyguardStatusHeight = keyguardStatusHeight + mStatusViewBottomMargin; mUserSwitchHeight = userSwitchHeight; mUserSwitchPreferredY = userSwitchPreferredY; mHasCustomClock = hasCustomClock; @@ -221,40 +221,15 @@ public class KeyguardClockPositionAlgorithm { 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/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index 6b864c9c35df..41af80e02b5a 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 @@ public class NotificationIconAreaController implements 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 aaddfca4b685..5d31786dd638 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.animation.ValueAnimator; 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.Region; 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 @@ public class NotificationPanelViewController extends PanelViewController { 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 @@ public class NotificationPanelViewController extends PanelViewController { private int mScreenCornerRadius; private boolean mQSAnimatingHiddenFromCollapsed; + private final ContentResolver mContentResolver; + private final Executor mUiExecutor; private final SecureSettings mSecureSettings; @@ -635,6 +643,7 @@ public class NotificationPanelViewController extends PanelViewController { @Inject public NotificationPanelViewController(NotificationPanelView view, @Main Resources resources, + @Main Handler handler, LayoutInflater layoutInflater, NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, DynamicPrivacyController dynamicPrivacyController, @@ -678,6 +687,7 @@ public class NotificationPanelViewController extends PanelViewController { TapAgainViewController tapAgainViewController, NavigationModeController navigationModeController, FragmentService fragmentService, + ContentResolver contentResolver, QuickAccessWalletController quickAccessWalletController, @Main Executor uiExecutor, SecureSettings secureSettings, @@ -704,15 +714,12 @@ public class NotificationPanelViewController extends PanelViewController { 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 @@ public class NotificationPanelViewController extends PanelViewController { } mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count); + updateUserSwitcherFlags(); onFinishInflate(); } @@ -1034,6 +1042,10 @@ public class NotificationPanelViewController extends PanelViewController { 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) { @@ -1061,6 +1073,7 @@ public class NotificationPanelViewController extends PanelViewController { updateResources(); // Re-inflate the keyguard user switcher group. + updateUserSwitcherFlags(); boolean isUserSwitcherEnabled = mUserManager.isUserSwitcherEnabled(); boolean showQsUserSwitch = mKeyguardQsUserSwitchEnabled && isUserSwitcherEnabled; boolean showKeyguardUserSwitcher = @@ -1260,7 +1273,7 @@ public class NotificationPanelViewController extends PanelViewController { mNotificationStackScrollLayoutController.getIntrinsicContentHeight(), expandedFraction, totalHeight, - mKeyguardStatusViewController.getHeight(), + mKeyguardStatusViewController.getLockscreenHeight(), userIconHeight, userSwitcherPreferredY, hasCustomClock(), hasVisibleNotifications, darkamount, mOverStretchAmount, @@ -1443,7 +1456,6 @@ public class NotificationPanelViewController extends PanelViewController { private void setQsExpansionEnabled() { mQsExpansionEnabled = mQsExpansionEnabledPolicy && mQsExpansionEnabledAmbient; - Log.d(TAG, "Set qsExpansionEnabled: " + mQsExpansionEnabled); if (mQs == null) return; mQs.setHeaderClickable(mQsExpansionEnabled); } @@ -2669,14 +2681,6 @@ public class NotificationPanelViewController extends PanelViewController { @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) { @@ -2701,16 +2705,6 @@ public class NotificationPanelViewController extends PanelViewController { 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; } @@ -2782,11 +2776,8 @@ public class NotificationPanelViewController extends PanelViewController { 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; @@ -3336,7 +3327,7 @@ public class NotificationPanelViewController extends PanelViewController { } if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) { // The expandedHeight is always the full panel Height when bypassing - expandedHeight = getMaxPanelHeightNonBypass(); + expandedHeight = getMaxPanelHeight(); } mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight); updateKeyguardBottomAreaAlpha(); @@ -3895,6 +3886,26 @@ public class NotificationPanelViewController extends PanelViewController { 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) { @@ -4216,6 +4227,15 @@ public class NotificationPanelViewController extends PanelViewController { } @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(); @@ -4228,6 +4248,21 @@ public class NotificationPanelViewController extends PanelViewController { } } + 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) { @@ -4343,10 +4378,12 @@ public class NotificationPanelViewController extends PanelViewController { 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/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java index 1cb0be0efc90..ed8fb31aea32 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 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout 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 class NotificationsQuickSettingsContainer extends ConstraintLayout } 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 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout 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 7a1e5cf1770b..cfcea9684c3b 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 class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump */ 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 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump 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 e52e1fa5f39f..06811932ac0c 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 @@ public enum ScrimState { } @Override - public float getBehindAlpha() { + public float getMaxLightRevealScrimAlpha() { return mWallpaperSupportsAmbientMode && !mHasBackdrop ? 0f : 1f; } @@ -220,18 +220,11 @@ public enum ScrimState { 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 @@ public enum ScrimState { 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 5d2fe523c803..3f07785520cf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1473,9 +1473,7 @@ public class StatusBar extends SystemUI implements DemoMode, * @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; @@ -4444,6 +4442,8 @@ public class StatusBar extends SystemUI implements DemoMode, } else { mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback); } + updateLightRevealScrimVisibility(); + Trace.endSection(); } @@ -4894,6 +4894,7 @@ public class StatusBar extends SystemUI implements DemoMode, 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/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt index 52bf2d577776..9a04d39c4e9e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt @@ -172,7 +172,8 @@ class UnlockedScreenOffAnimationController @Inject constructor( // 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 (statusBar.notificationPanelViewController.isFullyExpanded) { + if (!this::statusBar.isInitialized || + statusBar.notificationPanelViewController.isFullyExpanded) { return false } 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 399c8500ab48..a0edc7c494bc 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 @@ public class BrightnessMirrorController 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 0a6cf7be736f..c2bd87c6276f 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 @@ public interface ConfigurationController extends CallbackController<Configuratio 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/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index ce080752b5cc..2ac5c1eeae8e 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 @@ public class MobileSignalController extends SignalController<MobileState, Mobile 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 @@ public class MobileSignalController extends SignalController<MobileState, Mobile 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/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java index 753def0b0f1c..2406db3ee58f 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 @@ public class WifiSignalController extends 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 @@ public class WifiSignalController extends 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 ca1f55e95ff4..11ddbd045cd4 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 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { @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 class ThemeOverlayController extends SystemUI implements Dumpable { 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 c5e35a497956..8b394bfe35b7 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 @@ public class SystemUIToast implements ToastPlugin.Toast { 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 @@ public class SystemUIToast implements ToastPlugin.Toast { } 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 @@ public class SystemUIToast implements ToastPlugin.Toast { 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 @@ public class SystemUIToast implements ToastPlugin.Toast { + "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 @@ public class SystemUIToast implements ToastPlugin.Toast { 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/tv/TvBottomSheetActivity.java b/packages/SystemUI/src/com/android/systemui/tv/TvBottomSheetActivity.java new file mode 100644 index 000000000000..2b7a33260248 --- /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/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java index 5b50e897e254..62370313e5ae 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.ArgumentMatchers.any; 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.SysuiTestCase; 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 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { @Rule public MockitoRule mockito = MockitoJUnit.rule(); + private Context mContextWrapper; private KeyguardUpdateMonitor mKeyguardUpdateMonitor; private AccessibilityFloatingMenuController mController; private AccessibilityButtonTargetsObserver mTargetsObserver; @@ -66,6 +70,16 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { 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 class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { 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 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { 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 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { 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 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { @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 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { @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 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { @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 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { @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 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { @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 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { @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 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { @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 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { @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 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase { 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/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 41f987727860..0c750a179358 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -53,6 +53,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator; 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.FakeExecutor; @@ -117,6 +118,8 @@ public class UdfpsControllerTest extends SysuiTestCase { @Mock private AccessibilityManager mAccessibilityManager; @Mock + private LockscreenShadeTransitionController mLockscreenShadeTransitionController; + @Mock private ScreenLifecycle mScreenLifecycle; @Mock private Vibrator mVibrator; @@ -176,6 +179,7 @@ public class UdfpsControllerTest extends SysuiTestCase { mFalsingManager, mPowerManager, mAccessibilityManager, + mLockscreenShadeTransitionController, mScreenLifecycle, mVibrator, Optional.of(mHbmProvider)); 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 5923de6719a8..f62587c6e87c 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.SysuiTestCase; 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 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @Mock + private LockscreenShadeTransitionController mLockscreenShadeTransitionController; + @Mock private DumpManager mDumpManager; @Mock private DelayableExecutor mExecutor; @@ -106,6 +109,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { 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 a11b9cf357a8..10997fab081f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -209,33 +209,14 @@ public class DozeTriggersTest extends SysuiTestCase { // 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(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(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); 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 6e216428992f..2c686618a361 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 @@ public class MediaOutputAdapterTest extends SysuiTestCase { 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 @@ public class MediaOutputAdapterTest extends SysuiTestCase { 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 @@ public class MediaOutputAdapterTest extends SysuiTestCase { 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 @@ public class MediaOutputAdapterTest extends SysuiTestCase { 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 @@ public class MediaOutputAdapterTest extends SysuiTestCase { 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 589ae2e7f4f4..9bd07b88417d 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 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase { @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 da63b8a3ca4b..4980f7406cee 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.model.SysUiState; 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 @@ public class NavigationBarControllerTest extends SysuiTestCase { 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 4ec45b444c46..b1afeecf39f1 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.navigationbar.gestural.EdgeBackGestureHandler; 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 @@ public class NavigationBarTest extends SysuiTestCase { 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/screenshot/FakeSession.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSession.java index 9c68f0d8a7ff..478658eb232d 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 @@ class FakeSession implements ScrollCaptureClient.Session { 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 @@ class FakeSession implements ScrollCaptureClient.Session { } @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 2520af94380c..4c8a4b0f8f61 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 @@ import org.junit.runner.RunWith; @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 @@ public class FakeSessionTest extends SysuiTestCase { /* 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 @@ public class FakeSessionTest extends SysuiTestCase { /* 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 @@ public class FakeSessionTest extends SysuiTestCase { /* 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 @@ public class FakeSessionTest extends SysuiTestCase { /* 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 @@ public class FakeSessionTest extends SysuiTestCase { /* 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 @@ public class FakeSessionTest extends SysuiTestCase { /* 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 @@ public class FakeSessionTest extends SysuiTestCase { /* 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 @@ public class FakeSessionTest extends SysuiTestCase { /* 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 @@ public class FakeSessionTest extends SysuiTestCase { /* 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 @@ public class FakeSessionTest extends SysuiTestCase { /* 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 @@ public class FakeSessionTest extends SysuiTestCase { /* 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 @@ public class FakeSessionTest extends SysuiTestCase { /* 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 @@ public class FakeSessionTest extends SysuiTestCase { /* 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 5bab1bcddb6a..10c878a92745 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 @@ public class ScrollCaptureControllerTest extends SysuiTestCase { // 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 - - assertEquals("top", -40, screenshot.getTop()); - assertEquals("bottom", 205, screenshot.getBottom()); + // -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", -85, screenshot.getTop()); + assertEquals("bottom", 210, screenshot.getBottom()); } @Test @@ -119,13 +121,14 @@ public class ScrollCaptureControllerTest extends SysuiTestCase { // 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 @@ public class ScrollCaptureControllerTest extends SysuiTestCase { 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 7c7d2dc16c03..2b44d8bbd364 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 @@ class BlurUtilsTest : SysuiTestCase() { @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 @@ class BlurUtilsTest : SysuiTestCase() { 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 61c3835a88d2..60b38892e776 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.anyString 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 @@ -71,6 +72,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { @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 +92,11 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { `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,6 +105,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { notificationShadeWindowController, dozeParameters, dumpManager) notificationShadeDepthController.shadeSpring = shadeSpring notificationShadeDepthController.shadeAnimation = shadeAnimation + notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring notificationShadeDepthController.globalActionsSpring = globalActionsSpring notificationShadeDepthController.root = root @@ -191,6 +198,14 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { } @Test + fun setFullShadeTransition_appliesBlur_onlyIfSupported() { + reset(blurUtils) + notificationShadeDepthController.transitionToFullShadeProgress = 1f + notificationShadeDepthController.updateBlurCallback.doFrame(0) + verify(blurUtils).applyBlur(any(), eq(0), eq(false)) + } + + @Test fun updateGlobalDialogVisibility_animatesBlur() { notificationShadeDepthController.updateGlobalDialogVisibility(0.5f, root) verify(globalActionsSpring).animateTo(eq(maxBlur / 2), eq(root)) @@ -267,6 +282,32 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { } @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) 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 2d51683c8ae5..2693b949f6ff 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.android.systemui.statusbar.notification.ViewGroupFadeHelper.re 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 static org.mockito.Mockito.when; 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.MotionEvent; 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 androidx.test.filters.SmallTest; 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; @@ -140,6 +149,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { private KeyguardBottomAreaView mKeyguardBottomArea; @Mock private KeyguardBottomAreaView mQsFrame; + private KeyguardStatusView mKeyguardStatusView; @Mock private ViewGroup mBigClockContainer; @Mock @@ -153,6 +163,10 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Mock private KeyguardStatusBarView mKeyguardStatusBar; @Mock + private View mUserSwitcherView; + @Mock + private ViewStub mUserSwitcherStubView; + @Mock private HeadsUpTouchHelper.Callback mHeadsUpCallback; @Mock private PanelBar mPanelBar; @@ -204,7 +218,6 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Mock private KeyguardClockSwitch mKeyguardClockSwitch; private PanelViewController.TouchHandler mTouchHandler; - @Mock private ConfigurationController mConfigurationController; @Mock private MediaHierarchyManager mMediaHiearchyManager; @@ -265,6 +278,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Mock private SecureSettings mSecureSettings; @Mock + private ContentResolver mContentResolver; + @Mock private TapAgainViewController mTapAgainViewController; @Mock private KeyguardIndicationController mKeyguardIndicationController; @@ -287,6 +302,9 @@ public class NotificationPanelViewTest extends SysuiTestCase { 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); @@ -301,6 +319,9 @@ public class NotificationPanelViewTest extends SysuiTestCase { 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); @@ -320,7 +341,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { 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); @@ -348,6 +369,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { mFalsingManager, mLockscreenShadeTransitionController, new FalsingCollectorFake()); + mConfigurationController = new ConfigurationControllerImpl(mContext); when(mKeyguardStatusViewComponentFactory.build(any())) .thenReturn(mKeyguardStatusViewComponent); when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController()) @@ -358,10 +380,16 @@ public class NotificationPanelViewTest extends SysuiTestCase { .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(), @@ -395,6 +423,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { mTapAgainViewController, mNavigationModeController, mFragmentService, + mContentResolver, mQuickAccessWalletController, new FakeExecutor(new FakeSystemClock()), mSecureSettings, @@ -549,6 +578,38 @@ public class NotificationPanelViewTest extends SysuiTestCase { } @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(); @@ -682,6 +743,12 @@ public class NotificationPanelViewTest extends SysuiTestCase { 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); @@ -704,6 +771,21 @@ public class NotificationPanelViewTest extends SysuiTestCase { 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/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index e55361e750b1..678b193073c2 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 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimBehind, true, mScrimForBubble, false )); + + assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); } @Test @@ -321,8 +323,9 @@ public class ScrimControllerTest extends SysuiTestCase { 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 @@ public class ScrimControllerTest extends SysuiTestCase { 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 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, mScrimBehind, TRANSPARENT)); + assertEquals(0f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); } @Test @@ -359,7 +364,8 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, - mScrimBehind, OPAQUE)); + mScrimBehind, TRANSPARENT)); + assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); assertScrimTinted(Map.of( mScrimInFront, true, @@ -378,7 +384,8 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, - mScrimBehind, OPAQUE)); + mScrimBehind, TRANSPARENT)); + assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f); assertScrimTinted(Map.of( mScrimInFront, true, @@ -403,13 +410,15 @@ public class ScrimControllerTest extends SysuiTestCase { 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 @@ public class ScrimControllerTest extends SysuiTestCase { 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 @@ public class ScrimControllerTest extends SysuiTestCase { 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 @@ public class ScrimControllerTest extends SysuiTestCase { 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 @@ public class ScrimControllerTest extends SysuiTestCase { // 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 @@ public class ScrimControllerTest extends SysuiTestCase { // 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/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java index 521b958ab891..bd9d1a7fd657 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 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { 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 208790b24d8a..1a24c113a0df 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 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { } @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/services/CameraExtensionsProxy/AndroidManifest.xml b/packages/services/CameraExtensionsProxy/AndroidManifest.xml index e5f460e593b3..d35689413749 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 index 7b9ca2ddc345..69874f8a305a 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 241a0dbac758..f63198866b08 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub "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); + serviceInfos = new ArrayList<>( + getUserStateLocked(resolvedUserId).mInstalledServices); + } - if (Binder.getCallingPid() == OWN_PROCESS_ID) { - return 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 getUserStateLocked(resolvedUserId).mInstalledServices; } + return serviceInfos; } @Override diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index acbf4875ae8d..5aec6aa99c12 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 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku // 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 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } @Override - public void restoreStarting(int userId) { - mBackupRestoreController.restoreStarting(userId); + public void systemRestoreStarting(int userId) { + mBackupRestoreController.systemRestoreStarting(userId); } @Override @@ -2481,8 +2483,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } @Override - public void restoreFinished(int userId) { - mBackupRestoreController.restoreFinished(userId); + public void systemRestoreFinished(int userId) { + mBackupRestoreController.systemRestoreFinished(userId); } @SuppressWarnings("deprecation") @@ -4272,6 +4274,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku 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 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku 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 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } - // 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); + } + } - final UserHandle userHandle = new UserHandle(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) { - // 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 (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); + // 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 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } 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 312cde6c5152..de5f47dd5dbb 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 @@ public final class AutofillManagerService 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 a80efc461176..5f2d4e82883c 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.R; 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 @@ final class AutofillManagerServiceImpl } @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 68e6290c987a..1eaa59a0871a 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 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { 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 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { 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 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand { 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 b0755ac836e0..94872b09cd36 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 android.util.Slog; 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 @@ final class RemoteFillService extends ServiceConnector.Impl<IAutoFillService> { })); } + void onSavedPasswordCountRequest(IResultReceiver receiver) { + run(service -> service.onSavedPasswordCountRequest(receiver)); + } + public void destroy() { unbind(); } 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 261ebe69a15e..f07bac8cd762 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 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // 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 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { 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/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 92996241a3a6..f7ddd2922b25 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -1151,6 +1151,8 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP /** * 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 26ecee8f21ab..70176a0fefeb 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.VpnManager; 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 @@ public class VpnManagerService extends IVpnManager.Stub { /** * 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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2de091bb34bf..0c9772477734 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 966e746b3c73..317b7757c239 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 @@ public final class CachedAppOptimizer { 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 @@ public final class CachedAppOptimizer { updateUseFreezer(); updateMinOomAdjThrottle(); updateMaxOomAdjThrottle(); - updateFreezerDebounceTimeout(); } } @@ -656,6 +653,7 @@ public final class CachedAppOptimizer { || 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 @@ public final class CachedAppOptimizer { @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/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index 697f6fa8eea8..ae1cd51e11f0 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_CHANGED; 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 @@ public final class GameManagerService extends IGameManagerService.Stub { } } + // 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 @@ public final class GameManagerService extends IGameManagerService.Stub { * 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 @@ public final class GameManagerService extends IGameManagerService.Stub { 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 2074ffae1030..699f9e2d99f8 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 android.util.ArraySet; 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 class GameManagerShellCommand extends ShellCommand { 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 @@ public class GameManagerShellCommand extends ShellCommand { 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 @@ public class GameManagerShellCommand extends ShellCommand { 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 @@ public class GameManagerShellCommand extends ShellCommand { 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 4cb2e9b5cb97..7f1402d83a0c 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 @@ public final class AppHibernationService extends SystemService { 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 @@ public final class AppHibernationService extends SystemService { 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 @@ public final class AppHibernationService extends SystemService { } 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 @@ public final class AppHibernationService extends SystemService { 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 79e995b038fa..018d6023f083 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 @@ final class GlobalLevelHibernationProto implements ProtoReadWriter<List<GlobalLe 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 @@ final class GlobalLevelHibernationProto implements ProtoReadWriter<List<GlobalLe 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 f4433a7fa5f6..104ecbc17d8a 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 @@ final class GlobalLevelState { 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 @@ final class GlobalLevelState { 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 99a33e4462e2..5ba75d3ac312 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 @@ package com.android.server.appop; 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.SystemClock; 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.List; 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 { @@ -2357,10 +2360,21 @@ public class AppOpsService extends IAppOpsService.Stub { 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 @@ -2377,10 +2391,21 @@ public class AppOpsService extends IAppOpsService.Stub { 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 @@ -3838,7 +3863,8 @@ public class AppOpsService extends IAppOpsService.Stub { 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); @@ -5174,8 +5200,6 @@ public class AppOpsService extends IAppOpsService.Stub { } static class Shell extends ShellCommand { - static final AtomicInteger sAttributionChainIds = new AtomicInteger(0); - final IAppOpsService mInterface; final AppOpsService mInternal; @@ -5645,8 +5669,7 @@ public class AppOpsService extends IAppOpsService.Stub { 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; } diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteRegistry.java index 49469cc8a597..0439660f0c97 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; @@ -58,7 +61,9 @@ import com.android.internal.util.XmlUtils; 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; @@ -69,6 +74,8 @@ import java.util.Arrays; 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. @@ -138,6 +145,7 @@ final class DiscreteRegistry { 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"; @@ -182,6 +190,16 @@ final class DiscreteRegistry { 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() { @@ -190,15 +208,6 @@ final class DiscreteRegistry { 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) { @@ -251,7 +260,7 @@ final class DiscreteRegistry { DiscreteOps discreteOps; synchronized (mInMemoryLock) { discreteOps = mDiscreteOps; - mDiscreteOps = new DiscreteOps(); + mDiscreteOps = new DiscreteOps(discreteOps.mChainIdOffset); mCachedOps = null; } deleteOldDiscreteHistoryFilesLocked(); @@ -265,16 +274,109 @@ final class DiscreteRegistry { 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, @@ -301,7 +403,7 @@ final class DiscreteRegistry { void clearHistory() { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { - mDiscreteOps = new DiscreteOps(); + mDiscreteOps = new DiscreteOps(0); } clearOnDiskHistoryLocked(); } @@ -340,7 +442,11 @@ final class DiscreteRegistry { 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); } @@ -351,14 +457,14 @@ final class DiscreteRegistry { } 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); @@ -366,11 +472,143 @@ final class DiscreteRegistry { } } + /** + * 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() { @@ -378,6 +616,7 @@ final class DiscreteRegistry { } 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); @@ -390,14 +629,27 @@ final class DiscreteRegistry { @Nullable String attributionTag, @AppOpsManager.OpFlags int flags, @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, attributionFlags, attributionChainId); + 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)); @@ -406,7 +658,8 @@ final class DiscreteRegistry { 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); } @@ -429,10 +682,11 @@ final class DiscreteRegistry { } } - 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); } } @@ -442,6 +696,7 @@ final class DiscreteRegistry { 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++) { @@ -476,8 +731,13 @@ final class DiscreteRegistry { } private void readFromFile(File f, long beginTimeMillis) { + FileInputStream stream; + try { + stream = new FileInputStream(f); + } catch (FileNotFoundException e) { + return; + } try { - FileInputStream stream = new FileInputStream(f); TypedXmlPullParser parser = Xml.resolvePullParser(stream); XmlUtils.beginDocument(parser, TAG_HISTORY); @@ -487,7 +747,6 @@ final class DiscreteRegistry { 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())) { @@ -498,8 +757,12 @@ final class DiscreteRegistry { } 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) { + } } - } } @@ -590,7 +853,8 @@ final class DiscreteRegistry { 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)); @@ -599,7 +863,8 @@ final class DiscreteRegistry { 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); } @@ -634,10 +899,12 @@ final class DiscreteRegistry { 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); } } @@ -705,7 +972,8 @@ final class DiscreteRegistry { 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); @@ -715,7 +983,8 @@ final class DiscreteRegistry { 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); } @@ -739,11 +1008,12 @@ final class DiscreteRegistry { } 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); } } @@ -802,7 +1072,9 @@ final class DiscreteRegistry { 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, @@ -814,7 +1086,9 @@ final class DiscreteRegistry { 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); @@ -876,7 +1150,8 @@ final class DiscreteRegistry { } 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); @@ -884,9 +1159,21 @@ final class DiscreteRegistry { 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, discretizeTimeStamp(event.mNoteTime), - discretizeDuration(event.mNoteDuration)); + discretizeDuration(event.mNoteDuration), proxy); } } } @@ -981,6 +1268,13 @@ final class DiscreteRegistry { 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, @NonNull Date date, @NonNull String prefix) { pw.print(prefix); @@ -1063,11 +1357,20 @@ final class DiscreteRegistry { } 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) { diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java index dd5df503d936..2c68aaf4f3e5 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 @@ final class HistoricalRegistry { mMode = other.mMode; mBaseSnapshotInterval = other.mBaseSnapshotInterval; mIntervalCompressionMultiplier = other.mIntervalCompressionMultiplier; + mDiscreteRegistry = other.mDiscreteRegistry; } void systemReady(@NonNull ContentResolver resolver) { @@ -369,7 +370,7 @@ final class HistoricalRegistry { @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 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { 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 @@ final class HistoricalRegistry { 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) { 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 a5bb0f430609..0981f184e143 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 @@ -866,7 +866,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { 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/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java index 70e20339ce98..5343d0d17273 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 @@ public class FaceUpdateActiveUserClient extends HalClientMonitor<IBiometricsFace 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 @@ public class FaceUpdateActiveUserClient extends HalClientMonitor<IBiometricsFace } 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 f1f94564c35d..0ae2e381cf14 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 @@ -80,8 +80,7 @@ final class AidlConversionUtils { // 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/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java index 160736433112..8d777e1c2787 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 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint> 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/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 184ddae9699d..88f88a8439cc 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -19,6 +19,8 @@ package com.android.server.display; 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; @@ -86,6 +88,9 @@ public class DisplayDeviceConfig { // The details of the proximity sensor associated with this display. 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 private float[] mRawNits; @@ -306,6 +311,10 @@ public class DisplayDeviceConfig { return hbmData; } + public List<RefreshRateLimitation> getRefreshRateLimitations() { + return mRefreshRateLimitations; + } + @Override public String toString() { String str = "DisplayDeviceConfig{" @@ -329,6 +338,7 @@ public class DisplayDeviceConfig { + ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease + ", mAmbientLightSensor=" + mAmbientLightSensor + ", mProximitySensor=" + mProximitySensor + + ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray()) + "}"; return str; } @@ -647,6 +657,13 @@ public class DisplayDeviceConfig { 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)); + } } } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index bff39a932105..182a038d10f7 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -63,6 +63,7 @@ import android.hardware.display.DisplayManagerGlobal; 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; @@ -130,6 +131,7 @@ import java.io.FileDescriptor; 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; @@ -2111,6 +2113,11 @@ public final class DisplayManagerService extends SystemService { 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*/); @@ -3295,6 +3302,19 @@ public final class DisplayManagerService extends SystemService { } 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 2364a3c7c2ed..83fc9665f192 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,8 +28,10 @@ import android.hardware.Sensor; 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; @@ -102,6 +106,7 @@ public class DisplayModeDirector { private final DisplayObserver mDisplayObserver; private final UdfpsObserver mUdfpsObserver; private final SensorObserver mSensorObserver; + private final HbmObserver mHbmObserver; private final DeviceConfigInterface mDeviceConfig; private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings; @@ -127,7 +132,7 @@ public class DisplayModeDirector { 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, @@ -143,11 +148,13 @@ public class DisplayModeDirector { mDisplayObserver = new DisplayObserver(context, handler); mBrightnessObserver = new BrightnessObserver(context, handler); mUdfpsObserver = new UdfpsObserver(); - mSensorObserver = new SensorObserver(context, (displayId, priority, vote) -> { + 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; @@ -165,6 +172,7 @@ public class DisplayModeDirector { 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. @@ -596,6 +604,7 @@ public class DisplayModeDirector { mBrightnessObserver.dumpLocked(pw); mUdfpsObserver.dumpLocked(pw); mSensorObserver.dumpLocked(pw); + mHbmObserver.dumpLocked(pw); } } @@ -938,13 +947,16 @@ public class DisplayModeDirector { // 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 = 9; + 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 = 10; + 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. @@ -1021,29 +1033,30 @@ public class DisplayModeDirector { 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_PROXIMITY: - return "PRIORITY_PROXIMITY"; - + 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); } @@ -2155,6 +2168,75 @@ public class DisplayModeDirector { } } + /** + * 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() { } @@ -2309,10 +2391,21 @@ public class DisplayModeDirector { 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 @@ -2339,6 +2432,28 @@ public class DisplayModeDirector { 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 { diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index aeb1893c78b6..e6210b2a8cc9 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.PendingIntent; 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; @@ -91,6 +92,7 @@ import android.util.IndentingPrintWriter; 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; @@ -134,6 +136,8 @@ import com.android.server.location.provider.PassiveLocationProvider; 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; @@ -270,6 +274,8 @@ public class LocationManagerService extends ILocationManager.Stub implements mGeofenceManager = new GeofenceManager(mContext, injector); + mInjector.getLocationSettings().registerLocationUserSettingsListener( + this::onLocationUserSettingsChanged); mInjector.getSettingsHelper().addOnLocationEnabledChangedListener( this::onLocationModeChanged); mInjector.getSettingsHelper().addIgnoreSettingsAllowlistChangedListener( @@ -476,6 +482,25 @@ public class LocationManagerService extends ILocationManager.Stub implements } } + 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(); @@ -661,7 +686,7 @@ public class LocationManagerService extends ILocationManager.Stub implements // 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, @@ -687,7 +712,7 @@ public class LocationManagerService extends ILocationManager.Stub implements new IllegalArgumentException()); } - request = validateLocationRequest(request, identity); + request = validateLocationRequest(provider, request, identity); LocationProviderManager manager = getLocationProviderManager(provider); Preconditions.checkArgument(manager != null, @@ -725,7 +750,7 @@ public class LocationManagerService extends ILocationManager.Stub implements } } - request = validateLocationRequest(request, identity); + request = validateLocationRequest(provider, request, identity); LocationProviderManager manager = getLocationProviderManager(provider); Preconditions.checkArgument(manager != null, @@ -734,33 +759,27 @@ public class LocationManagerService extends ILocationManager.Stub implements 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); @@ -786,7 +805,52 @@ public class LocationManagerService extends ILocationManager.Stub implements } 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 @@ -834,7 +898,7 @@ public class LocationManagerService extends ILocationManager.Stub implements // 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) { @@ -844,16 +908,58 @@ public class LocationManagerService extends ILocationManager.Stub implements 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; @@ -1126,6 +1232,24 @@ public class LocationManagerService extends ILocationManager.Stub implements } @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); } @@ -1555,11 +1679,12 @@ public class LocationManagerService extends ILocationManager.Stub implements } } - 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; @@ -1584,6 +1709,7 @@ public class LocationManagerService extends ILocationManager.Stub implements mContext = context; mUserInfoHelper = userInfoHelper; + mLocationSettings = new LocationSettings(context); mAlarmHelper = new SystemAlarmHelper(context); mAppOpsHelper = new SystemAppOpsHelper(context); mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context, @@ -1621,6 +1747,11 @@ public class LocationManagerService extends ILocationManager.Stub implements } @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 937849309f96..b65338d9691d 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 @@ class LocationShellCommand extends BasicShellCommandHandler { 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 @@ class LocationShellCommand extends BasicShellCommandHandler { 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 @@ class LocationShellCommand extends BasicShellCommandHandler { 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 @@ class LocationShellCommand extends BasicShellCommandHandler { 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 e6d25ece93ef..db2a43f7a00d 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 @@ public class LocationEventLog extends LocalEventLog { 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 @@ public class LocationEventLog extends LocalEventLog { 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 @@ public class LocationEventLog extends LocalEventLog { 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 @@ public class LocationEventLog extends LocalEventLog { } } + 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 1cccf08b0367..f3dcfbbf2c0a 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 @@ public class GnssLocationProvider extends AbstractLocationProvider implements 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 b0351184ffe5..173fd13c11a1 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 @@ public interface Injector { /** 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 c315da476d99..3e8da7d7478a 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 @@ public class SystemSettingsHelper extends SettingsHelper { } } - - 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/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java index 8d335b83d99c..43886f7cb87b 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; 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 @@ public class LocationProviderManager extends } @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 @@ public class LocationProviderManager extends 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 @@ public class LocationProviderManager extends 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 @@ public class LocationProviderManager extends Location lastLocation = getLastLocationUnsafe( getIdentity().getUserId(), getPermissionLevel(), - getRequest().isLocationSettingsIgnored(), + getRequest().isBypass(), maxLocationAgeMs); if (lastLocation != null) { executeOperation(acceptLocationChange(LocationResult.wrap(lastLocation))); @@ -1114,7 +1144,7 @@ public class LocationProviderManager extends 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 @@ public class LocationProviderManager extends 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 @@ public class LocationProviderManager extends 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 @@ public class LocationProviderManager extends mLocationManagerInternal = Objects.requireNonNull( LocalServices.getService(LocationManagerInternal.class)); + mLocationSettings = injector.getLocationSettings(); mSettingsHelper = injector.getSettingsHelper(); mUserHelper = injector.getUserInfoHelper(); mAlarmHelper = injector.getAlarmHelper(); @@ -1362,6 +1396,7 @@ public class LocationProviderManager extends mStateChangedListener = listener; mUserHelper.addListener(mUserChangedListener); + mLocationSettings.registerLocationUserSettingsListener(mLocationUserSettingsListener); mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener); final long identity = Binder.clearCallingIdentity(); @@ -1389,6 +1424,7 @@ public class LocationProviderManager extends } mUserHelper.removeListener(mUserChangedListener); + mLocationSettings.unregisterLocationUserSettingsListener(mLocationUserSettingsListener); mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener); // if external entities are registering listeners it's their responsibility to @@ -1550,7 +1586,7 @@ public class LocationProviderManager extends public @Nullable Location getLastLocation(LastLocationRequest request, CallerIdentity identity, @PermissionLevel int permissionLevel) { - if (!isActive(request.isLocationSettingsIgnored(), identity)) { + if (!isActive(request.isBypass(), identity)) { return null; } @@ -1564,7 +1600,7 @@ public class LocationProviderManager extends getLastLocationUnsafe( identity.getUserId(), permissionLevel, - request.isLocationSettingsIgnored(), + request.isBypass(), Long.MAX_VALUE), permissionLevel); @@ -1584,7 +1620,7 @@ public class LocationProviderManager extends * 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 @@ -1592,7 +1628,7 @@ public class LocationProviderManager extends 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; @@ -1601,7 +1637,7 @@ public class LocationProviderManager extends return lastLocation; } else if (userId == UserHandle.USER_CURRENT) { return getLastLocationUnsafe(mUserHelper.getCurrentUserId(), permissionLevel, - ignoreLocationSettings, maximumAgeMs); + isBypass, maximumAgeMs); } Preconditions.checkArgument(userId >= 0); @@ -1613,7 +1649,7 @@ public class LocationProviderManager extends if (lastLocation == null) { location = null; } else { - location = lastLocation.get(permissionLevel, ignoreLocationSettings); + location = lastLocation.get(permissionLevel, isBypass); } } @@ -1925,7 +1961,7 @@ public class LocationProviderManager extends // 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 @@ -2002,12 +2038,12 @@ public class LocationProviderManager extends 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()) { @@ -2036,15 +2072,15 @@ public class LocationProviderManager extends 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; } @@ -2071,6 +2107,7 @@ public class LocationProviderManager extends 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; @@ -2086,6 +2123,7 @@ public class LocationProviderManager extends intervalMs = min(request.getIntervalMillis(), intervalMs); quality = min(request.getQuality(), quality); maxUpdateDelayMs = min(request.getMaxUpdateDelayMillis(), maxUpdateDelayMs); + adasGnssBypass |= request.isAdasGnssBypass(); locationSettingsIgnored |= request.isLocationSettingsIgnored(); lowPower &= request.isLowPower(); } @@ -2123,6 +2161,7 @@ public class LocationProviderManager extends .setIntervalMillis(intervalMs) .setQuality(quality) .setMaxUpdateDelayMillis(maxUpdateDelayMs) + .setAdasGnssBypass(adasGnssBypass) .setLocationSettingsIgnored(locationSettingsIgnored) .setLowPower(lowPower) .setWorkSource(workSource) @@ -2191,6 +2230,16 @@ public class LocationProviderManager extends } } + 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) { @@ -2560,16 +2609,16 @@ public class LocationProviderManager extends } 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/settings/LocationSettings.java b/services/core/java/com/android/server/location/settings/LocationSettings.java new file mode 100644 index 000000000000..d52153893970 --- /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 000000000000..283255ef4b22 --- /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 000000000000..01338a3129da --- /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/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 21f68aecd25b..d791bd69236c 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * Perform upgrade step of moving any user-defined meterness overrides over * into {@link WifiConfiguration}. */ - @GuardedBy({"mNetworkPoliciesSecondLock", "mUidRulesFirstLock"}) - private void upgradeWifiMeteredOverrideAL() { - boolean modified = false; + 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++; + } + } + } + + if (wifiNetworkIds.isEmpty()) { + return; + } 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); - } - } - } else { - i++; + 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); } } - if (modified) { - writePolicyAL(); + + synchronized (mUidRulesFirstLock) { + synchronized (mNetworkPoliciesSecondLock) { + writePolicyAL(); + } } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index a3f3a3a503c8..d78fbdb53d8d 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.INotificationManager; 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 @@ public class NotificationManagerService extends SystemService { // 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 @@ public class NotificationManagerService extends SystemService { } @VisibleForTesting + void setKeyguardManager(KeyguardManager keyguardManager) { + mKeyguardManager = keyguardManager; + } + + @VisibleForTesting ShortcutHelper getShortcutHelper() { return mShortcutHelper; } @@ -2653,6 +2661,7 @@ public class NotificationManagerService extends SystemService { 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 @@ public class NotificationManagerService extends SystemService { 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 @@ -7388,7 +7400,6 @@ public class NotificationManagerService extends SystemService { 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? @@ -7410,7 +7421,7 @@ public class NotificationManagerService extends SystemService { if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN && !suppressedByDnd) { - sendAccessibilityEvent(notification, record.getSbn().getPackageName()); + sendAccessibilityEvent(record); sentAccessibilityEvent = true; } @@ -7433,7 +7444,7 @@ public class NotificationManagerService extends SystemService { 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!"); @@ -8261,17 +8272,30 @@ public class NotificationManagerService extends SystemService { 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 03676b5556d9..96bde3df1e68 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 @@ public class PreferencesHelper implements RankingConfig { } @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 @@ public class PreferencesHelper implements RankingConfig { 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 b1d654671c2d..398259333e16 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 @@ public interface RankingConfig { 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 a98f113a9d57..b144ff27c993 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -417,7 +417,14 @@ public class ZenModeHelper { 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); } } diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 5fd8e3c6e302..44f7d8869322 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -177,8 +177,10 @@ public class PackageDexOptimizer { 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), diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 60a757118222..d2ed08f66944 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } 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 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements 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 4b0eb6546888..acc83cfd05b6 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.FrameworkStatsLog; 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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { .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 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { 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 a70a1132360a..2419873cdc41 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -12798,21 +12798,6 @@ public class PackageManagerService extends IPackageManager.Stub 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; @@ -21495,6 +21480,8 @@ public class PackageManagerService extends IPackageManager.Stub // 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); @@ -21539,16 +21526,23 @@ public class PackageManagerService extends IPackageManager.Stub } 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) { @@ -21617,6 +21611,19 @@ public class PackageManagerService extends IPackageManager.Stub } } } + 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; @@ -28141,8 +28148,8 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public void deleteOatArtifactsOfPackage(String packageName) { - PackageManagerService.this.deleteOatArtifactsOfPackage(packageName); + public long deleteOatArtifactsOfPackage(String packageName) { + return PackageManagerService.this.deleteOatArtifactsOfPackage(packageName); } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 49559f299fa0..1aa80a953d6c 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 @@ class PackageManagerShellCommand extends ShellCommand { 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 @@ class PackageManagerShellCommand extends ShellCommand { } } + 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/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 4a68b7606c60..c842ff1b11a5 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 @@ public class StagingManager { 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/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java index 83d4ce7ff057..b26b6940a1af 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__COMPILATI 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 @@ public class ArtStatsLogUtils { 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/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index bad7e5c27b7b..dab980a9e4b2 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 @@ final class DefaultPermissionGrantPolicy { // 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 a391dbc1fa47..38e9d3ec34e3 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 @@ public class PermissionManagerService extends IPermissionManager.Stub { 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 @@ public class PermissionManagerService extends IPermissionManager.Stub { 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 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + 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 05879ec9545e..fad0aefd3c0a 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 com.android.server.pm.PackageSetting; 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 @@ public class PackageStateUnserialized { 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/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java index ed4a7bf107d1..f72adb609f6f 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 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo @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 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo 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/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java index f7d61367c81e..95a06fcff734 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 @@ public class Vcn extends Handler { 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() { diff --git a/services/core/java/com/android/server/vibrator/StepToRampAdapter.java b/services/core/java/com/android/server/vibrator/StepToRampAdapter.java index d439b94ec2fc..1d8c64bddd49 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.RampSegment; import android.os.vibrator.StepSegment; import android.os.vibrator.VibrationEffectSegment; +import java.util.ArrayList; import java.util.List; /** @@ -59,6 +60,7 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter convertStepsToRamps(segments); int newRepeatIndex = addRampDownToZeroAmplitudeSegments(segments, repeatIndex); newRepeatIndex = addRampDownToLoop(segments, newRepeatIndex); + newRepeatIndex = splitLongRampSegments(info, segments, newRepeatIndex); return newRepeatIndex; } @@ -210,11 +212,70 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter 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 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter } 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 f756434e981a..885f0e4c78ab 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -353,6 +353,7 @@ final class VibrationSettings { + ", mLowPowerMode=" + mLowPowerMode + ", mZenMode=" + Settings.Global.zenModeToString(mZenMode) + ", mProcStatesCache=" + mUidObserver.mProcStatesCache + + ", mHapticChannelMaxVibrationAmplitude=" + getHapticChannelMaxVibrationAmplitude() + ", mHapticFeedbackIntensity=" + intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_TOUCH)) + ", mHapticFeedbackDefaultIntensity=" @@ -411,6 +412,12 @@ final class VibrationSettings { } } + 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); diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java index 150fde99b706..45d511159e11 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 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { 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 @@ final class VibrationThread extends Thread implements IBinder.DeathRecipient { 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 5dceac2d066c..001d5c440d58 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 android.util.Slog; 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 @@ final class VibratorController { 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 @@ final class VibratorController { /** 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 @@ final class VibratorController { 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 @@ final class VibratorController { 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/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 53f1035ee422..782e18b0250c 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.InputStream; 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; @@ -3140,7 +3142,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - private void writeWallpaperAttributes(TypedXmlSerializer out, String tag, + + @VisibleForTesting + void writeWallpaperAttributes(TypedXmlSerializer out, String tag, WallpaperData wallpaper) throws IllegalArgumentException, IllegalStateException, IOException { if (DEBUG) { @@ -3179,6 +3183,19 @@ public class WallpaperManagerService extends IWallpaperManager.Stub 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 +3427,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - 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 +3455,17 @@ public class WallpaperManagerService extends IWallpaperManager.Stub 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/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 55d1920681c5..0ad8782cea4f 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_ASSISTANT; 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.SystemClock; 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.internal.util.function.pooled.PooledLambda; 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; @@ -677,8 +680,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 @@ -1788,17 +1789,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** * 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; } @@ -1835,7 +1838,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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. @@ -1872,7 +1875,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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; } @@ -4873,6 +4880,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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); @@ -6278,15 +6291,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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; } @@ -7266,41 +7285,48 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } 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; - - // 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); + 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); + + // 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) { @@ -7342,8 +7368,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // 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 @@ -7404,13 +7428,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 @@ -7477,6 +7494,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // 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) { @@ -7713,6 +7731,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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. @@ -7721,17 +7745,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ // 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; } @@ -7739,20 +7765,30 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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: @@ -7780,9 +7816,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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); } } @@ -7806,6 +7842,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } 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; } diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java index 0ec01422474a..2ea043a5c623 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 @@ public final class CompatModePackages { * 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 @@ public final class CompatModePackages { /** * 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 @@ public final class CompatModePackages { /** * 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 @@ public final class CompatModePackages { /** * 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 @@ public final class CompatModePackages { /** * 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 final class CompatModePackages { 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 @@ public final class CompatModePackages { 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/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index ea80b8bb5286..bc2556a5a497 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 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // 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 @@ class RootWindowContainer extends WindowContainer<DisplayContent> 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) { @@ -2653,12 +2657,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent> 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 +3389,15 @@ class RootWindowContainer extends WindowContainer<DisplayContent> */ 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/Task.java b/services/core/java/com/android/server/wm/Task.java index 565f99f80890..325f10f65af2 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 @@ class Task extends WindowContainer<WindowContainer> { } 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); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index e9dd521670a9..2c8fcebdd50f 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2463,6 +2463,17 @@ public class WindowManagerService extends IWindowManager.Stub 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 */); } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 9a6e4448966d..4471f6c91190 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -683,8 +683,9 @@ class WindowStateAnimator { } // 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); } diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp index 698e3f75d0ed..9029fe7cca66 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 JavaVM* sJvm = nullptr; 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 @@ static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong ptr, 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.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.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.primitiveDurations.isOk()) { - std::vector<int32_t> durations; - for (auto duration : info.primitiveDurations.value()) { - durations.push_back(duration.count()); + if (info.supportedPrimitives.isOk()) { + 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 @@ static const JNINativeMethod method_table[] = { {"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 @@ int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env 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/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index 3e2e37c834f1..82aaa61527d1 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> diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt index d40854a87453..6e2e3625f60c 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 @@ package com.android.server.display.config { 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); } diff --git a/services/net/Android.bp b/services/net/Android.bp index dd864aed4e8e..a822257e1a74 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -51,7 +51,6 @@ java_library { // 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", ], diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java index e99113d1296f..acf50b4569c6 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 @@ public class DeviceIdleControllerTest { 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 @@ public class DeviceIdleControllerTest { 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 @@ public class DeviceIdleControllerTest { 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 @@ public class DeviceIdleControllerTest { .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 @@ public class DeviceIdleControllerTest { 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 @@ public class DeviceIdleControllerTest { 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/appsearch/stats/PlatformLoggerTest.java b/services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java index da0b83ec819c..28fcaee79572 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.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; 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 class PlatformLoggerTest { 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 @@ public class PlatformLoggerTest { 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 @@ public class PlatformLoggerTest { 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 @@ public class PlatformLoggerTest { 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 @@ public class PlatformLoggerTest { 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 @@ public class PlatformLoggerTest { 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/location/gnss/GnssGeofenceProxyTest.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java index b480f24fb371..5e219a25ed9a 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 @@ package com.android.server.location.gnss; 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 @@ public class GnssGeofenceProxyTest { 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 @@ public class GnssGeofenceProxyTest { 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 3d0378170c94..d728451d92b9 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 @@ import java.util.HashMap; 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 f1099f0e8184..cd70020f5c28 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.Set; 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 ae70dadba041..bd24cfd78a2c 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 @@ public class TestInjector implements Injector { 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 @@ public class TestInjector implements Injector { } @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 6bc3b6041070..f703e2e59181 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 @@ package com.android.server.location.provider; 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.mockito.MockitoAnnotations.initMocks; 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 android.util.Log; 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 @@ public class LocationProviderManagerTest { @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 @@ public class LocationProviderManagerTest { 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 @@ public class LocationProviderManagerTest { } @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/StationaryThrottlingLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/StationaryThrottlingLocationProviderTest.java index 04e0151e619a..63996f0e021c 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.times; 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 @@ public class StationaryThrottlingLocationProviderTest { private TestInjector mInjector; private FakeProvider mDelegateProvider; + private @Mock Context mContext; private @Mock AbstractLocationProvider.Listener mListener; private @Mock FakeProvider.FakeProviderInterface mDelegate; @@ -72,7 +74,7 @@ public class StationaryThrottlingLocationProviderTest { 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 000000000000..4d46abaa6733 --- /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 000000000000..4b6c79b954cd --- /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 7d628be571a9..68570ffc6fce 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 @@ public class StagingManagerTest { 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 @@ public class StagingManagerTest { 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 5a42c4bc7f3a..53483f6d70bc 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.assertEquals; 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.service.wallpaper.WallpaperService; 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.junit.runner.RunWith; 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 @@ public class WallpaperManagerServiceTests { 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 aadaba4d2fce..3c10789bc792 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java @@ -42,7 +42,7 @@ import androidx.test.core.app.ApplicationProvider; import com.android.compatibility.common.util.SystemUtil; import com.android.server.appsearch.external.localstorage.util.PrefixUtil; -import com.android.server.appsearch.visibilitystore.VisibilityStore; +import com.android.server.appsearch.visibilitystore.VisibilityStoreImpl; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -67,7 +67,7 @@ public class AppSearchImplPlatformTest { private final Map<UserHandle, PackageManager> mMockPackageManagers = new ArrayMap<>(); private Context mContext; private AppSearchImpl mAppSearchImpl; - private VisibilityStore mVisibilityStore; + private VisibilityStoreImpl mVisibilityStore; private int mGlobalQuerierUid; @Before @@ -93,7 +93,7 @@ public class AppSearchImplPlatformTest { // Give ourselves global query permissions mAppSearchImpl = AppSearchImpl.create( mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE); - mVisibilityStore = VisibilityStore.create(mAppSearchImpl, mContext); + mVisibilityStore = VisibilityStoreImpl.create(mAppSearchImpl, mContext); mGlobalQuerierUid = mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0); } @@ -127,8 +127,8 @@ public class AppSearchImplPlatformTest { "database", Collections.singletonList(new AppSearchSchema.Builder("schema1").build()), mVisibilityStore, - /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"), - /*schemasPackageAccessible=*/ ImmutableMap.of( + /*schemasNotDisplayedBySystem=*/ Collections.singletonList("schema1"), + /*schemasVisibleToPackages=*/ ImmutableMap.of( "schema1", ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))), /*forceOverride=*/ false, @@ -159,8 +159,8 @@ public class AppSearchImplPlatformTest { new AppSearchSchema.Builder("schema1").build(), new AppSearchSchema.Builder("schema2").build()), mVisibilityStore, - /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"), - /*schemasPackageAccessible=*/ ImmutableMap.of( + /*schemasNotDisplayedBySystem=*/ Collections.singletonList("schema1"), + /*schemasVisibleToPackages=*/ ImmutableMap.of( "schema1", ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))), /*forceOverride=*/ false, @@ -233,8 +233,8 @@ public class AppSearchImplPlatformTest { "database", Collections.singletonList(new AppSearchSchema.Builder("schema1").build()), mVisibilityStore, - /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"), - /*schemasPackageAccessible=*/ ImmutableMap.of( + /*schemasNotDisplayedBySystem=*/ Collections.singletonList("schema1"), + /*schemasVisibleToPackages=*/ ImmutableMap.of( "schema1", ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))), /*forceOverride=*/ false, @@ -263,8 +263,8 @@ public class AppSearchImplPlatformTest { "database", /*schemas=*/ Collections.emptyList(), mVisibilityStore, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ true, /*schemaVersion=*/ 0); @@ -292,8 +292,8 @@ public class AppSearchImplPlatformTest { "database", Collections.singletonList(new AppSearchSchema.Builder("schema1").build()), mVisibilityStore, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*schemaVersion=*/ 0); @@ -327,8 +327,8 @@ public class AppSearchImplPlatformTest { "database", Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), mVisibilityStore, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*schemaVersion=*/ 0); @@ -355,8 +355,8 @@ public class AppSearchImplPlatformTest { "database", Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), mVisibilityStore, - /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("Schema"), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.singletonList("Schema"), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*schemaVersion=*/ 0); @@ -370,7 +370,7 @@ public class AppSearchImplPlatformTest { } @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 @@ -384,8 +384,8 @@ public class AppSearchImplPlatformTest { "database", Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), mVisibilityStore, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*schemaVersion=*/ 0); assertThat(mVisibilityStore @@ -399,7 +399,7 @@ public class AppSearchImplPlatformTest { } @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}; @@ -423,8 +423,8 @@ public class AppSearchImplPlatformTest { "database", Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), mVisibilityStore, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ ImmutableMap.of( + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ ImmutableMap.of( "Schema", ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))), /*forceOverride=*/ false, 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 f032402f47a0..02bb168dbde6 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.android.server.appsearch.external.localstorage.util.PrefixUtil 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.SetSchemaResponse; 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; @@ -55,7 +56,6 @@ 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.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -391,7 +391,7 @@ public class AppSearchImplTest { DocumentProto.Builder actualDocument = documentProto.toBuilder(); AppSearchException e = - expectThrows( + assertThrows( AppSearchException.class, () -> removePrefixesFromDocument(actualDocument)); assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names"); } @@ -416,7 +416,7 @@ public class AppSearchImplTest { DocumentProto.Builder actualDocument = documentProto.toBuilder(); AppSearchException e = - expectThrows( + assertThrows( AppSearchException.class, () -> removePrefixesFromDocument(actualDocument)); assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names"); } @@ -431,8 +431,8 @@ public class AppSearchImplTest { "database", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -480,8 +480,8 @@ public class AppSearchImplTest { "database1", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -499,7 +499,7 @@ public class AppSearchImplTest { new SearchSpec.Builder().addFilterSchemas("Type1").build(), context.getPackageName(), /*visibilityStore=*/ null, - VisibilityStore.NO_OP_UID, + Process.INVALID_UID, /*callerHasSystemAccess=*/ false, /*logger=*/ null); assertThat(results.getResults()).hasSize(1); @@ -513,7 +513,7 @@ public class AppSearchImplTest { .setSchema(context.getPackageName() + "$database1/Type1") .build(); AppSearchException e = - expectThrows( + assertThrows( AppSearchException.class, () -> PrefixUtil.getPrefix(invalidDoc.getNamespace())); assertThat(e) @@ -538,10 +538,8 @@ public class AppSearchImplTest { 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()) @@ -558,7 +556,7 @@ public class AppSearchImplTest { new SearchSpec.Builder().addFilterSchemas("Type1").build(), context.getPackageName(), /*visibilityStore=*/ null, - VisibilityStore.NO_OP_UID, + Process.INVALID_UID, /*callerHasSystemAccess=*/ false, /*logger=*/ null); assertThat(results.getResults()).isEmpty(); @@ -569,8 +567,8 @@ public class AppSearchImplTest { "database1", Collections.singletonList(new AppSearchSchema.Builder("Type1").build()), /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -585,7 +583,7 @@ public class AppSearchImplTest { new SearchSpec.Builder().addFilterSchemas("Type1").build(), context.getPackageName(), /*visibilityStore=*/ null, - VisibilityStore.NO_OP_UID, + Process.INVALID_UID, /*callerHasSystemAccess=*/ false, /*logger=*/ null); assertThat(results.getResults()).hasSize(1); @@ -604,8 +602,8 @@ public class AppSearchImplTest { "database", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -638,8 +636,8 @@ public class AppSearchImplTest { "database1", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); mAppSearchImpl.setSchema( @@ -647,8 +645,8 @@ public class AppSearchImplTest { "database2", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -692,8 +690,8 @@ public class AppSearchImplTest { "database", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -734,8 +732,8 @@ public class AppSearchImplTest { "database1", schema1, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -747,8 +745,8 @@ public class AppSearchImplTest { "database2", schema2, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -790,8 +788,8 @@ public class AppSearchImplTest { "database1", schema1, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -803,8 +801,8 @@ public class AppSearchImplTest { "database2", schema2, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -851,7 +849,7 @@ public class AppSearchImplTest { searchSpec, /*callerPackageName=*/ "", /*visibilityStore=*/ null, - VisibilityStore.NO_OP_UID, + Process.INVALID_UID, /*callerHasSystemAccess=*/ false, /*logger=*/ null); assertThat(searchResultPage.getResults()).isEmpty(); @@ -893,8 +891,8 @@ public class AppSearchImplTest { "database1", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -938,8 +936,8 @@ public class AppSearchImplTest { "database1", oldSchemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -954,8 +952,8 @@ public class AppSearchImplTest { "database1", newSchemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ true, /*version=*/ 0); assertThat(setSchemaResponse.getDeletedTypes()).containsExactly("Text"); @@ -977,8 +975,8 @@ public class AppSearchImplTest { "database1", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -1010,8 +1008,8 @@ public class AppSearchImplTest { "database1", finalSchemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -1024,8 +1022,8 @@ public class AppSearchImplTest { "database1", finalSchemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ true, /*version=*/ 0); @@ -1062,8 +1060,8 @@ public class AppSearchImplTest { "database1", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); mAppSearchImpl.setSchema( @@ -1071,8 +1069,8 @@ public class AppSearchImplTest { "database2", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -1111,8 +1109,8 @@ public class AppSearchImplTest { "database1", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ true, /*version=*/ 0); @@ -1156,8 +1154,8 @@ public class AppSearchImplTest { "database", schema, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -1218,8 +1216,8 @@ public class AppSearchImplTest { "database", schema, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); mAppSearchImpl.setSchema( @@ -1227,8 +1225,8 @@ public class AppSearchImplTest { "database", schema, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -1273,8 +1271,8 @@ public class AppSearchImplTest { "database1", Collections.singletonList(new AppSearchSchema.Builder("schema").build()), /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); assertThat(mAppSearchImpl.getPackageToDatabases()) @@ -1287,8 +1285,8 @@ public class AppSearchImplTest { "database2", Collections.singletonList(new AppSearchSchema.Builder("schema").build()), /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); assertThat(mAppSearchImpl.getPackageToDatabases()) @@ -1301,8 +1299,8 @@ public class AppSearchImplTest { "database1", Collections.singletonList(new AppSearchSchema.Builder("schema").build()), /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); assertThat(mAppSearchImpl.getPackageToDatabases()) @@ -1360,8 +1358,8 @@ public class AppSearchImplTest { "database", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -1511,8 +1509,8 @@ public class AppSearchImplTest { "database", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -1534,8 +1532,8 @@ public class AppSearchImplTest { "database", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -1550,8 +1548,8 @@ public class AppSearchImplTest { "database", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -1599,8 +1597,8 @@ public class AppSearchImplTest { "database", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -1622,8 +1620,8 @@ public class AppSearchImplTest { "database1", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -1644,8 +1642,8 @@ public class AppSearchImplTest { "database1", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); mAppSearchImpl.setSchema( @@ -1653,8 +1651,8 @@ public class AppSearchImplTest { "database2", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -1700,15 +1698,15 @@ public class AppSearchImplTest { "database", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); appSearchImpl.close(); // Check all our public APIs - expectThrows( + assertThrows( IllegalStateException.class, () -> appSearchImpl.setSchema( @@ -1716,15 +1714,15 @@ public class AppSearchImplTest { "database", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0)); - expectThrows( + assertThrows( IllegalStateException.class, () -> appSearchImpl.getSchema("package", "database")); - expectThrows( + assertThrows( IllegalStateException.class, () -> appSearchImpl.putDocument( @@ -1733,13 +1731,13 @@ public class AppSearchImplTest { new GenericDocument.Builder<>("namespace", "id", "type").build(), /*logger=*/ null)); - expectThrows( + assertThrows( IllegalStateException.class, () -> appSearchImpl.getDocument( "package", "database", "namespace", "id", Collections.emptyMap())); - expectThrows( + assertThrows( IllegalStateException.class, () -> appSearchImpl.query( @@ -1749,7 +1747,7 @@ public class AppSearchImplTest { new SearchSpec.Builder().build(), /*logger=*/ null)); - expectThrows( + assertThrows( IllegalStateException.class, () -> appSearchImpl.globalQuery( @@ -1757,19 +1755,19 @@ public class AppSearchImplTest { new SearchSpec.Builder().build(), "package", /*visibilityStore=*/ null, - VisibilityStore.NO_OP_UID, + Process.INVALID_UID, /*callerHasSystemAccess=*/ false, /*logger=*/ null)); - expectThrows( + assertThrows( IllegalStateException.class, () -> appSearchImpl.getNextPage(/*nextPageToken=*/ 1L)); - expectThrows( + assertThrows( IllegalStateException.class, () -> appSearchImpl.invalidateNextPageToken(/*nextPageToken=*/ 1L)); - expectThrows( + assertThrows( IllegalStateException.class, () -> appSearchImpl.reportUsage( @@ -1780,7 +1778,7 @@ public class AppSearchImplTest { /*usageTimestampMillis=*/ 1000L, /*systemUsage=*/ false)); - expectThrows( + assertThrows( IllegalStateException.class, () -> appSearchImpl.remove( @@ -1790,7 +1788,7 @@ public class AppSearchImplTest { "id", /*removeStatsBuilder=*/ null)); - expectThrows( + assertThrows( IllegalStateException.class, () -> appSearchImpl.removeByQuery( @@ -1800,15 +1798,15 @@ public class AppSearchImplTest { new SearchSpec.Builder().build(), /*removeStatsBuilder=*/ null)); - expectThrows( + assertThrows( IllegalStateException.class, () -> appSearchImpl.getStorageInfoForPackage("package")); - expectThrows( + assertThrows( IllegalStateException.class, () -> appSearchImpl.getStorageInfoForDatabase("package", "database")); - expectThrows( + assertThrows( IllegalStateException.class, () -> appSearchImpl.persistToDisk(PersistType.Code.FULL)); } @@ -1827,8 +1825,8 @@ public class AppSearchImplTest { "database", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -1866,8 +1864,8 @@ public class AppSearchImplTest { "database", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -1892,7 +1890,7 @@ public class AppSearchImplTest { // Delete the first document appSearchImpl.remove("package", "database", "namespace1", "id1", /*statsBuilder=*/ null); appSearchImpl.persistToDisk(PersistType.Code.LITE); - expectThrows( + assertThrows( AppSearchException.class, () -> appSearchImpl.getDocument( @@ -1909,7 +1907,7 @@ public class AppSearchImplTest { // Only the second document should be retrievable from another instance. AppSearchImpl appSearchImpl2 = AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE); - expectThrows( + assertThrows( AppSearchException.class, () -> appSearchImpl2.getDocument( @@ -1938,8 +1936,8 @@ public class AppSearchImplTest { "database", schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); @@ -1972,7 +1970,7 @@ public class AppSearchImplTest { .build(), /*statsBuilder=*/ null); appSearchImpl.persistToDisk(PersistType.Code.LITE); - expectThrows( + assertThrows( AppSearchException.class, () -> appSearchImpl.getDocument( @@ -1989,7 +1987,7 @@ public class AppSearchImplTest { // Only the second document should be retrievable from another instance. AppSearchImpl appSearchImpl2 = AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE); - expectThrows( + 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 f20e8c6d13a9..9b75561fd2ec 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,6 +25,7 @@ import android.app.appsearch.AppSearchSchema; import android.app.appsearch.GenericDocument; import android.app.appsearch.SearchResultPage; import android.app.appsearch.SearchSpec; +import android.app.appsearch.exceptions.AppSearchException; import com.android.server.appsearch.external.localstorage.stats.CallStats; import com.android.server.appsearch.external.localstorage.stats.InitializeStats; @@ -32,17 +33,24 @@ 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.DocumentProto; import com.android.server.appsearch.proto.InitializeStatsProto; import com.android.server.appsearch.proto.PutDocumentStatsProto; +import com.android.server.appsearch.proto.PutResultProto; import com.android.server.appsearch.proto.QueryStatsProto; import com.android.server.appsearch.proto.ScoringSpecProto; +import com.android.server.appsearch.proto.StatusProto; import com.android.server.appsearch.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; @@ -279,7 +287,7 @@ public class AppSearchLoggerTest { // Testing actual logging // @Test - public void testLoggingStats_initialize() throws Exception { + public void testLoggingStats_initializeWithoutDocuments_success() throws Exception { // Create an unused AppSearchImpl to generated an InitializeStats. InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder(); AppSearchImpl.create(mTemporaryFolder.newFolder(), initStatsBuilder, ALWAYS_OPTIMIZE); @@ -295,25 +303,139 @@ public class AppSearchLoggerTest { .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 { - // Insert schema + 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 = - Collections.singletonList(new AppSearchSchema.Builder("type").build()); + 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"; + 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, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*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); @@ -322,43 +444,119 @@ public class AppSearchLoggerTest { 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, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*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; @@ -368,17 +566,59 @@ public class AppSearchLoggerTest { 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_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() throws Exception { + public void testLoggingStats_remove_success() throws Exception { // Insert schema final String testPackageName = "testPackage"; final String testDatabase = "testDatabase"; @@ -391,8 +631,8 @@ public class AppSearchLoggerTest { testDatabase, schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); GenericDocument document = @@ -406,12 +646,59 @@ public class AppSearchLoggerTest { 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"; @@ -423,8 +710,8 @@ public class AppSearchLoggerTest { testDatabase, schemas, /*visibilityStore=*/ null, - /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), - /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), + /*schemasVisibleToPackages=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); GenericDocument document1 = @@ -444,6 +731,7 @@ public class AppSearchLoggerTest { 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/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java index d3feb12912ad..64a670dcdb38 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 @@ -46,7 +46,6 @@ public class SnippetTest { 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 @@ public class SnippetTest { 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 6d9068675a72..57d994155d5b 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 @@ public class AppSearchStatsTest { @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 @@ public class AppSearchStatsTest { 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 @@ public class AppSearchStatsTest { } @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 7c275e13e24a..ec96d6a71be4 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 @@ public class PlatformLoggerTest { 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 @@ public class PlatformLoggerTest { 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 index b67ebe423eab..07a728bac2a5 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 android.util.ArrayMap; 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 org.mockito.Mockito; 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 @@ public class VisibilityStoreTest { @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 @@ -90,7 +89,7 @@ public class VisibilityStoreTest { // Give ourselves global query permissions AppSearchImpl appSearchImpl = AppSearchImpl.create( mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE); - mVisibilityStore = VisibilityStore.create(appSearchImpl, mContext); + mVisibilityStore = VisibilityStoreImpl.create(appSearchImpl, mContext); mUid = mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0); } @@ -132,7 +131,7 @@ public class VisibilityStoreTest { } @Test - public void testSetVisibility_platformSurfaceable() throws Exception { + public void testSetVisibility_displayedBySystem() throws Exception { // Make sure we have global query privileges PackageManager mockPackageManager = getMockPackageManager(mContext.getUser()); when(mockPackageManager @@ -143,9 +142,9 @@ public class VisibilityStoreTest { mVisibilityStore.setVisibility( "package", "database", - /*schemasNotPlatformSurfaceable=*/ ImmutableSet.of( + /*schemasNotDisplayedBySystem=*/ ImmutableSet.of( "prefix/schema1", "prefix/schema2"), - /*schemasPackageAccessible=*/ Collections.emptyMap()); + /*schemasVisibleToPackages=*/ Collections.emptyMap()); assertThat( mVisibilityStore.isSchemaSearchableByCaller( "package", @@ -168,9 +167,9 @@ public class VisibilityStoreTest { mVisibilityStore.setVisibility( "package", "database", - /*schemasNotPlatformSurfaceable=*/ ImmutableSet.of( + /*schemasNotDisplayedBySystem=*/ ImmutableSet.of( "prefix/schema1", "prefix/schema3"), - /*schemasPackageAccessible=*/ Collections.emptyMap()); + /*schemasVisibleToPackages=*/ Collections.emptyMap()); assertThat( mVisibilityStore.isSchemaSearchableByCaller( "package", @@ -200,8 +199,8 @@ public class VisibilityStoreTest { mVisibilityStore.setVisibility( "package", "database", - /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(), - /*schemasPackageAccessible=*/ Collections.emptyMap()); + /*schemasNotDisplayedBySystem=*/ Collections.emptySet(), + /*schemasVisibleToPackages=*/ Collections.emptyMap()); assertThat( mVisibilityStore.isSchemaSearchableByCaller( "package", @@ -229,7 +228,7 @@ public class VisibilityStoreTest { } @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}; @@ -272,8 +271,8 @@ public class VisibilityStoreTest { 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", @@ -339,8 +338,8 @@ public class VisibilityStoreTest { mVisibilityStore.setVisibility( "package", "database", - /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(), - /*schemasPackageAccessible=*/ ImmutableMap.of( + /*schemasNotDisplayedBySystem=*/ Collections.emptySet(), + /*schemasVisibleToPackages=*/ ImmutableMap.of( "prefix/schemaFoo", ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo)))); @@ -392,8 +391,8 @@ public class VisibilityStoreTest { mVisibilityStore.setVisibility( "package", "database", - /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(), - /*schemasPackageAccessible=*/ ImmutableMap.of( + /*schemasNotDisplayedBySystem=*/ Collections.emptySet(), + /*schemasVisibleToPackages=*/ ImmutableMap.of( "prefix/schemaFoo", ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo)))); @@ -425,8 +424,8 @@ public class VisibilityStoreTest { mVisibilityStore.setVisibility( /*packageName=*/ "", /*databaseName=*/ "", - /*schemasNotPlatformSurfaceable=*/ Collections.emptySet(), - /*schemasPackageAccessible=*/ ImmutableMap.of( + /*schemasNotDisplayedBySystem=*/ Collections.emptySet(), + /*schemasVisibleToPackages=*/ ImmutableMap.of( "schema", ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo)))); 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 c0df2e33ffed..cae6c863ab02 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -48,8 +48,11 @@ import android.database.ContentObserver; 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; @@ -120,7 +123,7 @@ public class DisplayModeDirectorTest { 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); @@ -1256,13 +1259,163 @@ public class DisplayModeDirectorTest { 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 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)); @@ -1403,7 +1556,7 @@ public class DisplayModeDirectorTest { 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; @@ -1444,6 +1597,14 @@ public class DisplayModeDirectorTest { 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/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index 7df2dd6988bf..c572dd65245f 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 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { 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 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { @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 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } @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 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } @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 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } @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/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java index 17d99e652726..9044b27d4994 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 @@ public class SystemConfigTest { } /** + * 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/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java index 058575817acf..0449e4450d06 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 @@ final class FakeVibratorControllerProvider { 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 @@ final class FakeVibratorControllerProvider { } @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 @@ final class FakeVibratorControllerProvider { 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 f4eb2ded5a9d..32988efbab2c 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 @@ public class StepToRampAdapterTest { } @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/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java index f02e2f081e3b..b8fdb552e453 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 @@ public class VibrationThreadTest { @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 @@ public class VibrationThreadTest { 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 @@ public class VibrationThreadTest { } @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 @@ public class VibrationThreadTest { @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 @@ public class VibrationThreadTest { 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 9e98e7d0410c..a732bd18676a 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 @@ public class VibratorControllerTest { 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/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java index 3862d754b8f5..71c05b5c46f7 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.verify; 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 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { NotificationUsageStats mUsageStats; @Mock IAccessibilityManager mAccessibilityService; + @Mock + KeyguardManager mKeyguardManager; NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake(); private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake( 1 << 30); @@ -153,6 +156,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { 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 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { 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 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { } @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/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index a522b5c6161e..1f543a17885a 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.spy; 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; @@ -2425,6 +2426,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 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 +2436,22 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @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"); 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 3a51ff2fb5fa..23da02c0e4a1 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -3333,6 +3333,17 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @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/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 9267285b446b..9cf29d4dcc50 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.anyInt; 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 @@ public class RootWindowContainerTests extends WindowTestsBase { // 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 4e261deb67f6..4872ec511ccc 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 @@ public class SizeCompatTests extends WindowTestsBase { 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 @@ public class SizeCompatTests extends WindowTestsBase { // 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(); 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 d93ebb383b3b..0ebff1d253ef 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.anyBoolean; 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 @@ public class TaskTests extends WindowTestsBase { @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 @@ public class TaskTests extends WindowTestsBase { 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/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 128602d5acbd..1b8492722c10 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 @@ public class UsageStatsService extends SystemService implements } @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 eac21b492f10..5183e5b8e246 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 @@ public class SoundTriggerService extends SystemService { } 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 17303a4aa7e1..beaca68b9a37 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.Context; 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.IOException; 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 @@ final class HotwordDetectionConnection { // 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 class HotwordDetectionConnection { 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 @@ final class HotwordDetectionConnection { 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 @@ final class HotwordDetectionConnection { .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 @@ final class HotwordDetectionConnection { }); } - 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 @@ final class HotwordDetectionConnection { 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 @@ final class HotwordDetectionConnection { 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 @@ final class HotwordDetectionConnection { 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 @@ final class HotwordDetectionConnection { if (DEBUG) { Slog.d(TAG, "startListeningFromExternalSource"); } + handleExternalSourceHotwordDetection( audioStream, audioFormat, @@ -326,16 +327,25 @@ final class HotwordDetectionConnection { if (DEBUG) { Slog.d(TAG, "stopListening"); } + synchronized (mLock) { + stopListeningLocked(); + } + } - mRemoteHotwordDetectionService.run(service -> service.stopDetection()); + private void stopListeningLocked() { + if (!mPerformingSoftwareHotwordDetection) { + Slog.i(TAG, "Hotword detection is not running"); + return; + } + mPerformingSoftwareHotwordDetection = false; - synchronized (mLock) { - if (mCurrentAudioSink != null) { - Slog.i(TAG, "Closing audio stream to hotword detector: stopping requested"); - bestEffortClose(mCurrentAudioSink); - } - mCurrentAudioSink = null; + mRemoteHotwordDetectionService.run(IHotwordDetectionService::stopDetection); + + if (mCurrentAudioSink != null) { + Slog.i(TAG, "Closing audio stream to hotword detector: stopping requested"); + bestEffortClose(mCurrentAudioSink); } + mCurrentAudioSink = null; } void triggerHardwareRecognitionEventForTestLocked( @@ -358,7 +368,14 @@ final class HotwordDetectionConnection { 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 @@ final class HotwordDetectionConnection { 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 @@ final class HotwordDetectionConnection { 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 @@ final class HotwordDetectionConnection { 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 @@ final class HotwordDetectionConnection { } } - // 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 @@ final class HotwordDetectionConnection { 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 @@ final class HotwordDetectionConnection { } mAudioCopyExecutor.execute(() -> { - try (AudioReader source = audioSource; + try (InputStream source = audioSource; OutputStream fos = new ParcelFileDescriptor.AutoCloseOutputStream(serviceAudioSink)) { @@ -681,6 +660,150 @@ final class HotwordDetectionConnection { })); } + 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 bc812c2ae4a7..162acba8002d 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 @@ public class VoiceInteractionManagerService extends SystemService { 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 ca30bc51e046..89c5a720ee7e 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 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne && (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 2e3ca0157a3b..cdd8f7b91d9d 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 @@ final class VoiceInteractionManagerServiceShellCommand extends ShellCommand { return requestHide(pw); case "disable": return requestDisable(pw); + case "restart-detection": + return requestRestartDetection(pw); default: return handleDefaultCommands(cmd); } @@ -74,6 +76,8 @@ final class VoiceInteractionManagerServiceShellCommand extends ShellCommand { 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 @@ final class VoiceInteractionManagerServiceShellCommand extends ShellCommand { 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 1953af4adee5..e000265f0a2c 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -1018,6 +1018,16 @@ public class TelecomManager { // 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 @@ public class TelecomManager { * 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 @@ public class TelecomManager { 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 18afde742abb..6f286d9f3006 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -79,7 +79,7 @@ interface ITelecomService { /** * @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 45d3dade82b6..9ffb236d3f59 100644 --- a/tests/Internal/src/android/app/WallpaperColorsTest.java +++ b/tests/Internal/src/android/app/WallpaperColorsTest.java @@ -20,6 +20,7 @@ import android.graphics.Bitmap; 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 @@ public class WallpaperColorsTest { // 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 c679d0487629..c563e06ab528 100644 --- a/tests/StagedInstallTest/Android.bp +++ b/tests/StagedInstallTest/Android.bp @@ -52,6 +52,7 @@ java_test_host { 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 e633c87d7fbb..6a62304f9af7 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 @@ public class StagedInstallInternalTest { 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 ccd63f94de54..5d7fdd183dec 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 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { @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 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { 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 4ce78aa4d8c1..dc338ae0fdc7 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 android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE; 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 class VcnGatewayConnectionConfigTest { 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 @@ public class VcnGatewayConnectionConfigTest { 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 @@ public class VcnGatewayConnectionConfigTest { 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 @@ public class VcnGatewayConnectionConfigTest { 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/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java index f681ee19ab12..5d2f9d748581 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 @@ public class VcnTest { 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); |