diff options
779 files changed, 14831 insertions, 5632 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/framework/java/android/app/AlarmManager.java b/apex/jobscheduler/framework/java/android/app/AlarmManager.java index 1efe5cb2f53e..4843415fdbdd 100644 --- a/apex/jobscheduler/framework/java/android/app/AlarmManager.java +++ b/apex/jobscheduler/framework/java/android/app/AlarmManager.java @@ -1285,14 +1285,20 @@ public class AlarmManager { } /** - * Called to check if the caller has the permission - * {@link Manifest.permission#SCHEDULE_EXACT_ALARM}. - * - * Apps can start {@link android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM} to + * Called to check if the caller can schedule exact alarms. + * <p> + * Apps targeting {@link Build.VERSION_CODES#S} or higher can schedule exact alarms if they + * have the {@link Manifest.permission#SCHEDULE_EXACT_ALARM} permission. These apps can also + * start {@link android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM} to * request this from the user. + * <p> + * Apps targeting lower sdk versions, can always schedule exact alarms. * - * @return {@code true} if the caller has the permission, {@code false} otherwise. + * @return {@code true} if the caller can schedule exact alarms. * @see android.provider.Settings#ACTION_REQUEST_SCHEDULE_EXACT_ALARM + * @see #setExact(int, long, PendingIntent) + * @see #setExactAndAllowWhileIdle(int, long, PendingIntent) + * @see #setAlarmClock(AlarmClockInfo, PendingIntent) */ public boolean canScheduleExactAlarms() { return hasScheduleExactAlarm(mContext.getOpPackageName(), mContext.getUserId()); diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index 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/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index fb5129f18417..70e548d4c547 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -2572,6 +2572,9 @@ public class AlarmManagerService extends SystemService { throw new SecurityException("Uid " + callingUid + " cannot query hasScheduleExactAlarm for uid " + uid); } + if (!isExactAlarmChangeEnabled(packageName, userId)) { + return true; + } return (uid > 0) ? hasScheduleExactAlarmInternal(packageName, uid) : false; } diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java index 37b3c0431455..d532e20a0158 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java @@ -20,6 +20,7 @@ import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT; import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER; import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK; import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED; +import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT; import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE; import static android.app.usage.UsageStatsManager.REASON_SUB_MASK; import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION; @@ -259,8 +260,10 @@ public class AppIdleHistory { int bucketingReason = REASON_MAIN_USAGE | usageReason; final boolean isUserUsage = isUserUsage(bucketingReason); - if (appUsageHistory.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage) { - // Only user usage should bring an app out of the RESTRICTED bucket. + if (appUsageHistory.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage + && (appUsageHistory.bucketingReason & REASON_MAIN_MASK) != REASON_MAIN_TIMEOUT) { + // Only user usage should bring an app out of the RESTRICTED bucket, unless the app + // just timed out into RESTRICTED. newBucket = STANDBY_BUCKET_RESTRICTED; bucketingReason = appUsageHistory.bucketingReason; } else { diff --git a/api/Android.bp b/api/Android.bp index 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/OWNERS b/api/OWNERS index 88d0b61a2ab6..a0272709ffc0 100644 --- a/api/OWNERS +++ b/api/OWNERS @@ -1 +1,6 @@ +hansson@google.com + +# Modularization team +file:platform/packages/modules/common:/OWNERS + per-file Android.bp = file:platform/build/soong:/OWNERS diff --git a/api/api_versions_trimmer.py b/api/api_versions_trimmer.py new file mode 100755 index 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/ActivityThread.java b/core/java/android/app/ActivityThread.java index 02ab3143eed9..034ad8e83fd3 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -447,7 +447,7 @@ public final class ActivityThread extends ClientTransactionHandler @GuardedBy("mLock") ContentProviderHolder mHolder; // Temp holder to be used between notifier and waiter - Object mLock; // The lock to be used to get notified when the provider is ready + final Object mLock; // The lock to be used to get notified when the provider is ready public ProviderKey(String authority, int userId) { this.authority = authority; @@ -1827,11 +1827,14 @@ public final class ActivityThread extends ClientTransactionHandler @Override public void notifyContentProviderPublishStatus(@NonNull ContentProviderHolder holder, - @NonNull String auth, int userId, boolean published) { - final ProviderKey key = getGetProviderKey(auth, userId); - synchronized (key.mLock) { - key.mHolder = holder; - key.mLock.notifyAll(); + @NonNull String authorities, int userId, boolean published) { + final String auths[] = authorities.split(";"); + for (String auth: auths) { + final ProviderKey key = getGetProviderKey(auth, userId); + synchronized (key.mLock) { + key.mHolder = holder; + key.mLock.notifyAll(); + } } } 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/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 4555c1725a56..d6ff6d3dfc3a 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -153,7 +153,7 @@ oneway interface IApplicationThread { void performDirectAction(IBinder activityToken, String actionId, in Bundle arguments, in RemoteCallback cancellationCallback, in RemoteCallback resultCallback); - void notifyContentProviderPublishStatus(in ContentProviderHolder holder, String auth, + void notifyContentProviderPublishStatus(in ContentProviderHolder holder, String authorities, int userId, boolean published); void instrumentWithoutRestart(in ComponentName instrumentationName, in Bundle instrumentationArgs, diff --git a/core/java/android/app/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/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index 04a12afb8039..6ca7dfbe2284 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -34,6 +34,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; @@ -260,12 +261,19 @@ public abstract class PropertyInvalidatedCache<Query, Result> { private static final HashMap<String, Integer> sCorks = new HashMap<>(); /** + * A map of cache keys that have been disabled in the local process. When a key is + * disabled locally, existing caches are disabled and the key is saved in this map. + * Future cache instances that use the same key will be disabled in their constructor. + */ + @GuardedBy("sCorkLock") + private static final HashSet<String> sDisabledKeys = new HashSet<>(); + + /** * Weakly references all cache objects in the current process, allowing us to iterate over * them all for purposes like issuing debug dumps and reacting to memory pressure. */ @GuardedBy("sCorkLock") - private static final WeakHashMap<PropertyInvalidatedCache, Void> sCaches = - new WeakHashMap<>(); + private static final WeakHashMap<PropertyInvalidatedCache, Void> sCaches = new WeakHashMap<>(); private final Object mLock = new Object(); @@ -348,6 +356,9 @@ public abstract class PropertyInvalidatedCache<Query, Result> { }; synchronized (sCorkLock) { sCaches.put(this, null); + if (sDisabledKeys.contains(mCacheName)) { + disableInstance(); + } } } @@ -372,6 +383,14 @@ public abstract class PropertyInvalidatedCache<Query, Result> { protected abstract Result recompute(Query query); /** + * Return true if the query should bypass the cache. The default behavior is to + * always use the cache but the method can be overridden for a specific class. + */ + protected boolean bypass(Query query) { + return false; + } + + /** * Determines if a pair of responses are considered equal. Used to determine whether * a cache is inadvertently returning stale results when VERIFY is set to true. */ @@ -414,7 +433,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> { /** * Disable the use of this cache in this process. */ - public final void disableLocal() { + public final void disableInstance() { synchronized (mLock) { mDisabled = true; clear(); @@ -422,6 +441,30 @@ public abstract class PropertyInvalidatedCache<Query, Result> { } /** + * Disable the local use of all caches with the same name. All currently registered caches + * using the key will be disabled now, and all future cache instances that use the key will be + * disabled in their constructor. + */ + public static final void disableLocal(@NonNull String name) { + synchronized (sCorkLock) { + sDisabledKeys.add(name); + for (PropertyInvalidatedCache cache : sCaches.keySet()) { + if (name.equals(cache.mCacheName)) { + cache.disableInstance(); + } + } + } + } + + /** + * Disable this cache in the current process, and all other caches that use the same + * property. + */ + public final void disableLocal() { + disableLocal(mCacheName); + } + + /** * Return whether the cache is disabled in this process. */ public final boolean isDisabledLocal() { @@ -435,8 +478,8 @@ public abstract class PropertyInvalidatedCache<Query, Result> { // Let access to mDisabled race: it's atomic anyway. long currentNonce = (!isDisabledLocal()) ? getCurrentNonce() : NONCE_DISABLED; for (;;) { - if (currentNonce == NONCE_DISABLED || currentNonce == NONCE_UNSET || - currentNonce == NONCE_CORKED) { + if (currentNonce == NONCE_DISABLED || currentNonce == NONCE_UNSET + || currentNonce == NONCE_CORKED || bypass(query)) { if (!mDisabled) { // Do not bother collecting statistics if the cache is // locally disabled. @@ -875,6 +918,15 @@ public abstract class PropertyInvalidatedCache<Query, Result> { } /** + * Report the disabled status of this cache instance. The return value does not + * reflect status of the property key. + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public boolean getDisabledState() { + return isDisabledLocal(); + } + + /** * Returns a list of caches alive at the current time. */ public static ArrayList<PropertyInvalidatedCache> getActiveCaches() { diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index 0ab3f2f4be46..7cb1d89aa954 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -697,6 +697,18 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * service element of manifest file. The value of attribute * {@link android.R.attr#foregroundServiceType} can be multiple flags ORed together.</p> * + * <div class="caution"> + * <p><strong>Note:</strong> + * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S}, + * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S} + * or higher are not allowed to start foreground services from the background. + * See + * <a href="{@docRoot}/about/versions/12/behavior-changes-12"> + * Behavior changes: Apps targeting Android 12 + * </a> + * for more details. + * </div> + * * @throws ForegroundServiceStartNotAllowedException * If the app targeting API is * {@link android.os.Build.VERSION_CODES#S} or later, and the service is restricted from @@ -733,6 +745,18 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST} to use all flags that * is specified in manifest attribute foregroundServiceType.</p> * + * <div class="caution"> + * <p><strong>Note:</strong> + * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S}, + * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S} + * or higher are not allowed to start foreground services from the background. + * See + * <a href="{@docRoot}/about/versions/12/behavior-changes-12"> + * Behavior changes: Apps targeting Android 12 + * </a> + * for more details. + * </div> + * * @param id The identifier for this notification as per * {@link NotificationManager#notify(int, Notification) * NotificationManager.notify(int, Notification)}; must not be 0. diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java index 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/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index b5b7dbdff172..8284cdde3d06 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -13896,7 +13896,11 @@ public class DevicePolicyManager { } /** - * Returns whether USB data signaling is currently enabled by the admin. Callable by any app. + * Returns whether USB data signaling is currently enabled. + * + * <p> When called by a device owner or profile owner of an organization-owned managed profile, + * this API returns whether USB data signaling is currently enabled by that admin. When called + * by any other app, returns whether USB data signaling is currently enabled on the device. * * @return {@code true} if USB data signaling is enabled, {@code false} otherwise. */ diff --git a/core/java/android/app/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/companion/OWNERS b/core/java/android/companion/OWNERS index da723b3b67da..54b35fc32f62 100644 --- a/core/java/android/companion/OWNERS +++ b/core/java/android/companion/OWNERS @@ -1 +1,4 @@ -eugenesusla@google.com
\ No newline at end of file +ewol@google.com +evanxinchen@google.com +guojing@google.com +svetoslavganov@google.com
\ No newline at end of file diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index ea0e321377c8..9c60f431b06e 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3182,7 +3182,8 @@ public abstract class Context { * <p>This function will throw {@link SecurityException} if you do not * have permission to start the given service. * - * <p class="note"><strong>Note:</strong> Each call to startService() + * <div class="caution"> + * <p><strong>Note:</strong> Each call to startService() * results in significant work done by the system to manage service * lifecycle surrounding the processing of the intent, which can take * multiple milliseconds of CPU time. Due to this cost, startService() @@ -3191,6 +3192,25 @@ public abstract class Context { * for high frequency calls. * </p> * + * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#O}, + * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#O} + * or higher are not allowed to start background services from the background. + * See + * <a href="{@docRoot}/about/versions/oreo/background"> + * Background Execution Limits</a> + * for more details. + * + * <p><strong>Note:</strong> + * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S}, + * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S} + * or higher are not allowed to start foreground services from the background. + * See + * <a href="{@docRoot}/about/versions/12/behavior-changes-12"> + * Behavior changes: Apps targeting Android 12 + * </a> + * for more details. + * </div> + * * @param service Identifies the service to be started. The Intent must be * fully explicit (supplying a component name). Additional values * may be included in the Intent extras to supply arguments along with @@ -3215,6 +3235,7 @@ public abstract class Context { * This excemption extends {@link IllegalStateException}, so apps can * use {@code catch (IllegalStateException)} to catch both. * + * @see #startForegroundService(Intent) * @see #stopService * @see #bindService */ @@ -3232,6 +3253,18 @@ public abstract class Context { * at any time, regardless of whether the app hosting the service is in a foreground * state. * + * <div class="caution"> + * <p><strong>Note:</strong> + * Beginning with SDK Version {@link android.os.Build.VERSION_CODES#S}, + * apps targeting SDK Version {@link android.os.Build.VERSION_CODES#S} + * or higher are not allowed to start foreground services from the background. + * See + * <a href="{@docRoot}/about/versions/12/behavior-changes-12"> + * Behavior changes: Apps targeting Android 12 + * </a> + * for more details. + * </div> + * * @param service Identifies the service to be started. The Intent must be * fully explicit (supplying a component name). Additional values * may be included in the Intent extras to supply arguments along with 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/PackagePartitions.java b/core/java/android/content/pm/PackagePartitions.java index 52ee4de5bed6..d1577684aac6 100644 --- a/core/java/android/content/pm/PackagePartitions.java +++ b/core/java/android/content/pm/PackagePartitions.java @@ -119,6 +119,9 @@ public class PackagePartitions { @Nullable private final DeferredCanonicalFile mOverlayFolder; + @NonNull + private final File mNonConicalFolder; + private SystemPartition(@NonNull File folder, @PartitionType int type, boolean containsPrivApp, boolean containsOverlay) { this.type = type; @@ -128,6 +131,7 @@ public class PackagePartitions { : null; this.mOverlayFolder = containsOverlay ? new DeferredCanonicalFile(folder, "overlay") : null; + this.mNonConicalFolder = folder; } public SystemPartition(@NonNull SystemPartition original) { @@ -136,6 +140,7 @@ public class PackagePartitions { this.mAppFolder = original.mAppFolder; this.mPrivAppFolder = original.mPrivAppFolder; this.mOverlayFolder = original.mOverlayFolder; + this.mNonConicalFolder = original.mNonConicalFolder; } /** @@ -153,6 +158,12 @@ public class PackagePartitions { return mFolder.getFile(); } + /** Returns the non-canonical folder of the partition. */ + @NonNull + public File getNonConicalFolder() { + return mNonConicalFolder; + } + /** Returns the canonical app folder of the partition. */ @Nullable public File getAppFolder() { diff --git a/core/java/android/content/pm/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/content/pm/parsing/component/ParsedIntentInfo.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java index 6b797bcb6ab2..463a1810b511 100644 --- a/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java +++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfo.java @@ -19,7 +19,6 @@ package android.content.pm.parsing.component; import android.annotation.Nullable; import android.content.IntentFilter; import android.os.Parcel; -import android.os.Parcelable; import android.util.Pair; import com.android.internal.util.Parcelling; @@ -167,19 +166,6 @@ public final class ParsedIntentInfo extends IntentFilter { + '}'; } - public static final Parcelable.Creator<ParsedIntentInfo> CREATOR = - new Parcelable.Creator<ParsedIntentInfo>() { - @Override - public ParsedIntentInfo createFromParcel(Parcel source) { - return new ParsedIntentInfo(source); - } - - @Override - public ParsedIntentInfo[] newArray(int size) { - return new ParsedIntentInfo[size]; - } - }; - public boolean isHasDefault() { return hasDefault; } diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index 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 79f716cd5ce9..e665d0fcc836 100644 --- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java @@ -60,7 +60,8 @@ public interface BiometricFingerprintConstants { BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED, BIOMETRIC_ERROR_RE_ENROLL, BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED, - FINGERPRINT_ERROR_UNKNOWN}) + FINGERPRINT_ERROR_UNKNOWN, + FINGERPRINT_ERROR_BAD_CALIBARTION}) @Retention(RetentionPolicy.SOURCE) @interface FingerprintError {} @@ -181,6 +182,12 @@ public interface BiometricFingerprintConstants { int FINGERPRINT_ERROR_UNKNOWN = 17; /** + * Error indicating that the fingerprint sensor has bad calibration. + * @hide + */ + int FINGERPRINT_ERROR_BAD_CALIBARTION = 18; + + /** * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -201,7 +208,8 @@ 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 {} @@ -271,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 e5946663df47..0819835f5fb5 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -1375,6 +1375,9 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing case BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED: return context.getString( com.android.internal.R.string.fingerprint_error_security_update_required); + case FINGERPRINT_ERROR_BAD_CALIBARTION: + context.getString( + com.android.internal.R.string.fingerprint_error_bad_calibration); case FINGERPRINT_ERROR_VENDOR: { String[] msgArray = context.getResources().getStringArray( com.android.internal.R.array.fingerprint_error_vendor); @@ -1414,6 +1417,9 @@ 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/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index 77f8a87cdcd1..f48375246616 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -306,14 +306,23 @@ public final class BatteryUsageStats implements Parcelable { AggregateBatteryConsumer.CREATOR.createFromParcel(source); mAggregateBatteryConsumers[i].setCustomPowerComponentNames(mCustomPowerComponentNames); } - int uidCount = source.readInt(); + + // UidBatteryConsumers are included as a blob to avoid a TransactionTooLargeException + final Parcel blob = Parcel.obtain(); + final byte[] bytes = source.readBlob(); + blob.unmarshall(bytes, 0, bytes.length); + blob.setDataPosition(0); + + final int uidCount = blob.readInt(); mUidBatteryConsumers = new ArrayList<>(uidCount); for (int i = 0; i < uidCount; i++) { final UidBatteryConsumer consumer = - UidBatteryConsumer.CREATOR.createFromParcel(source); + UidBatteryConsumer.CREATOR.createFromParcel(blob); consumer.setCustomPowerComponentNames(mCustomPowerComponentNames); mUidBatteryConsumers.add(consumer); } + blob.recycle(); + int userCount = source.readInt(); mUserBatteryConsumers = new ArrayList<>(userCount); for (int i = 0; i < userCount; i++) { @@ -323,14 +332,10 @@ public final class BatteryUsageStats implements Parcelable { mUserBatteryConsumers.add(consumer); } if (source.readBoolean()) { - mHistoryBuffer = Parcel.obtain(); - mHistoryBuffer.setDataSize(0); - mHistoryBuffer.setDataPosition(0); + final byte[] historyBlob = source.readBlob(); - int historyBufferSize = source.readInt(); - int curPos = source.dataPosition(); - mHistoryBuffer.appendFrom(source, curPos, historyBufferSize); - source.setDataPosition(curPos + historyBufferSize); + mHistoryBuffer = Parcel.obtain(); + mHistoryBuffer.unmarshall(historyBlob, 0, historyBlob.length); int historyTagCount = source.readInt(); mHistoryTagPool = new ArrayList<>(historyTagCount); @@ -362,21 +367,26 @@ public final class BatteryUsageStats implements Parcelable { for (int i = 0; i < AGGREGATE_BATTERY_CONSUMER_SCOPE_COUNT; i++) { mAggregateBatteryConsumers[i].writeToParcel(dest, flags); } - dest.writeInt(mUidBatteryConsumers.size()); + + // UidBatteryConsumers are included as a blob, because each UidBatteryConsumer + // takes > 300 bytes, so a typical number of UIDs in the system, 300 would result + // in a 90 kB Parcel, which is not safe to pass in a binder transaction because + // of the possibility of TransactionTooLargeException + final Parcel blob = Parcel.obtain(); + blob.writeInt(mUidBatteryConsumers.size()); for (int i = mUidBatteryConsumers.size() - 1; i >= 0; i--) { - mUidBatteryConsumers.get(i).writeToParcel(dest, flags); + mUidBatteryConsumers.get(i).writeToParcel(blob, flags); } + dest.writeBlob(blob.marshall()); + blob.recycle(); + dest.writeInt(mUserBatteryConsumers.size()); for (int i = mUserBatteryConsumers.size() - 1; i >= 0; i--) { mUserBatteryConsumers.get(i).writeToParcel(dest, flags); } if (mHistoryBuffer != null) { dest.writeBoolean(true); - - final int historyBufferSize = mHistoryBuffer.dataSize(); - dest.writeInt(historyBufferSize); - dest.appendFrom(mHistoryBuffer, 0, historyBufferSize); - + dest.writeBlob(mHistoryBuffer.marshall()); dest.writeInt(mHistoryTagPool.size()); for (int i = mHistoryTagPool.size() - 1; i >= 0; i--) { final BatteryStats.HistoryTag tag = mHistoryTagPool.get(i); 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/translation/TranslationService.java b/core/java/android/service/translation/TranslationService.java index e1d4a5656b39..93c006aff435 100644 --- a/core/java/android/service/translation/TranslationService.java +++ b/core/java/android/service/translation/TranslationService.java @@ -361,6 +361,11 @@ public abstract class TranslationService extends Service { new Consumer<Set<TranslationCapability>>() { @Override public void accept(Set<TranslationCapability> values) { + if (!isValidCapabilities(sourceFormat, targetFormat, values)) { + throw new IllegalStateException("Invalid capabilities and " + + "format compatibility"); + } + final ArraySet<TranslationCapability> capabilities = new ArraySet<>(values); final Bundle bundle = new Bundle(); bundle.putParcelableArray(TranslationManager.EXTRA_CAPABILITIES, @@ -369,4 +374,23 @@ public abstract class TranslationService extends Service { } }); } + + /** + * Helper method to validate capabilities and format compatibility. + */ + private boolean isValidCapabilities(@TranslationSpec.DataFormat int sourceFormat, + @TranslationSpec.DataFormat int targetFormat, Set<TranslationCapability> capabilities) { + if (sourceFormat != TranslationSpec.DATA_FORMAT_TEXT + && targetFormat != TranslationSpec.DATA_FORMAT_TEXT) { + return true; + } + + for (TranslationCapability capability : capabilities) { + if (capability.getState() == TranslationCapability.STATE_REMOVED_AND_AVAILABLE) { + return false; + } + } + + return true; + } } diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 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/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java index 85ff93bcc5e1..1506ee4c2c7a 100644 --- a/core/java/android/view/InsetsSourceControl.java +++ b/core/java/android/view/InsetsSourceControl.java @@ -48,6 +48,7 @@ public class InsetsSourceControl implements Parcelable { private Insets mInsetsHint; private boolean mSkipAnimationOnce; + private int mParcelableFlags; public InsetsSourceControl(@InternalInsetsType int type, @Nullable SurfaceControl leash, Point surfacePosition, Insets insetsHint) { @@ -132,6 +133,10 @@ public class InsetsSourceControl implements Parcelable { return result; } + public void setParcelableFlags(int parcelableFlags) { + mParcelableFlags = parcelableFlags; + } + @Override public int describeContents() { return 0; @@ -140,9 +145,9 @@ public class InsetsSourceControl implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mType); - dest.writeTypedObject(mLeash, 0 /* parcelableFlags */); - dest.writeTypedObject(mSurfacePosition, 0 /* parcelableFlags */); - dest.writeTypedObject(mInsetsHint, 0 /* parcelableFlags */); + dest.writeTypedObject(mLeash, mParcelableFlags); + dest.writeTypedObject(mSurfacePosition, mParcelableFlags); + dest.writeTypedObject(mInsetsHint, mParcelableFlags); dest.writeBoolean(mSkipAnimationOnce); } diff --git a/core/java/android/view/ScrollCaptureTarget.java b/core/java/android/view/ScrollCaptureTarget.java index 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..c03db6d67356 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 { @@ -2604,16 +2610,6 @@ public final class SurfaceControl implements Parcelable { = sRegistry.registerNativeAllocation(this, mNativeObject); } - /** - * Create a transaction object that wraps a native peer. - * @hide - */ - Transaction(long nativeObject) { - mNativeObject = nativeObject; - mFreeNativeResources = - sRegistry.registerNativeAllocation(this, mNativeObject); - } - private Transaction(Parcel in) { readFromParcel(in); } @@ -3602,4 +3598,27 @@ 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..5a248af7a097 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1371,8 +1371,12 @@ public final class ViewRootImpl implements ViewParent, HardwareRenderer.ASurfaceTransactionCallback callback = (nativeTransactionObj, nativeSurfaceControlObj, frameNr) -> { - Transaction t = new Transaction(nativeTransactionObj); - mergeWithNextTransaction(t, frameNr); + if (mBlastBufferQueue == null) { + return false; + } else { + mBlastBufferQueue.mergeWithNextTransaction(nativeTransactionObj, frameNr); + return true; + } }; mAttachInfo.mThreadedRenderer.setASurfaceTransactionCallback(callback); } @@ -5290,7 +5294,16 @@ public final class ViewRootImpl implements ViewParent, // b) When loosing control, controller can restore server state by taking last // dispatched state as truth. mInsetsController.onStateChanged((InsetsState) args.arg1); - mInsetsController.onControlsChanged((InsetsSourceControl[]) args.arg2); + InsetsSourceControl[] controls = (InsetsSourceControl[]) args.arg2; + if (mAdded) { + mInsetsController.onControlsChanged(controls); + } else if (controls != null) { + for (InsetsSourceControl control : controls) { + if (control != null) { + control.release(SurfaceControl::release); + } + } + } args.recycle(); break; } @@ -8136,6 +8149,10 @@ public final class ViewRootImpl implements ViewParent, } } + // If our window is removed, we might not get notified about losing control. + // Invoking this can release the leashes as soon as possible instead of relying on GC. + mInsetsController.onControlsChanged(null); + mAdded = false; } WindowManagerGlobal.getInstance().doRemoveView(this); @@ -10382,4 +10399,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/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index c6332686bf7f..cc47f09d4e8d 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -341,11 +341,7 @@ public abstract class ContentCaptureSession implements AutoCloseable { } } - try { - flush(FLUSH_REASON_SESSION_FINISHED); - } finally { - onDestroy(); - } + onDestroy(); } abstract void onDestroy(); diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index d8ac779ddc27..7ec9d34bd364 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -263,7 +263,13 @@ public final class MainContentCaptureSession extends ContentCaptureSession { @Override void onDestroy() { mHandler.removeMessages(MSG_FLUSH); - mHandler.post(() -> destroySession()); + mHandler.post(() -> { + try { + flush(FLUSH_REASON_SESSION_FINISHED); + } finally { + destroySession(); + } + }); } /** @@ -362,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) { @@ -571,9 +580,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession { private ParceledListSlice<ContentCaptureEvent> clearEvents() { // NOTE: we must save a reference to the current mEvents and then set it to to null, // otherwise clearing it would clear it in the receiving side if the service is also local. - final List<ContentCaptureEvent> events = mEvents == null - ? Collections.EMPTY_LIST - : new ArrayList<>(mEvents); + if (mEvents == null) { + return new ParceledListSlice<>(Collections.EMPTY_LIST); + } + + final List<ContentCaptureEvent> events = new ArrayList<>(mEvents); mEvents.clear(); return new ParceledListSlice<>(events); } diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 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/TranslationCapability.java b/core/java/android/view/translation/TranslationCapability.java index d104b2427c8e..65b749add1b2 100644 --- a/core/java/android/view/translation/TranslationCapability.java +++ b/core/java/android/view/translation/TranslationCapability.java @@ -61,6 +61,13 @@ public final class TranslationCapability implements Parcelable { * was dropped.</p> */ public static final @ModelState int STATE_NOT_AVAILABLE = 4; + /** + * The translation between the source and target specs were removed from the system, but is + * still available to be downloaded again. + * + * @hide + */ + public static final @ModelState int STATE_REMOVED_AND_AVAILABLE = 1000; /** * The state of translation readiness between {@code mSourceSpec} and {@code mTargetSpec}. @@ -134,7 +141,8 @@ public final class TranslationCapability implements Parcelable { STATE_AVAILABLE_TO_DOWNLOAD, STATE_DOWNLOADING, STATE_ON_DEVICE, - STATE_NOT_AVAILABLE + STATE_NOT_AVAILABLE, + STATE_REMOVED_AND_AVAILABLE }) @Retention(RetentionPolicy.SOURCE) @DataClass.Generated.Member @@ -152,6 +160,8 @@ public final class TranslationCapability implements Parcelable { return "STATE_ON_DEVICE"; case STATE_NOT_AVAILABLE: return "STATE_NOT_AVAILABLE"; + case STATE_REMOVED_AND_AVAILABLE: + return "STATE_REMOVED_AND_AVAILABLE"; default: return Integer.toHexString(value); } } @@ -255,13 +265,15 @@ public final class TranslationCapability implements Parcelable { if (!(mState == STATE_AVAILABLE_TO_DOWNLOAD) && !(mState == STATE_DOWNLOADING) && !(mState == STATE_ON_DEVICE) - && !(mState == STATE_NOT_AVAILABLE)) { + && !(mState == STATE_NOT_AVAILABLE) + && !(mState == STATE_REMOVED_AND_AVAILABLE)) { throw new java.lang.IllegalArgumentException( "state was " + mState + " but must be one of: " + "STATE_AVAILABLE_TO_DOWNLOAD(" + STATE_AVAILABLE_TO_DOWNLOAD + "), " + "STATE_DOWNLOADING(" + STATE_DOWNLOADING + "), " + "STATE_ON_DEVICE(" + STATE_ON_DEVICE + "), " - + "STATE_NOT_AVAILABLE(" + STATE_NOT_AVAILABLE + ")"); + + "STATE_NOT_AVAILABLE(" + STATE_NOT_AVAILABLE + "), " + + "STATE_REMOVED_AND_AVAILABLE(" + STATE_REMOVED_AND_AVAILABLE + ")"); } this.mSourceSpec = sourceSpec; @@ -293,10 +305,10 @@ public final class TranslationCapability implements Parcelable { }; @DataClass.Generated( - time = 1621545303074L, + time = 1624307114468L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/view/translation/TranslationCapability.java", - inputSignatures = "public static final @android.view.translation.TranslationCapability.ModelState int STATE_AVAILABLE_TO_DOWNLOAD\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_DOWNLOADING\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_ON_DEVICE\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_NOT_AVAILABLE\nprivate final @android.view.translation.TranslationCapability.ModelState int mState\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mTargetSpec\nprivate final boolean mUiTranslationEnabled\nprivate final @android.view.translation.TranslationContext.TranslationFlag int mSupportedTranslationFlags\nclass TranslationCapability extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstDefs=true, genToString=true, genConstructor=false)") + inputSignatures = "public static final @android.view.translation.TranslationCapability.ModelState int STATE_AVAILABLE_TO_DOWNLOAD\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_DOWNLOADING\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_ON_DEVICE\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_NOT_AVAILABLE\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_REMOVED_AND_AVAILABLE\nprivate final @android.view.translation.TranslationCapability.ModelState int mState\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mTargetSpec\nprivate final boolean mUiTranslationEnabled\nprivate final @android.view.translation.TranslationContext.TranslationFlag int mSupportedTranslationFlags\nclass TranslationCapability extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstDefs=true, genToString=true, genConstructor=false)") @Deprecated private void __metadata() {} diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java index 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/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 08db74f5e351..7000ed75d5be 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -464,8 +464,8 @@ public class ChooserActivity extends ResolverActivity implements private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED = 5; private static final int LIST_VIEW_UPDATE_MESSAGE = 6; - private static final int WATCHDOG_TIMEOUT_MAX_MILLIS = 10000; - private static final int WATCHDOG_TIMEOUT_MIN_MILLIS = 3000; + private static final int WATCHDOG_TIMEOUT_MAX_MILLIS = 1000; + private static final int WATCHDOG_TIMEOUT_MIN_MILLIS = 300; private boolean mMinTimeoutPassed = false; diff --git a/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl b/core/java/com/android/internal/app/IHotwordRecognitionStatusCallback.aidl index 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/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java index b38f623e3a6d..3f3c9bdbe5f5 100644 --- a/core/java/com/android/internal/content/om/OverlayConfig.java +++ b/core/java/com/android/internal/content/om/OverlayConfig.java @@ -111,7 +111,7 @@ public class OverlayConfig { // Rebase the system partitions and settings file on the specified root directory. partitions = new ArrayList<>(PackagePartitions.getOrderedPartitions( p -> new OverlayPartition( - new File(rootDirectory, p.getFolder().getPath()), + new File(rootDirectory, p.getNonConicalFolder().getPath()), p))); } diff --git a/core/java/com/android/internal/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/drawable/chooser_direct_share_icon_placeholder.xml b/core/res/res/drawable/chooser_direct_share_icon_placeholder.xml index 838cb49ca79e..bd8dba866dd2 100644 --- a/core/res/res/drawable/chooser_direct_share_icon_placeholder.xml +++ b/core/res/res/drawable/chooser_direct_share_icon_placeholder.xml @@ -26,7 +26,7 @@ <group android:name="background"> <path android:pathData="M0,0 L 64,0 64,64 0,64 z" - android:fillColor="@color/chooser_gradient_background"/> + android:fillColor="@android:color/transparent"/> </group> <!-- Gradient starts offscreen so it is not visible in the first frame before start --> @@ -44,7 +44,7 @@ android:color="@android:color/transparent" android:offset="0.0" /> <item - android:color="@color/chooser_gradient_highlight" + android:color="@android:color/transparent" android:offset="0.5" /> <item android:color="@android:color/transparent" @@ -58,7 +58,7 @@ shadow. Using clip-path is a more elegant solution but leaves awful jaggies around the path's shape. --> <group android:name="cover"> - <path android:fillColor="?attr/colorBackgroundFloating" + <path android:fillColor="@android:color/transparent" android:pathData="M0,0 L64,0 L64,64 L0,64 L0,0 Z M59.0587325,42.453601 C60.3124932,39.2104785 61,35.6855272 61,32 C61,15.9837423 48.0162577,3 32,3 C15.9837423,3 3,15.9837423 3,32 C3,48.0162577 15.9837423,61 32,61 C35.6855272,61 39.2104785,60.3124932 42.453601,59.0587325 C44.3362195,60.2864794 46.5847839,61 49,61 C55.627417,61 61,55.627417 61,49 C61,46.5847839 60.2864794,44.3362195 59.0587325,42.453601 Z"/> </group> </vector> diff --git a/core/res/res/layout/splash_screen_view.xml b/core/res/res/layout/splash_screen_view.xml index 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 b6e60d7f3cfb..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> @@ -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-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..67d29b5ee2b7 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1706,6 +1706,10 @@ config_enableFusedLocationOverlay is false. --> <string name="config_fusedLocationProviderPackageName" translatable="false">com.android.location.fused</string> + <!-- Default value for the ADAS GNSS Location Enabled setting if this setting has never been + set before. --> + <bool name="config_defaultAdasGnssLocationEnabled" translatable="false">false</bool> + <string-array name="config_locationExtraPackageNames" translatable="false"></string-array> <!-- The package name of the default network recommendation app. @@ -1890,6 +1894,9 @@ STREAM_MUSIC as if it's on TV platform. --> <bool name="config_single_volume">false</bool> + <!-- Flag indicating whether the volume panel should show remote sessions. --> + <bool name="config_volumeShowRemoteSessions">true</bool> + <!-- Flag indicating that an outbound call must have a call capable phone account that has declared it can process the call's handle. --> <bool name="config_requireCallCapableAccountForHandle">false</bool> @@ -3350,6 +3357,10 @@ <!-- The default vibration strength, must be between 1 and 255 inclusive. --> <integer name="config_defaultVibrationAmplitude">255</integer> + <!-- The max vibration strength allowed in audio haptic channels, must be positive or zero if + limit is unknown. --> + <item name="config_hapticChannelMaxVibrationAmplitude" format="float" type="dimen">0</item> + <!-- If the device should still vibrate even in low power mode, for certain priority vibrations (e.g. accessibility, alarms). This is mainly for Wear devices that don't have speakers. --> <bool name="config_allowPriorityVibrationsInLowPowerMode">false</bool> @@ -4624,6 +4635,15 @@ <!-- The package name for the default bug report handler app from power menu short press. This app must be allowlisted. --> <string name="config_defaultBugReportHandlerApp" translatable="false"></string> + <!-- When true, enables the allowlisted app to upload profcollect reports. --> + <bool name="config_profcollectReportUploaderEnabled">false</bool> + + <!-- The package name for the default profcollect report uploader app. This app must be allowlisted. --> + <string name="config_defaultProfcollectReportUploaderApp" translatable="false"></string> + + <!-- The action name for the default profcollect report uploader app. --> + <string name="config_defaultProfcollectReportUploaderAction" translatable="false"></string> + <!-- The default value used for RawContacts.ACCOUNT_NAME when contacts are inserted without this column set. These contacts are stored locally on the device and will not be removed even if no android.account.Account with this name exists. A null string will be used if the @@ -5003,6 +5023,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 bdeff89352ea..bcd121a2caa6 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1603,6 +1603,8 @@ <string name="fingerprint_acquired_too_bright">Too bright</string> <!-- Message shown during fingerprint acquisition when a fingerprint must be adjusted.[CHAR LIMIT=50] --> <string name="fingerprint_acquired_try_adjusting">Try adjusting</string> + <!-- Message shown during fingerprint acquisition when a fingeprint area has already been captured during enrollment [CHAR LIMIT=100] --> + <string name="fingerprint_acquired_immobile">Change the position of your finger slightly each time</string> <!-- Array containing custom messages shown during fingerprint acquisision from vendor. Vendor is expected to add and translate these strings --> <string-array name="fingerprint_acquired_vendor"> </string-array> @@ -1636,6 +1638,8 @@ <string name="fingerprint_error_hw_not_present">This device does not have a fingerprint sensor.</string> <!-- Generic error message shown when fingerprint is not available due to a security vulnerability. [CHAR LIMIT=50] --> <string name="fingerprint_error_security_update_required">Sensor temporarily disabled.</string> + <!-- Generic error message shown when fingerprint needs calibration [CHAR LIMIT=50] --> + <string name="fingerprint_error_bad_calibration">Sensor needs calibration</string> <!-- Template to be used to name enrolled fingerprints by default. --> <string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string> @@ -1659,9 +1663,9 @@ <!-- Notification name shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] --> <string name="face_recalibrate_notification_name">Face Unlock</string> <!-- Notification title shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] --> - <string name="face_recalibrate_notification_title">Re-enroll your face</string> + <string name="face_recalibrate_notification_title">Issue with Face Unlock</string> <!-- Notification content shown when the system requires the user to re-enroll their face. [CHAR LIMIT=NONE] --> - <string name="face_recalibrate_notification_content">To improve recognition, please re-enroll your face</string> + <string name="face_recalibrate_notification_content">Tap to delete your face model, then add your face again</string> <!-- Title of a notification that directs the user to set up Face Unlock by enrolling their face. [CHAR LIMIT=NONE] --> <string name="face_setup_notification_title">Set up Face Unlock</string> <!-- Contents of a notification that directs the user to set up face unlock by enrolling their face. [CHAR LIMIT=NONE] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 97a2a38bdab8..ea2821526768 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" /> @@ -2537,6 +2539,8 @@ <java-symbol type="string" name="fingerprint_error_no_fingerprints" /> <java-symbol type="string" name="fingerprint_error_hw_not_present" /> <java-symbol type="string" name="fingerprint_error_security_update_required" /> + <java-symbol type="string" name="fingerprint_error_bad_calibration" /> + <java-symbol type="string" name="fingerprint_acquired_immobile" /> <!-- Fingerprint config --> <java-symbol type="integer" name="config_fingerprintMaxTemplatesPerUser"/> @@ -4015,6 +4019,11 @@ <java-symbol type="bool" name="config_bugReportHandlerEnabled" /> <java-symbol type="string" name="config_defaultBugReportHandlerApp" /> + <!-- For profcollect report uploader --> + <java-symbol type="bool" name="config_profcollectReportUploaderEnabled" /> + <java-symbol type="string" name="config_defaultProfcollectReportUploaderApp" /> + <java-symbol type="string" name="config_defaultProfcollectReportUploaderAction" /> + <java-symbol type="string" name="usb_device_resolve_prompt_warn" /> <!-- For Accessibility system actions --> @@ -4401,5 +4410,9 @@ <java-symbol type="bool" name="config_assistLongPressHomeEnabledDefault" /> <java-symbol type="bool" name="config_assistTouchGestureEnabledDefault" /> + <java-symbol type="integer" name="config_hotwordDetectedResultMaxBundleSize" /> + <java-symbol type="dimen" name="config_wallpaperDimAmount" /> + + <java-symbol type="bool" name="config_volumeShowRemoteSessions" /> </resources> diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java new file mode 100644 index 000000000000..8c05978ad03c --- /dev/null +++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import static org.junit.Assert.assertEquals; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +/** + * Test for verifying the behavior of {@link PropertyInvalidatedCache}. This test does + * not use any actual binder calls - it is entirely self-contained. + * <p> + * Build/Install/Run: + * atest FrameworksCoreTests:PropertyInvalidatedCacheTests + */ +@SmallTest +public class PropertyInvalidatedCacheTests { + + static final String CACHE_PROPERTY = "cache_key.cache_test_a"; + + // This class is a proxy for binder calls. It contains a counter that increments + // every time the class is queried. + private static class ServerProxy { + // The number of times this class was queried. + private int mCount = 0; + + // A single query. The key behavior is that the query count is incremented. + boolean query(int x) { + mCount++; + return value(x); + } + + // Return the expected value of an input, without incrementing the query count. + boolean value(int x) { + return x % 3 == 0; + } + + // Verify the count. + void verify(int x) { + assertEquals(x, mCount); + } + } + + @Test + public void testDisableCache1() { + + // A stand-in for the binder. The test verifies that calls are passed through to + // this class properly. + ServerProxy tester = new ServerProxy(); + + // Three caches, all using the same system property but one uses a different name. + PropertyInvalidatedCache<Integer, Boolean> cache1 = + new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) { + @Override + protected Boolean recompute(Integer x) { + return tester.query(x); + } + }; + PropertyInvalidatedCache<Integer, Boolean> cache2 = + new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) { + @Override + protected Boolean recompute(Integer x) { + return tester.query(x); + } + }; + PropertyInvalidatedCache<Integer, Boolean> cache3 = + new PropertyInvalidatedCache<>(4, CACHE_PROPERTY, "cache3") { + @Override + protected Boolean recompute(Integer x) { + return tester.query(x); + } + }; + + // Caches are enabled upon creation. + assertEquals(false, cache1.getDisabledState()); + assertEquals(false, cache2.getDisabledState()); + assertEquals(false, cache3.getDisabledState()); + + // Disable the cache1 instance. Only cache1 is disabled + cache1.disableInstance(); + assertEquals(true, cache1.getDisabledState()); + assertEquals(false, cache2.getDisabledState()); + assertEquals(false, cache3.getDisabledState()); + + // Disable cache1. This will disable cache1 and cache2 because they share the + // same name. cache3 has a different name and will not be disabled. + cache1.disableLocal(); + assertEquals(true, cache1.getDisabledState()); + assertEquals(true, cache2.getDisabledState()); + assertEquals(false, cache3.getDisabledState()); + + // Create a new cache1. Verify that the new instance is disabled. + cache1 = new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) { + @Override + protected Boolean recompute(Integer x) { + return tester.query(x); + } + }; + assertEquals(true, cache1.getDisabledState()); + } +} diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java index 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/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java index 3e620c2bbec6..fedbf7a9868e 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java @@ -24,6 +24,8 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.os.BatteryConsumer; import android.os.BatteryUsageStats; @@ -47,7 +49,9 @@ import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; @SmallTest @RunWith(AndroidJUnit4.class) @@ -64,13 +68,15 @@ public class BatteryUsageStatsTest { } @Test - public void testParcelability() { + public void testParcelability_smallNumberOfUids() { final BatteryUsageStats outBatteryUsageStats = buildBatteryUsageStats1(true).build(); final Parcel outParcel = Parcel.obtain(); outParcel.writeParcelable(outBatteryUsageStats, 0); final byte[] bytes = outParcel.marshall(); outParcel.recycle(); + assertThat(bytes.length).isLessThan(2000); + final Parcel inParcel = Parcel.obtain(); inParcel.unmarshall(bytes, 0, bytes.length); inParcel.setDataPosition(0); @@ -80,6 +86,46 @@ public class BatteryUsageStatsTest { assertBatteryUsageStats1(inBatteryUsageStats, true); } + @Test + public void testParcelability_largeNumberOfUids() { + final BatteryUsageStats.Builder builder = + new BatteryUsageStats.Builder(new String[0]); + + // Without the use of a blob, this BatteryUsageStats object would generate a Parcel + // larger than 64 Kb + final int uidCount = 200; + for (int i = 0; i < uidCount; i++) { + BatteryStatsImpl.Uid mockUid = mock(BatteryStatsImpl.Uid.class); + when(mockUid.getUid()).thenReturn(i); + builder.getOrCreateUidBatteryConsumerBuilder(mockUid) + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN, i * 100); + } + + BatteryUsageStats outBatteryUsageStats = builder.build(); + + final Parcel parcel = Parcel.obtain(); + parcel.writeParcelable(outBatteryUsageStats, 0); + + assertThat(parcel.dataSize()).isLessThan(2000); + + // This parcel cannot be marshaled because it contains a file descriptor. + // Assuming that parcel marshaling works fine, let's just rewind the parcel. + parcel.setDataPosition(0); + + final BatteryUsageStats inBatteryUsageStats = + parcel.readParcelable(getClass().getClassLoader()); + parcel.recycle(); + + assertThat(inBatteryUsageStats.getUidBatteryConsumers()).hasSize(uidCount); + final Map<Integer, UidBatteryConsumer> consumersByUid = + inBatteryUsageStats.getUidBatteryConsumers().stream().collect( + Collectors.toMap(UidBatteryConsumer::getUid, c -> c)); + for (int i = 0; i < uidCount; i++) { + final UidBatteryConsumer uidBatteryConsumer = consumersByUid.get(i); + assertThat(uidBatteryConsumer).isNotNull(); + assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(i * 100); + } + } @Test public void testDefaultSessionDuration() { diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java index 4534d36342db..6c1c2ee1ee57 100644 --- a/graphics/java/android/graphics/BLASTBufferQueue.java +++ b/graphics/java/android/graphics/BLASTBufferQueue.java @@ -131,4 +131,12 @@ public final class BLASTBufferQueue { nativeMergeWithNextTransaction(mNativeObject, t.mNativeObject, frameNumber); } + /** + * Merge the transaction passed in to the next transaction in BlastBufferQueue. + * @param nativeTransaction native handle passed from native c/c++ code. + */ + public void mergeWithNextTransaction(long nativeTransaction, long frameNumber) { + nativeMergeWithNextTransaction(mNativeObject, nativeTransaction, frameNumber); + } + } diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index e141d5178570..30d1e0fdb9d8 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -912,7 +912,7 @@ public class HardwareRenderer { * @param aSurfaceControlNativeObj ASurfaceControl native object handle * @param frame The id of the frame being drawn. */ - void onMergeTransaction(long aSurfaceTranactionNativeObj, + boolean onMergeTransaction(long aSurfaceTranactionNativeObj, long aSurfaceControlNativeObj, long frame); } diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index 8aba87ba3c8f..b994ad20320b 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -184,7 +184,6 @@ public class RippleDrawable extends LayerDrawable { private PorterDuffColorFilter mMaskColorFilter; private PorterDuffColorFilter mFocusColorFilter; private boolean mHasValidMask; - private int mComputedRadius = -1; /** The current ripple. May be actively animating or pending entry. */ private RippleForeground mRipple; @@ -390,8 +389,6 @@ public class RippleDrawable extends LayerDrawable { if (mRipple != null) { mRipple.onBoundsChange(); } - - mComputedRadius = Math.round(computeRadius()); invalidateSelf(); } @@ -750,7 +747,7 @@ public class RippleDrawable extends LayerDrawable { if (mBackground != null) { mBackground.onHotspotBoundsChanged(); } - float newRadius = Math.round(computeRadius()); + float newRadius = Math.round(getComputedRadius()); for (int i = 0; i < mRunningAnimations.size(); i++) { RippleAnimationSession s = mRunningAnimations.get(i); s.setRadius(newRadius); @@ -939,14 +936,13 @@ public class RippleDrawable extends LayerDrawable { } private float computeRadius() { - Rect b = getDirtyBounds(); - float radius = (float) Math.sqrt(b.width() * b.width() + b.height() * b.height()) / 2; - return radius; + final float halfWidth = mHotspotBounds.width() / 2.0f; + final float halfHeight = mHotspotBounds.height() / 2.0f; + return (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight); } private int getComputedRadius() { if (mState.mMaxRadius >= 0) return mState.mMaxRadius; - if (mComputedRadius >= 0) return mComputedRadius; return (int) computeRadius(); } 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/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java index b2ac61cf3f6e..cb27ad9f58f5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java @@ -249,6 +249,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged !activeControl.getSurfacePosition().equals(lastSurfacePosition); final boolean leashChanged = !haveSameLeash(mImeSourceControl, activeControl); + final InsetsSourceControl lastImeControl = mImeSourceControl; mImeSourceControl = activeControl; if (mAnimation != null) { if (positionChanged) { @@ -262,6 +263,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged removeImeSurface(); } } + if (lastImeControl != null) { + lastImeControl.release(SurfaceControl::release); + } } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java index a525c2c0219c..3253bb06c835 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java @@ -87,4 +87,9 @@ public interface OneHanded { * Notifies when user switch complete */ void onUserSwitch(int userId); + + /** + * Notifies when keyguard visibility changed + */ + void onKeyguardVisibilityChanged(boolean showing); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index ab92faa2e53d..bf753727a7ee 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 @@ -81,6 +81,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> private volatile boolean mIsSwipeToNotificationEnabled; private boolean mTaskChangeToExit; private boolean mLockedDisabled; + private boolean mKeyguardShowing; private int mUserId; private float mOffSetFraction; @@ -359,7 +360,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> @VisibleForTesting void startOneHanded() { - if (isLockedDisabled()) { + if (isLockedDisabled() || mKeyguardShowing) { Slog.d(TAG, "Temporary lock disabled"); return; } @@ -483,7 +484,9 @@ public class OneHandedController implements RemoteCallable<OneHandedController> @VisibleForTesting void notifyExpandNotification() { - mMainExecutor.execute(() -> mEventCallback.notifyExpandNotification()); + if (mEventCallback != null) { + mMainExecutor.execute(() -> mEventCallback.notifyExpandNotification()); + } } @VisibleForTesting @@ -698,6 +701,10 @@ public class OneHandedController implements RemoteCallable<OneHandedController> mTutorialHandler.onConfigurationChanged(); } + private void onKeyguardVisibilityChanged(boolean showing) { + mKeyguardShowing = showing; + } + private void onUserSwitch(int newUserId) { unregisterSettingObservers(); mUserId = newUserId; @@ -844,6 +851,13 @@ public class OneHandedController implements RemoteCallable<OneHandedController> OneHandedController.this.onUserSwitch(userId); }); } + + @Override + public void onKeyguardVisibilityChanged(boolean showing) { + mMainExecutor.execute(() -> { + OneHandedController.this.onKeyguardVisibilityChanged(showing); + }); + } } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 6451b94caaba..324a6e27a242 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -835,11 +835,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, WindowContainerTransaction wct) { // note that this can be called when swipe-to-home or fixed-rotation is happening. // Skip this entirely if that's the case. - if ((mInSwipePipToHomeTransition || mWaitForFixedRotation) && fromRotation) { + final boolean waitForFixedRotationOnEnteringPip = mWaitForFixedRotation + && (mState != State.ENTERED_PIP); + if ((mInSwipePipToHomeTransition || waitForFixedRotationOnEnteringPip) && fromRotation) { if (DEBUG) { Log.d(TAG, "Skip onMovementBoundsChanged on rotation change" + " mInSwipePipToHomeTransition=" + mInSwipePipToHomeTransition - + " mWaitForFixedRotation=" + mWaitForFixedRotation); + + " mWaitForFixedRotation=" + mWaitForFixedRotation + + " mState=" + mState); } return; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/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/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index 9ff2f461d40d..b2ff38cc4e23 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -247,7 +247,7 @@ static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject claz static void android_view_ThreadedRenderer_deleteProxy(JNIEnv* env, jobject clazz, jlong proxyPtr) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); - delete proxy; + RenderProxy::asyncDelete(proxy); } static jboolean android_view_ThreadedRenderer_loadSystemProperties(JNIEnv* env, jobject clazz, @@ -662,16 +662,18 @@ static void android_view_ThreadedRenderer_setASurfaceTransactionCallback( auto globalCallbackRef = std::make_shared<JWeakGlobalRefHolder>(vm, aSurfaceTransactionCallback); proxy->setASurfaceTransactionCallback( - [globalCallbackRef](int64_t transObj, int64_t scObj, int64_t frameNr) { + [globalCallbackRef](int64_t transObj, int64_t scObj, int64_t frameNr) -> bool { JNIEnv* env = getenv(globalCallbackRef->vm()); jobject localref = env->NewLocalRef(globalCallbackRef->ref()); if (CC_UNLIKELY(!localref)) { - return; + return false; } - env->CallVoidMethod(localref, gASurfaceTransactionCallback.onMergeTransaction, - static_cast<jlong>(transObj), static_cast<jlong>(scObj), - static_cast<jlong>(frameNr)); + jboolean ret = env->CallBooleanMethod( + localref, gASurfaceTransactionCallback.onMergeTransaction, + static_cast<jlong>(transObj), static_cast<jlong>(scObj), + static_cast<jlong>(frameNr)); env->DeleteLocalRef(localref); + return ret; }); } } @@ -1064,7 +1066,7 @@ int register_android_view_ThreadedRenderer(JNIEnv* env) { jclass aSurfaceTransactionCallbackClass = FindClassOrDie(env, "android/graphics/HardwareRenderer$ASurfaceTransactionCallback"); gASurfaceTransactionCallback.onMergeTransaction = - GetMethodIDOrDie(env, aSurfaceTransactionCallbackClass, "onMergeTransaction", "(JJJ)V"); + GetMethodIDOrDie(env, aSurfaceTransactionCallbackClass, "onMergeTransaction", "(JJJ)Z"); jclass prepareSurfaceControlForWebviewCallbackClass = FindClassOrDie( env, "android/graphics/HardwareRenderer$PrepareSurfaceControlForWebviewCallback"); diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 44d0038ad47e..0c9711ba8025 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -910,9 +910,8 @@ CanvasContext* CanvasContext::getActiveContext() { bool CanvasContext::mergeTransaction(ASurfaceTransaction* transaction, ASurfaceControl* control) { if (!mASurfaceTransactionCallback) return false; - std::invoke(mASurfaceTransactionCallback, reinterpret_cast<int64_t>(transaction), - reinterpret_cast<int64_t>(control), getFrameNumber()); - return true; + return std::invoke(mASurfaceTransactionCallback, reinterpret_cast<int64_t>(transaction), + reinterpret_cast<int64_t>(control), getFrameNumber()); } void CanvasContext::prepareSurfaceControlForWebview() { diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index a61c2bfd5c01..3279ccb8e597 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -206,7 +206,7 @@ public: ASurfaceControlStats* stats); void setASurfaceTransactionCallback( - const std::function<void(int64_t, int64_t, int64_t)>& callback) { + const std::function<bool(int64_t, int64_t, int64_t)>& callback) { mASurfaceTransactionCallback = callback; } @@ -317,7 +317,7 @@ private: // If set to true, we expect that callbacks into onSurfaceStatsAvailable bool mExpectSurfaceStats = false; - std::function<void(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback; + std::function<bool(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback; std::function<void()> mPrepareSurfaceControlForWebviewCallback; void cleanupResources(); diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index c47050c31e9a..e538a92fbd97 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -26,6 +26,7 @@ #include "renderthread/CanvasContext.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" +#include "thread/CommonPool.h" #include "utils/Macros.h" #include "utils/TimeUtils.h" @@ -42,6 +43,17 @@ RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode); } +void RenderProxy::asyncDelete(RenderProxy* proxy) { + if (!proxy) return; + + if (proxy->mContext) { + // Use the common pool because ~RenderProxy blocks on calling into RenderThread + CommonPool::post([proxy]() { delete proxy; }); + } else { + delete proxy; + } +} + RenderProxy::~RenderProxy() { destroyContext(); } @@ -314,7 +326,7 @@ void RenderProxy::setPictureCapturedCallback( } void RenderProxy::setASurfaceTransactionCallback( - const std::function<void(int64_t, int64_t, int64_t)>& callback) { + const std::function<bool(int64_t, int64_t, int64_t)>& callback) { mRenderThread.queue().post( [this, cb = callback]() { mContext->setASurfaceTransactionCallback(cb); }); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index d575aa77e4ab..5f6b5cde652d 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -62,10 +62,14 @@ enum { * references RenderProxy fields. This is safe as RenderProxy cannot * be deleted if it is blocked inside a call. */ -class RenderProxy { +class RenderProxy final { public: RenderProxy(bool opaque, RenderNode* rootNode, IContextFactory* contextFactory); - virtual ~RenderProxy(); + ~RenderProxy(); + + // Schedules a delete of the RenderProxy at a later date. Avoids blocking the current thread + // on destruction which ~RenderProxy does by default. + static void asyncDelete(RenderProxy*); // Won't take effect until next EGLSurface creation void setSwapBehavior(SwapBehavior swapBehavior); @@ -123,7 +127,7 @@ public: void setContentDrawBounds(int left, int top, int right, int bottom); void setPictureCapturedCallback(const std::function<void(sk_sp<SkPicture>&&)>& callback); void setASurfaceTransactionCallback( - const std::function<void(int64_t, int64_t, int64_t)>& callback); + const std::function<bool(int64_t, int64_t, int64_t)>& callback); void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback); void setFrameCallback(std::function<void(int64_t)>&& callback); void setFrameCompleteCallback(std::function<void(int64_t)>&& callback); diff --git a/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/CompanionDeviceManager/OWNERS b/packages/CompanionDeviceManager/OWNERS index da723b3b67da..734d8b6c5f43 100644 --- a/packages/CompanionDeviceManager/OWNERS +++ b/packages/CompanionDeviceManager/OWNERS @@ -1 +1 @@ -eugenesusla@google.com
\ No newline at end of file +include /core/java/android/companion/OWNERS
\ No newline at end of file diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java index 42c1997b41a3..bfc00bb8b94d 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java +++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java @@ -52,6 +52,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.lang.ref.WeakReference; import java.util.Arrays; +import java.util.NoSuchElementException; public final class RemotePrintDocument { private static final String LOG_TAG = "RemotePrintDocument"; @@ -441,7 +442,12 @@ public final class RemotePrintDocument { // Keep going - best effort... } - mPrintDocumentAdapter.asBinder().unlinkToDeath(mDeathRecipient, 0); + try { + mPrintDocumentAdapter.asBinder().unlinkToDeath(mDeathRecipient, 0); + } catch (NoSuchElementException e) { + Log.w(LOG_TAG, "Error unlinking print document adapter death recipient."); + // Keep going - best effort... + } } private void scheduleCommand(AsyncCommand command) { diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java index 59f272ff70f6..d25d5dcaac87 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java @@ -61,7 +61,6 @@ import android.print.PrinterId; import android.print.PrinterInfo; import android.printservice.PrintService; import android.printservice.PrintServiceInfo; -import android.provider.DocumentsContract; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; diff --git a/packages/SettingsLib/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp index 51c9c39cdf03..b6e167717023 100644 --- a/packages/SettingsLib/ActionButtonsPreference/Android.bp +++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp @@ -14,7 +14,8 @@ android_library { resource_dirs: ["res"], static_libs: [ - "androidx.preference_preference", + "androidx.preference_preference", + "SettingsLibUtils", ], sdk_version: "system_current", diff --git a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java index 9aa511d610f2..029c9196fa5e 100644 --- a/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java +++ b/packages/SettingsLib/ActionButtonsPreference/src/com/android/settingslib/widget/ActionButtonsPreference.java @@ -27,10 +27,11 @@ import android.widget.Button; import androidx.annotation.DrawableRes; import androidx.annotation.StringRes; -import androidx.core.os.BuildCompat; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; +import com.android.settingslib.utils.BuildCompatUtils; + import java.util.ArrayList; import java.util.List; @@ -94,7 +95,7 @@ public class ActionButtonsPreference extends Preference { public void onBindViewHolder(PreferenceViewHolder holder) { super.onBindViewHolder(holder); - final boolean allowedDivider = !BuildCompat.isAtLeastS(); + final boolean allowedDivider = !BuildCompatUtils.isAtLeastS(); holder.setDividerAllowedAbove(allowedDivider); holder.setDividerAllowedBelow(allowedDivider); diff --git a/packages/SettingsLib/BannerMessagePreference/Android.bp b/packages/SettingsLib/BannerMessagePreference/Android.bp index c6a95625a414..0f7a451e7c8b 100644 --- a/packages/SettingsLib/BannerMessagePreference/Android.bp +++ b/packages/SettingsLib/BannerMessagePreference/Android.bp @@ -16,6 +16,7 @@ android_library { static_libs: [ "androidx.preference_preference", "SettingsLibSettingsTheme", + "SettingsLibUtils", ], sdk_version: "system_current", 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-iw/strings.xml b/packages/SettingsLib/BannerMessagePreference/res/values-iw/strings.xml new file mode 100644 index 000000000000..aa4c669c8e2c --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values-iw/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/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java index 9e39355e452d..3a70d657f62f 100644 --- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java +++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java @@ -35,10 +35,11 @@ import androidx.annotation.ColorInt; import androidx.annotation.ColorRes; import androidx.annotation.RequiresApi; import androidx.annotation.StringRes; -import androidx.core.os.BuildCompat; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; +import com.android.settingslib.utils.BuildCompatUtils; + /** * Banner message is a banner displaying important information (permission request, page error etc), * and provide actions for user to address. It requires a user action to be dismissed. @@ -83,7 +84,7 @@ public class BannerMessagePreference extends Preference { } private static final String TAG = "BannerPreference"; - private static final boolean IS_AT_LEAST_S = BuildCompat.isAtLeastS(); + private static final boolean IS_AT_LEAST_S = BuildCompatUtils.isAtLeastS(); private final BannerMessagePreference.ButtonInfo mPositiveButtonInfo = new BannerMessagePreference.ButtonInfo(); diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_base_layout.xml index 55de3775bcad..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 @@ -21,8 +21,7 @@ android:id="@+id/content_parent" android:layout_width="match_parent" android:layout_height="match_parent" - android:fitsSystemWindows="true" - android:transitionGroup="true"> + android:fitsSystemWindows="true"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/app_bar" @@ -34,7 +33,7 @@ android:background="?android:attr/colorPrimary" android:theme="@style/Theme.CollapsingToolbar.Settings"> - <com.android.settingslib.collapsingtoolbar.AdjustableToolbarLayout + <com.google.android.material.appbar.CollapsingToolbarLayout android:id="@+id/collapsing_toolbar" android:layout_width="match_parent" android:layout_height="@dimen/toolbar_one_line_height" @@ -60,7 +59,7 @@ android:transitionName="shared_element_view" app:layout_collapseMode="pin"/> - </com.android.settingslib.collapsingtoolbar.AdjustableToolbarLayout> + </com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.AppBarLayout> <FrameLayout diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/values/styles.xml index 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 e6ca2e0d10c8..dbcecf1fe5b9 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java @@ -23,8 +23,11 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Toolbar; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.appbar.CollapsingToolbarLayout; import com.google.android.material.resources.TextAppearanceConfig; @@ -32,9 +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) { @@ -43,6 +55,14 @@ public class CollapsingToolbarBaseActivity extends SettingsTransitionActivity { TextAppearanceConfig.setShouldLoadFontSynchronously(true); super.setContentView(R.layout.collapsing_toolbar_base_layout); mCollapsingToolbarLayout = findViewById(R.id.collapsing_toolbar); + mAppBarLayout = findViewById(R.id.app_bar); + mAppBarLayout.addOnOffsetChangedListener(this); + if (savedInstanceState != null) { + mIsToolbarCollapsed = savedInstanceState.getBoolean(KEY_IS_TOOLBAR_COLLAPSED); + } + + initCollapsingToolbar(); + disableCollapsingToolbarLayoutScrollingBehavior(); final Toolbar toolbar = findViewById(R.id.action_bar); setActionBar(toolbar); @@ -101,10 +121,88 @@ 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(); + behavior.setDragCallback( + new AppBarLayout.Behavior.DragCallback() { + @Override + public boolean canDrag(@NonNull AppBarLayout appBarLayout) { + return false; + } + }); + params.setBehavior(behavior); + } + + @SuppressWarnings("RestrictTo") + private void initCollapsingToolbar() { + if (mCollapsingToolbarLayout == null || mAppBarLayout == null) { + return; + } + mCollapsingToolbarLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + v.removeOnLayoutChangeListener(this); + if (mIsToolbarCollapsed) { + return; + } + final int count = mCollapsingToolbarLayout.getLineCount(); + if (count > TOOLBAR_MAX_LINE_NUMBER) { + final ViewGroup.LayoutParams lp = mCollapsingToolbarLayout.getLayoutParams(); + lp.height = getResources() + .getDimensionPixelSize(R.dimen.toolbar_three_lines_height); + mCollapsingToolbarLayout.setScrimVisibleHeightTrigger( + getResources().getDimensionPixelSize( + R.dimen.scrim_visible_height_trigger_three_lines)); + mCollapsingToolbarLayout.setLayoutParams(lp); + } else if (count == TOOLBAR_MAX_LINE_NUMBER) { + final ViewGroup.LayoutParams lp = mCollapsingToolbarLayout.getLayoutParams(); + lp.height = getResources() + .getDimensionPixelSize(R.dimen.toolbar_two_lines_height); + mCollapsingToolbarLayout.setScrimVisibleHeightTrigger( + getResources().getDimensionPixelSize( + R.dimen.scrim_visible_height_trigger_two_lines)); + mCollapsingToolbarLayout.setLayoutParams(lp); + } + } + }); + } } diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseFragment.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseFragment.java index 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/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java index 8c2621d3e4ec..3a7fe3b92e78 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java @@ -16,22 +16,8 @@ package com.android.settingslib.collapsingtoolbar; -import static com.android.settingslib.transition.SettingsTransitionHelper.EXTRA_PAGE_TRANSITION_TYPE; - -import android.app.ActivityOptions; -import android.content.Intent; -import android.os.Bundle; -import android.util.Log; -import android.view.Window; -import android.widget.Toolbar; - -import androidx.annotation.Nullable; -import androidx.core.os.BuildCompat; import androidx.fragment.app.FragmentActivity; -import com.android.settingslib.transition.SettingsTransitionHelper; -import com.android.settingslib.transition.SettingsTransitionHelper.TransitionType; - /** * A base Activity for Settings-specific page transition. Activities extending it will get * Settings transition applied. @@ -39,57 +25,7 @@ import com.android.settingslib.transition.SettingsTransitionHelper.TransitionTyp public abstract class SettingsTransitionActivity extends FragmentActivity { private static final String TAG = "SettingsTransitionActivity"; - private Toolbar mToolbar; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - if (isSettingsTransitionEnabled()) { - getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS); - SettingsTransitionHelper.applyForwardTransition(this); - SettingsTransitionHelper.applyBackwardTransition(this); - } - - super.onCreate(savedInstanceState); - } - - @Override - public void setActionBar(@Nullable Toolbar toolbar) { - super.setActionBar(toolbar); - - mToolbar = toolbar; - } - - @Override - public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) { - final int transitionType = intent.getIntExtra(EXTRA_PAGE_TRANSITION_TYPE, - TransitionType.TRANSITION_SHARED_AXIS); - if (!isSettingsTransitionEnabled() || - transitionType == TransitionType.TRANSITION_NONE) { - super.startActivityForResult(intent, requestCode, options); - return; - } - - super.startActivityForResult(intent, requestCode, - createActivityOptionsBundleForTransition(options)); - } - protected boolean isSettingsTransitionEnabled() { - return BuildCompat.isAtLeastS(); - } - - @Nullable - private Bundle createActivityOptionsBundleForTransition(@Nullable Bundle options) { - if (mToolbar == null) { - Log.w(TAG, "setActionBar(Toolbar) is not called. Cannot apply settings transition!"); - return options; - } - final Bundle transitionOptions = ActivityOptions.makeSceneTransitionAnimation(this, - mToolbar, "shared_element_view").toBundle(); - if (options == null) { - return transitionOptions; - } - final Bundle mergedOptions = new Bundle(options); - mergedOptions.putAll(transitionOptions); - return mergedOptions; + return false; } } diff --git a/packages/SettingsLib/FooterPreference/res/drawable/ic_info_outline_24.xml b/packages/SettingsLib/FooterPreference/res/drawable/settingslib_ic_info_outline_24.xml index d9afeb0a6361..d9afeb0a6361 100644 --- a/packages/SettingsLib/FooterPreference/res/drawable/ic_info_outline_24.xml +++ b/packages/SettingsLib/FooterPreference/res/drawable/settingslib_ic_info_outline_24.xml 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-gu/strings.xml b/packages/SettingsLib/FooterPreference/res/values-gu/strings.xml new file mode 100644 index 000000000000..54249b88b47e --- /dev/null +++ b/packages/SettingsLib/FooterPreference/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="settingslib_learn_more_text" msgid="7385478101223578464">"વધુ જાણો"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/res/values-iw/strings.xml b/packages/SettingsLib/FooterPreference/res/values-iw/strings.xml new file mode 100644 index 000000000000..55b01873c2fc --- /dev/null +++ b/packages/SettingsLib/FooterPreference/res/values-iw/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-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/FooterPreference/res/values-te/strings.xml b/packages/SettingsLib/FooterPreference/res/values-te/strings.xml new file mode 100644 index 000000000000..6c8d6799754b --- /dev/null +++ b/packages/SettingsLib/FooterPreference/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="settingslib_learn_more_text" msgid="7385478101223578464">"మరింత తెలుసుకోండి"</string> +</resources> diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java index 005c75c45a46..8216edf872f3 100644 --- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java +++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java @@ -149,7 +149,7 @@ public class FooterPreference extends Preference { private void init() { setLayoutResource(R.layout.preference_footer); if (getIcon() == null) { - setIcon(R.drawable.ic_info_outline_24); + setIcon(R.drawable.settingslib_ic_info_outline_24); } setOrder(ORDER_FOOTER); if (TextUtils.isEmpty(getKey())) { diff --git a/packages/SettingsLib/MainSwitchPreference/Android.bp b/packages/SettingsLib/MainSwitchPreference/Android.bp index 23ee49e70595..76d1ea78258d 100644 --- a/packages/SettingsLib/MainSwitchPreference/Android.bp +++ b/packages/SettingsLib/MainSwitchPreference/Android.bp @@ -16,8 +16,9 @@ android_library { static_libs: [ "androidx.preference_preference", "SettingsLibSettingsTheme", + "SettingsLibUtils", ], sdk_version: "system_current", - min_sdk_version: "21", + min_sdk_version: "28", } diff --git a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml index 26075295c155..5817f77eb6d9 100644 --- a/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml +++ b/packages/SettingsLib/MainSwitchPreference/AndroidManifest.xml @@ -19,7 +19,7 @@ package="com.android.settingslib.widget"> <uses-sdk - android:minSdkVersion="21" + android:minSdkVersion="28" android:targetSdkVersion="31"/> </manifest> diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml b/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml index 0c95a9ec65e1..57b8446e2530 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/values/colors.xml @@ -16,7 +16,6 @@ --> <resources xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> - <color name="settingslib_switchbar_background_color">@*android:color/material_grey_600</color> <color name="settingslib_switchbar_switch_track_tint">#BFFFFFFF</color> <color name="settingslib_switchbar_switch_thumb_tint">@android:color/white</color> </resources> diff --git a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java index 91e1b9319f28..5f47be4b0642 100644 --- a/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java +++ b/packages/SettingsLib/MainSwitchPreference/src/com/android/settingslib/widget/MainSwitchBar.java @@ -30,7 +30,8 @@ import android.widget.Switch; import android.widget.TextView; import androidx.annotation.ColorInt; -import androidx.core.os.BuildCompat; + +import com.android.settingslib.utils.BuildCompatUtils; import java.util.ArrayList; import java.util.List; @@ -74,11 +75,11 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec LayoutInflater.from(context).inflate(R.layout.settingslib_main_switch_bar, this); - if (!BuildCompat.isAtLeastS()) { + if (!BuildCompatUtils.isAtLeastS()) { final TypedArray a = context.obtainStyledAttributes( new int[]{android.R.attr.colorAccent}); mBackgroundActivatedColor = a.getColor(0, 0); - mBackgroundColor = context.getColor(R.color.settingslib_switchbar_background_color); + mBackgroundColor = context.getColor(R.color.material_grey_600); a.recycle(); } @@ -88,11 +89,12 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec mFrameView = findViewById(R.id.frame); mTextView = (TextView) findViewById(R.id.switch_text); mSwitch = (Switch) findViewById(android.R.id.switch_widget); - mBackgroundOn = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_on); - mBackgroundOff = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_off); - mBackgroundDisabled = getContext().getDrawable( - R.drawable.settingslib_switch_bar_bg_disabled); - + if (BuildCompatUtils.isAtLeastS()) { + mBackgroundOn = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_on); + mBackgroundOff = getContext().getDrawable(R.drawable.settingslib_switch_bar_bg_off); + mBackgroundDisabled = getContext().getDrawable( + R.drawable.settingslib_switch_bar_bg_disabled); + } addOnSwitchChangeListener((switchView, isChecked) -> setChecked(isChecked)); setChecked(mSwitch.isChecked()); @@ -194,9 +196,7 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec * Remove a listener for switch changes */ public void removeOnSwitchChangeListener(OnMainSwitchChangeListener listener) { - if (mSwitchChangeListeners.contains(listener)) { - mSwitchChangeListeners.remove(listener); - } + mSwitchChangeListeners.remove(listener); } /** @@ -207,7 +207,7 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec mTextView.setEnabled(enabled); mSwitch.setEnabled(enabled); - if (BuildCompat.isAtLeastS()) { + if (BuildCompatUtils.isAtLeastS()) { if (enabled) { mFrameView.setBackground(isChecked() ? mBackgroundOn : mBackgroundOff); } else { @@ -226,7 +226,7 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec } private void setBackground(boolean isChecked) { - if (!BuildCompat.isAtLeastS()) { + if (!BuildCompatUtils.isAtLeastS()) { setBackgroundColor(isChecked ? mBackgroundActivatedColor : mBackgroundColor); } else { mFrameView.setBackground(isChecked ? mBackgroundOn : mBackgroundOff); @@ -267,10 +267,12 @@ public class MainSwitchBar extends LinearLayout implements CompoundButton.OnChec public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { + @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } + @Override public SavedState[] newArray(int size) { return new SavedState[size]; } diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerPreference.java index 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/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml index aed6338495f0..b78da90ba239 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/colors.xml @@ -34,7 +34,7 @@ <!-- Dialog accent color --> <color name="settingslib_dialog_accent">@android:color/system_accent1_600</color> <!-- Dialog background color --> - <color name="settingslib_dialog_background">@android:color/system_neutral1_800</color> + <color name="settingslib_dialog_background">@*android:color/surface_light</color> <!-- Dialog error color. --> <color name="settingslib_dialog_colorError">#d93025</color> <!-- Red 600 --> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/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/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml index 6b18f2879659..8034710b4341 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml @@ -42,7 +42,7 @@ </style> <style name="Theme.AlertDialog.SettingsLib" parent="@style/Theme.AppCompat.DayNight.Dialog.Alert"> - <item name="colorAccent">@*android:color/accent_device_default_light</item> + <item name="colorAccent">@color/settingslib_dialog_accent</item> <item name="android:colorError">@color/settingslib_dialog_colorError</item> <item name="android:colorBackground">@color/settingslib_dialog_background</item> diff --git a/packages/SettingsLib/SettingsTransition/Android.bp b/packages/SettingsLib/SettingsTransition/Android.bp index d8cd556ba85d..f06a9a7c0f43 100644 --- a/packages/SettingsLib/SettingsTransition/Android.bp +++ b/packages/SettingsLib/SettingsTransition/Android.bp @@ -11,7 +11,6 @@ android_library { name: "SettingsLibSettingsTransition", srcs: ["src/**/*.java"], - resource_dirs: ["res"], static_libs: [ "com.google.android.material_material", diff --git a/packages/SettingsLib/SettingsTransition/res/values/dimens.xml b/packages/SettingsLib/SettingsTransition/res/values/dimens.xml deleted file mode 100644 index 0630ca85b621..000000000000 --- a/packages/SettingsLib/SettingsTransition/res/values/dimens.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2021 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<resources> - <dimen name="settings_shared_axis_x_slide_distance">96dp</dimen> -</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java b/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java index a6eeb8867346..540f8c2ce1f5 100644 --- a/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java +++ b/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java @@ -16,21 +16,11 @@ package com.android.settingslib.transition; -import androidx.annotation.IntDef; import android.app.Activity; -import android.content.Context; -import android.util.Log; -import android.view.Window; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; -import androidx.core.os.BuildCompat; +import androidx.annotation.IntDef; import androidx.fragment.app.Fragment; -import com.google.android.material.transition.platform.FadeThroughProvider; -import com.google.android.material.transition.platform.MaterialSharedAxis; -import com.google.android.material.transition.platform.SlideDistanceProvider; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -59,31 +49,6 @@ public class SettingsTransitionHelper { public static final String EXTRA_PAGE_TRANSITION_TYPE = "page_transition_type"; private static final String TAG = "SettingsTransitionHelper"; - private static final long DURATION = 450L; - private static final float FADE_THROUGH_THRESHOLD = 0.22F; - - private static MaterialSharedAxis createSettingsSharedAxis(Context context, boolean forward) { - final MaterialSharedAxis transition = new MaterialSharedAxis(MaterialSharedAxis.X, forward); - transition.excludeTarget(android.R.id.statusBarBackground, true); - transition.excludeTarget(android.R.id.navigationBarBackground, true); - - final SlideDistanceProvider forwardDistanceProvider = - (SlideDistanceProvider) transition.getPrimaryAnimatorProvider(); - final int distance = context.getResources().getDimensionPixelSize( - R.dimen.settings_shared_axis_x_slide_distance); - forwardDistanceProvider.setSlideDistance(distance); - transition.setDuration(DURATION); - - final FadeThroughProvider fadeThroughProvider = - (FadeThroughProvider) transition.getSecondaryAnimatorProvider(); - fadeThroughProvider.setProgressThreshold(FADE_THROUGH_THRESHOLD); - - final Interpolator interpolator = - AnimationUtils.loadInterpolator(context, R.interpolator.fast_out_extra_slow_in); - transition.setInterpolator(interpolator); - - return transition; - } /** * Apply the forward transition to the {@link Activity}, including Exit Transition and Enter @@ -92,23 +57,16 @@ public class SettingsTransitionHelper { * The Exit Transition takes effect when leaving the page, while the Enter Transition is * triggered when the page is launched/entering. */ - public static void applyForwardTransition(Activity activity) { - if (!isSettingsTransitionEnabled()) { - return; - } - if (activity == null) { - Log.w(TAG, "applyForwardTransition: Invalid activity!"); - return; - } - final Window window = activity.getWindow(); - if (window == null) { - Log.w(TAG, "applyForwardTransition: Invalid window!"); - return; - } - final MaterialSharedAxis forward = createSettingsSharedAxis(activity, true); - window.setExitTransition(forward); - window.setEnterTransition(forward); - } + public static void applyForwardTransition(Activity activity) {} + + /** + * Apply the forward transition to the {@link Fragment}, including Exit Transition and Enter + * Transition. + * + * The Exit Transition takes effect when leaving the page, while the Enter Transition is + * triggered when the page is launched/entering. + */ + public static void applyForwardTransition(Fragment fragment) {} /** * Apply the backward transition to the {@link Activity}, including Return Transition and @@ -118,43 +76,7 @@ public class SettingsTransitionHelper { * to close. Reenter Transition will be used to move Views in to the scene when returning from a * previously-started Activity. */ - public static void applyBackwardTransition(Activity activity) { - if (!isSettingsTransitionEnabled()) { - return; - } - if (activity == null) { - Log.w(TAG, "applyBackwardTransition: Invalid activity!"); - return; - } - final Window window = activity.getWindow(); - if (window == null) { - Log.w(TAG, "applyBackwardTransition: Invalid window!"); - return; - } - final MaterialSharedAxis backward = createSettingsSharedAxis(activity, false); - window.setReturnTransition(backward); - window.setReenterTransition(backward); - } - - /** - * Apply the forward transition to the {@link Fragment}, including Exit Transition and Enter - * Transition. - * - * The Exit Transition takes effect when leaving the page, while the Enter Transition is - * triggered when the page is launched/entering. - */ - public static void applyForwardTransition(Fragment fragment) { - if (!isSettingsTransitionEnabled()) { - return; - } - if (fragment == null) { - Log.w(TAG, "applyForwardTransition: Invalid fragment!"); - return; - } - final MaterialSharedAxis forward = createSettingsSharedAxis(fragment.getContext(), true); - fragment.setExitTransition(forward); - fragment.setEnterTransition(forward); - } + public static void applyBackwardTransition(Activity activity) {} /** * Apply the backward transition to the {@link Fragment}, including Return Transition and @@ -164,20 +86,5 @@ public class SettingsTransitionHelper { * to close. Reenter Transition will be used to move Views in to the scene when returning from a * previously-started Fragment. */ - public static void applyBackwardTransition(Fragment fragment) { - if (!isSettingsTransitionEnabled()) { - return; - } - if (fragment == null) { - Log.w(TAG, "applyBackwardTransition: Invalid fragment!"); - return; - } - final MaterialSharedAxis backward = createSettingsSharedAxis(fragment.getContext(), false); - fragment.setReturnTransition(backward); - fragment.setReenterTransition(backward); - } - - private static boolean isSettingsTransitionEnabled() { - return BuildCompat.isAtLeastS(); - } + public static void applyBackwardTransition(Fragment fragment) {} } diff --git a/packages/SettingsLib/Utils/AndroidManifest.xml b/packages/SettingsLib/Utils/AndroidManifest.xml index 96d9e518663f..fd89676ab6d7 100644 --- a/packages/SettingsLib/Utils/AndroidManifest.xml +++ b/packages/SettingsLib/Utils/AndroidManifest.xml @@ -16,7 +16,7 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.settingslib.widget"> + package="com.android.settingslib.utils"> <uses-sdk android:minSdkVersion="21" /> diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java new file mode 100644 index 000000000000..44f6f5439af3 --- /dev/null +++ b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/BuildCompatUtils.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.utils; + +import android.os.Build.VERSION; + +/** + * An util class to check whether the current OS version is higher or equal to sdk version of + * device. + */ +public final class BuildCompatUtils { + + /** + * Implementation of BuildCompat.isAtLeast*() suitable for use in Settings + * + * <p>This still should try using BuildCompat.isAtLeastR() as source of truth, but also checking + * for VERSION_SDK_INT and VERSION.CODENAME in case when BuildCompat implementation returned + * false. Note that both checks should be >= and not = to make sure that when Android version + * increases (i.e., from R to S), this does not stop working. + * + * <p>Supported configurations: + * + * <ul> + * <li>For current Android release: when new API is not finalized yet (CODENAME = "S", SDK_INT + * = 30|31) + * <li>For current Android release: when new API is finalized (CODENAME = "REL", SDK_INT = 31) + * <li>For next Android release (CODENAME = "T", SDK_INT = 30+) + * </ul> + * + * <p>Note that Build.VERSION_CODES.S cannot be used here until final SDK is available, because + * it is equal to Build.VERSION_CODES.CUR_DEVELOPMENT before API finalization. + * + * @return Whether the current OS version is higher or equal to S. + */ + public static boolean isAtLeastS() { + if (VERSION.SDK_INT < 30) { + return false; + } + + return (VERSION.CODENAME.equals("REL") && VERSION.SDK_INT >= 31) + || (VERSION.CODENAME.length() == 1 + && VERSION.CODENAME.compareTo("S") >= 0 + && VERSION.CODENAME.compareTo("Z") <= 0); + } + + private BuildCompatUtils() {} +} diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java index 4505dad8ea12..5dc0b7274408 100644 --- a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java +++ b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java @@ -22,7 +22,7 @@ import android.content.pm.PackageManager; import android.os.UserManager; import android.util.Log; -import com.android.settingslib.widget.R; +import com.android.settingslib.utils.R; public class AppUtils { diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 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-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 2d1a3629992c..c7a0b27649bc 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/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-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-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index c169d768cceb..c5a310183f55 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Kaldenavn"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæst"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gæsten"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Nulstil gæstesession"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gæst"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tag et billede"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Vælg et billede"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 5808b6ded540..9b7fcc6f767d 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Alias"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Gast hinzufügen"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Gast entfernen"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Gast zurücksetzen"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gast"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Foto machen"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Bild auswählen"</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-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 5d0a61ab2047..d6f7d7cf1454 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Sobrenombre"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Agregar invitado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Restablecer perfil de invitado"</string> <string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tomar una foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Elegir una imagen"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 6ff48541e4e8..08cca4239071 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -507,7 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmas y recordatorios"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Permitir la programación de alarmas y recordatorios"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmas y recordatorios"</string> - <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite que esta aplicación programe alarmas y otras acciones de carácter temporal. Este permite sirve para que la aplicación siga activa en segundo plano, lo que puede usar más batería.\n\nSi este permiso está desactivados, no funcionarán las alarmas ni los eventos con carácter temporal programados por esta aplicación."</string> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Permite que esta aplicación programe alarmas y otras acciones que se llevan a cabo a una hora determinada. Esto hace que la aplicación siga activa en segundo plano, lo que puede usar más batería.\n\nSi este permiso está desactivado, no funcionarán las alarmas ni los eventos que se activan a una hora determinada que programe esta aplicación."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"programar, alarma, recordatorio, reloj"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activar"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activar el modo No molestar"</string> @@ -566,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..a9d21695c4dd 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -507,7 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmid ja meeldetuletused"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Luba alarmide ja meeldetuletuste määramine"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmid ja meeldetuletused"</string> - <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lubage sellel rakendusel määrata äratusi ja ajastada kiire tähtajaga toiminguid. See võimaldab rakendusel töötada taustal, mistõttu võib akukasutus olla suurem.\n\nKui see luba on välja lülitatud, siis olemasolevad äratused ja selle rakenduse ajastatud ajapõhised sündmused ei tööta."</string> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Lubage sellel rakendusel määrata alarme ja ajastada kiire tähtajaga toiminguid. See võimaldab rakendusel töötada taustal, mistõttu võib akukasutus olla suurem.\n\nKui see luba on välja lülitatud, siis olemasolevad alarmid ja selle rakenduse ajastatud ajapõhised sündmused ei tööta."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ajakava, äratus, meeldetuletus, kell"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Lülita sisse"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Valiku Mitte segada sisselülitamine"</string> @@ -566,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-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index eb68f5fe2bbb..6e421487de3c 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/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-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-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 551dc36faac0..776e8647310e 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Réinitialiser la session Invité"</string> <string name="guest_nickname" msgid="6332276931583337261">"Invité"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Sélectionner une image"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 5ade70f74e9e..753e4466d996 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -461,7 +461,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charge lente"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"En charge sans fil"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Pas en charge"</string> - <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connecté, pas en charge"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connectée, pas en charge"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Chargée"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Contrôlé par l\'administrateur"</string> <string name="disabled" msgid="8017887509554714950">"Désactivée"</string> @@ -507,7 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"Alarmes et rappels"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"Autoriser à définir des alarmes et des rappels"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"Alarmes et rappels"</string> - <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Autorisez cette appli à définir des alarmes et à planifier des actions soumises à un délai. Cela lui permet de s\'exécuter en arrière-plan, ce qui peut consommer plus de batterie.\n\nSi cette autorisation est désactivée, les alarmes existantes et les événements basés sur l\'heure planifiés par cette appli ne fonctionneront pas."</string> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"Autorisez cette appli à définir des alarmes et à programmer des actions à certaines heures. Elle s\'exécutera alors en arrière-plan, ce qui peut solliciter davantage la batterie.\n\nSi l\'autorisation est désactivée, les alarmes existantes et les événements programmés par l\'appli ne fonctionneront pas."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"définir, alarme, rappel, horloge"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"Activer"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"Activer le mode Ne pas déranger"</string> @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Réinitialiser la session Invité"</string> <string name="guest_nickname" msgid="6332276931583337261">"Invité"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choisir une image"</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-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index b55a5a23493f..d1ecff8b9326 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -454,7 +454,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="8085099012811384899">"પૂર્ણ ચાર્જ થવામાં <xliff:g id="TIME">%1$s</xliff:g> બાકી છે"</string> <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - પૂર્ણ ચાર્જ થવામાં <xliff:g id="TIME">%2$s</xliff:g> બાકી છે"</string> - <string name="power_charging_limited" msgid="7956120998372505295">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ હંગામી રૂપે પ્રતિબંધિત કરવામાં આવ્યું છે"</string> + <string name="power_charging_limited" msgid="7956120998372505295">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જિંગ હંગામીરૂપે પ્રતિબંધિત કરવામાં આવ્યું છે"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"અજાણ્યું"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ચાર્જ થઈ રહ્યું છે"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ઝડપથી ચાર્જ થાય છે"</string> @@ -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-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 5620d946b999..8af48e5752fc 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -507,7 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"अलार्म और रिमाइंडर"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"अलार्म और रिमाइंडर सेट करने की अनुमति दें"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"अलार्म और रिमाइंडर"</string> - <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"इस ऐप्लिकेशन को अलार्म और तय समय पर होने वाली कार्रवाइयों के रिमाइंडर सेट करने की अनुमति दें. ऐसा करने से, ऐप्लिकेशन को बैकग्राउंड में चलने की अनुमति मिलती है. इससे, बैटरी ज़्यादा खर्च होती है.\n\nअगर आप यह अनुमति नहीं देते हैं, तो इस ऐप्लिकेशन की मदद से सेट किए गए अलार्म और तय समय पर होने वाली कार्रवाइयों के रिमाइंडर काम नहीं करेंगे."</string> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"इस ऐप्लिकेशन को अलार्म और तय समय पर होने वाली कार्रवाइयों के रिमाइंडर सेट करने की अनुमति दें. ऐसा करने से, ऐप्लिकेशन को बैकग्राउंड में चलने की अनुमति मिलती है. इससे बैटरी ज़्यादा खर्च होती है.\n\nअगर आप यह अनुमति नहीं देते हैं, तो इस ऐप्लिकेशन की मदद से सेट किए गए अलार्म और तय समय पर होने वाली कार्रवाइयों के रिमाइंडर काम नहीं करेंगे."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"शेड्यूल, अलार्म, रिमाइंडर, घड़ी"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"चालू करें"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'परेशान न करें\' चालू करें"</string> @@ -566,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-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index fcd3c87454e2..130d4cccdea9 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/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-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index f67c068c75c0..600dc1a220b7 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Tambahkan tamu"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Hapus tamu"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Reset tamu"</string> <string name="guest_nickname" msgid="6332276931583337261">"Tamu"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih gambar"</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-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index dc4836213508..b75f7c18174c 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/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-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 7e9949f72602..a10fe1b5df04 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/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-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index eef252abceeb..1322643b6e24 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/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-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-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 592707190eec..582a3e1e8ab6 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/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-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 618b7db9a988..a438a408f3c6 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/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-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index bf6c2a32ab7a..732807267871 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/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-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 77006fc1bf1a..9feb6ac45689 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Tambah tetamu"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Alih keluar tetamu"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Tetapkan semula tetamu"</string> <string name="guest_nickname" msgid="6332276931583337261">"Tetamu"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih imej"</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-ne/arrays.xml b/packages/SettingsLib/res/values-ne/arrays.xml index 1fc6899891aa..3699bf3ec59c 100644 --- a/packages/SettingsLib/res/values-ne/arrays.xml +++ b/packages/SettingsLib/res/values-ne/arrays.xml @@ -260,7 +260,7 @@ <item msgid="6506681373060736204">"बढीमा ४ प्रक्रियाहरू"</item> </string-array> <string-array name="usb_configuration_titles"> - <item msgid="3358668781763928157">"चार्ज हुँदै"</item> + <item msgid="3358668781763928157">"चार्ज हुँदै छ"</item> <item msgid="7804797564616858506">"MTP (मिडिया स्थानान्तरण प्रोटोकल)"</item> <item msgid="910925519184248772">"PTP (चित्र स्थानान्तरण प्रोटोकल)"</item> <item msgid="3825132913289380004">"RNDIS (USB इथरनेट)"</item> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index a54fc0c85071..a69f552195e5 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -456,8 +456,8 @@ <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - पूरा चार्ज हुन <xliff:g id="TIME">%2$s</xliff:g> लाग्ने छ"</string> <string name="power_charging_limited" msgid="7956120998372505295">"<xliff:g id="LEVEL">%1$s</xliff:g> - चार्जिङ केही समयका लागि सीमित पारिएको छ"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string> - <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हुँदै"</string> - <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"द्रुत गतिमा चार्ज गरिँदै"</string> + <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हुँदै छ"</string> + <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"द्रुत गतिमा चार्ज गरिँदै छ"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"बिस्तारै चार्ज गरिँदै"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"वायरलेस तरिकाले चार्ज गरिँदै छ"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"चार्ज भइरहेको छैन"</string> @@ -507,7 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"अलार्म र रिमाइन्डरहरू"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"अलार्म तथा रिमाइन्डर सेट गर्न दिइयोस्"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"घडी तथा रिमाइन्डरहरू"</string> - <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"यो एपलाई अलार्म सेट गर्ने र समयमै पूरा गर्नु पर्ने कारबाहीहरूको रुटिन तय गर्ने अनुमति दिनुहोस्। यो अनुमति दिइएको छ भने यो एप ब्याकग्राउन्डमा यसले चलेर धेरै ब्याट्री खपत गर्न सक्छ।\n\nयो अनुमति दिइएको छैन भने समय तोकिएका अलार्म र यो एपले तय गरेका समयअनुसार चल्ने कार्यक्रमले काम गर्दैन।"</string> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"यो एपलाई अलार्म सेट गर्ने र समयमै पूरा गर्नु पर्ने कारबाहीहरूको रुटिन बनाउने अनुमति दिनुहोस्। यो अनुमति दिइएको छ भने यो एप ब्याकग्राउन्डमा चल्छ र धेरै ब्याट्री खपत हुन्छ।\n\nयो अनुमति दिइएको छैन भने सेट गरिएका अलार्म बज्दैनन् र यो एपले तय गरेका गतिविधि चल्दैनन्।"</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"समयतालिका, अलार्म, रिमाइन्डर, घडी"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"सक्रिय गर्नुहोस्"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"बाधा नपुऱ्याउनुहोस् नामक मोडलाई सक्रिय गर्नुहोस्"</string> @@ -566,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-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-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 6cc3cc560e53..00850231f0b4 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/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-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 0af499023717..ff1526eadffd 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -461,7 +461,7 @@ <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ਬਿਨਾਂ ਤਾਰ ਤੋਂ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string> - <string name="battery_info_status_not_charging" msgid="3371084153747234837">"ਕਨੈਕਟ ਹੈ, ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string> + <string name="battery_info_status_not_charging" msgid="3371084153747234837">"ਕਨੈਕਟ ਹੈ, ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਹੀ"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"ਚਾਰਜ ਹੋ ਗਈ"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਕੰਟਰੋਲ ਕੀਤੀ ਗਈ"</string> <string name="disabled" msgid="8017887509554714950">"ਅਯੋਗ ਬਣਾਇਆ"</string> @@ -507,7 +507,7 @@ <string name="alarms_and_reminders_label" msgid="6918395649731424294">"ਅਲਾਰਮ ਅਤੇ ਰਿਮਾਈਂਡਰ"</string> <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"ਅਲਾਰਮ ਅਤੇ ਰਿਮਾਈਂਡਰ ਸੈੱਟ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿਓ"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"ਅਲਾਰਮ ਅਤੇ ਰਿਮਾਈਂਡਰ"</string> - <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ਇਸ ਐਪ ਨੂੰ ਅਲਾਰਮ ਸੈੱਟ ਕਰਨ ਜਾਂ ਹੋਰ ਸਮਾਂ-ਸੰਵੇਦਨਸ਼ੀਲ ਕਾਰਵਾਈਆਂ ਨੂੰ ਨਿਯਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ। ਇਸ ਨਾਲ ਐਪ ਨੂੰ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚਲਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਮਿਲਦੀ ਹੈ, ਜੋ ਵੱਧ ਬੈਟਰੀ ਦੀ ਵਰਤੋਂ ਕਰ ਸਕਦੀ ਹੈ।\n\nਜੇ ਇਹ ਇਜਾਜ਼ਤ ਬੰਦ ਹੈ, ਤਾਂ ਮੌਜੂਦਾ ਅਲਾਰਮ ਅਤੇ ਇਸ ਐਪ ਰਾਹੀਂ ਸਮਾਂ ਨਿਯਤ ਕੀਤੇ ਸਮਾਂ-ਆਧਾਰਿਤ ਇਵੈਂਟਾਂ ਕੰਮ ਨਹੀਂ ਕਰਨਗੇ।"</string> + <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"ਇਸ ਐਪ ਨੂੰ ਅਲਾਰਮ ਸੈੱਟ ਕਰਨ ਜਾਂ ਹੋਰ ਸਮਾਂ-ਸੰਵੇਦਨਸ਼ੀਲ ਕਾਰਵਾਈਆਂ ਨੂੰ ਨਿਯਤ ਕਰਨ ਦਿਓ। ਇਸ ਨਾਲ ਐਪ ਨੂੰ ਬੈਕਗ੍ਰਾਊਂਡ ਵਿੱਚ ਚਲਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਮਿਲਦੀ ਹੈ, ਜਿਸ ਨਾਲ ਬੈਟਰੀ ਦੀ ਵਰਤੋਂ ਵੱਧ ਸਕਦੀ ਹੈ।\n\nਜੇ ਇਹ ਇਜਾਜ਼ਤ ਬੰਦ ਹੈ, ਤਾਂ ਮੌਜੂਦਾ ਅਲਾਰਮ ਅਤੇ ਇਸ ਐਪ ਰਾਹੀਂ ਨਿਯਤ ਕੀਤੇ ਸਮਾਂ-ਆਧਾਰਿਤ ਇਵੈਂਟ ਕੰਮ ਨਹੀਂ ਕਰਨਗੇ।"</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"ਸਮਾਂ-ਸੂਚੀ, ਅਲਾਰਮ, ਰਿਮਾਈਂਡਰ, ਘੜੀ"</string> <string name="zen_mode_enable_dialog_turn_on" msgid="6418297231575050426">"ਚਾਲੂ ਕਰੋ"</string> <string name="zen_mode_settings_turn_on_dialog_title" msgid="2760567063190790696">"\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਨੂੰ ਚਾਲੂ ਕਰੋ"</string> 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..9937600c82b9 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -559,7 +559,7 @@ <string name="user_new_user_name" msgid="60979820612818840">"Новый пользователь"</string> <string name="user_new_profile_name" msgid="2405500423304678841">"Новый профиль"</string> <string name="user_info_settings_title" msgid="6351390762733279907">"Сведения о пользователе"</string> - <string name="profile_info_settings_title" msgid="105699672534365099">"Информация о профиле"</string> + <string name="profile_info_settings_title" msgid="105699672534365099">"Данные профиля"</string> <string name="user_need_lock_message" msgid="4311424336209509301">"Чтобы создать профиль с ограниченным доступом, необходимо предварительно настроить блокировку экрана для защиты приложений и личных данных"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Включить блокировку"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Сменить пользователя на <xliff:g id="USER_NAME">%s</xliff:g>"</string> @@ -568,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-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index a385055f8fbb..2fae557feccd 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/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-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-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index ba7443600e3d..a784df994491 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Pseudonimi"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Shto të ftuar"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Hiq të ftuarin"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Rivendos vizitorin"</string> <string name="guest_nickname" msgid="6332276931583337261">"I ftuar"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Bëj një fotografi"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Zgjidh një imazh"</string> 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-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 332cd9dc386a..9424dab08d71 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Jina wakilishi"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ongeza mgeni"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ondoa mgeni"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Badilisha kipindi cha mgeni"</string> <string name="guest_nickname" msgid="6332276931583337261">"Mgeni"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Piga picha"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Chagua picha"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 7952a76859f5..0387c920dffd 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -505,8 +505,7 @@ <string name="cancel" msgid="5665114069455378395">"ரத்துசெய்"</string> <string name="okay" msgid="949938843324579502">"சரி"</string> <string name="alarms_and_reminders_label" msgid="6918395649731424294">"அலாரங்களும் நினைவூட்டல்களும்"</string> - <!-- unknown quoting pattern: original -1, translation 1 --> - <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"அலாரங்கள் &amp; நினைவூட்டல்களை அமைக்க அனுமதித்தல்"</string> + <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"அலாரங்கள் & நினைவூட்டல்களை அமைக்க அனுமதித்தல்"</string> <string name="alarms_and_reminders_title" msgid="8819933264635406032">"அலாரங்கள் & நினைவூட்டல்கள்"</string> <string name="alarms_and_reminders_footer_title" msgid="6302587438389079695">"அலாரங்களை அமைக்கவும் குறிப்பிட்ட கால இடைவெளியில் செயல்களைத் திட்டமிடவும் இந்த ஆப்ஸை அனுமதிக்கும். இது ஆப்ஸ் பின்னணியில் இயங்குவதை அனுமதிக்கும், இதற்காக அதிக பேட்டரியைப் பயன்படுத்தக்கூடும்.\n\nஇந்த அனுமதி முடக்கப்பட்டிருந்தால் இந்த ஆப்ஸ் மூலம் திட்டமிடப்பட்ட ஏற்கெனவே அமைத்த அலாரங்களும் நேர அடிப்படையிலான நிகழ்வுகளும் வேலை செய்யாது."</string> <string name="keywords_alarms_and_reminders" msgid="6633360095891110611">"திட்டமிடல், அலாரம், நினைவூட்டல், கடிகாரம்"</string> @@ -567,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-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-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 36144ebf1c63..ed90a29184f4 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Takma ad"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Misafir ekle"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Misafir oturumunu kaldır"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Misafir oturumunu sıfırla"</string> <string name="guest_nickname" msgid="6332276931583337261">"Misafir"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotoğraf çek"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Resim seç"</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-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index d0ed2ab8ba34..7f9e5fb56aaa 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/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-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-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 0fba1ecffe02..10203832c6d2 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -566,8 +566,7 @@ <string name="user_nickname" msgid="262624187455825083">"Biệt hiệu"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Thêm khách"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Xóa phiên khách"</string> - <!-- no translation found for guest_reset_guest (6110013010356013758) --> - <skip /> + <string name="guest_reset_guest" msgid="6110013010356013758">"Đặt lại phiên khách"</string> <string name="guest_nickname" msgid="6332276931583337261">"Khách"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Chụp ảnh"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Chọn một hình ảnh"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 642dd1dcb4ed..b2c84a51f1f1 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/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-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 1cab0afbead2..c62c84edb77a 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/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-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/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 8651595753a7..6b840bd7901d 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1426,7 +1426,12 @@ <string name="guest_reset_guest">Reset guest</string> <!-- Name for the guest user [CHAR LIMIT=35] --> <string name="guest_nickname">Guest</string> - + <!-- Title of the confirmation dialog to confirm resetting guest. [CHAR LIMIT=NONE] --> + <string name="guest_reset_guest_dialog_title">Reset guest?</string> + <!-- Label for button in confirmation dialog when resetting guest user [CHAR LIMIT=35] --> + <string name="guest_reset_guest_confirm_button">Reset</string> + <!-- Status message indicating the device is in the process of resetting the guest user. [CHAR_LIMIT=NONE] --> + <string name="guest_resetting">Resetting guest\u2026</string> <!-- An option in a photo selection dialog to take a new photo [CHAR LIMIT=50] --> <string name="user_image_take_photo">Take a photo</string> <!-- An option in a photo selection dialog to choose a pre-existing image [CHAR LIMIT=50] --> diff --git a/packages/SettingsLib/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/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 959b5ca56687..f7123c180ead 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -419,6 +419,9 @@ <!-- Permission required for running networking unit tests --> <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" /> + <!-- Permission required for CTS test - CtsHostsideNetworkTests --> + <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" /> + <!-- Permissions required for CTS test - TunerTest --> <uses-permission android:name="android.permission.ACCESS_TV_DESCRAMBLER" /> <uses-permission android:name="android.permission.ACCESS_TV_TUNER" /> diff --git a/packages/Shell/res/values-az/strings.xml b/packages/Shell/res/values-az/strings.xml index 15853c2cc270..23a1ad7abd7c 100644 --- a/packages/Shell/res/values-az/strings.xml +++ b/packages/Shell/res/values-az/strings.xml @@ -35,7 +35,7 @@ <string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"Zip faylı üçün baq hesabat detalları əlavə edilmədi"</string> <string name="bugreport_unnamed" msgid="2800582406842092709">"adsız"</string> <string name="bugreport_info_action" msgid="2158204228510576227">"Detallar"</string> - <string name="bugreport_screenshot_action" msgid="8677781721940614995">"displey görüntüsü"</string> + <string name="bugreport_screenshot_action" msgid="8677781721940614995">"Skrinşot"</string> <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"Displey görüntüsü uğurla çəkildi."</string> <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"Displey görüntüsü əlçatan deyil."</string> <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Baq hesabatı <xliff:g id="ID">#%d</xliff:g> detalları"</string> diff --git a/packages/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/docs/qs-tiles.md b/packages/SystemUI/docs/qs-tiles.md index 89c28a074c76..efcb2de0f6c3 100644 --- a/packages/SystemUI/docs/qs-tiles.md +++ b/packages/SystemUI/docs/qs-tiles.md @@ -306,6 +306,7 @@ This section describes necessary and recommended steps when implementing a Quick * Add a case to the `switch` with a unique String spec for the chosen tile. 5. In [SystemUI/res/values/config.xml](/packages/SystemUI/res/values/config.xml), modify `quick_settings_tiles_stock` and add the spec defined in the previous step. If necessary, add it also to `quick_settings_tiles_default`. The first one contains a list of all the tiles that SystemUI knows how to create (to show to the user in the customization screen). The second one contains only the default tiles that the user will experience on a fresh boot or after they reset their tiles. 6. In [SystemUI/res/values/tiles_states_strings.xml](/packages/SystemUI/res/values/tiles_states_strings.xml), add a new array for your tile. The name has to be `tile_states_<spec>`. Use a good description to help the translators. +7. In [`SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt`](/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt), add a new element to the map in `SubtitleArrayMapping` corresponding to the resource created in the previous step. #### Abstract methods in QSTileImpl diff --git a/packages/SystemUI/res-keyguard/values-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/drawable/rounded_bg_full.xml b/packages/SystemUI/res/drawable/rounded_bg_full.xml index e0d3f63e8f40..967c57b0c370 100644 --- a/packages/SystemUI/res/drawable/rounded_bg_full.xml +++ b/packages/SystemUI/res/drawable/rounded_bg_full.xml @@ -1,7 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:shape="rectangle"> - <solid android:color="?android:attr/colorBackgroundFloating" /> + <solid android:color="?androidprv:attr/colorSurface" /> <corners android:bottomLeftRadius="?android:attr/dialogCornerRadius" android:topLeftRadius="?android:attr/dialogCornerRadius" diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/packages/SystemUI/res/interpolator/tv_privacy_chip_collapse_interpolator.xml index a2bbd2be777d..429812484674 100644 --- a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml +++ b/packages/SystemUI/res/interpolator/tv_privacy_chip_collapse_interpolator.xml @@ -12,8 +12,11 @@ ~ distributed under the License is distributed on an "AS IS" BASIS, ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ~ See the License for the specific language governing permissions and - ~ limitations under the License + ~ limitations under the License. --> <pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android" - android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/> + android:controlX1="0.4" + android:controlY1="1.00" + android:controlX2="0.12" + android:controlY2="1.00"/>
\ No newline at end of file diff --git a/packages/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/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml index 42f14f30da00..6bc01389e3c3 100644 --- a/packages/SystemUI/res/layout/notif_half_shelf.xml +++ b/packages/SystemUI/res/layout/notif_half_shelf.xml @@ -64,10 +64,12 @@ android:layout_gravity="center" android:padding="8dp" android:gravity="center_vertical|start" - android:textSize="15sp" android:ellipsize="end" - android:maxLines="1" - style="@style/TextAppearance.NotificationImportanceChannel" /> + android:maxLines="2" + android:textColor="?android:attr/textColorPrimary" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:textSize="16sp" + /> <Switch android:id="@+id/toggle" @@ -80,18 +82,13 @@ <View android:layout_width="match_parent" android:layout_height="1dp" - android:background="?android:attr/colorAccent" + android:background="@*android:color/background_device_default_light" /> <!-- ChannelRows get added dynamically --> </com.android.systemui.statusbar.notification.row.ChannelEditorListView> - <!-- divider view --> - <View - android:layout_width="match_parent" - android:layout_height="1dp" - android:background="?android:attr/colorAccent" - /> + <RelativeLayout android:id="@+id/bottom_actions" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/notif_half_shelf_row.xml b/packages/SystemUI/res/layout/notif_half_shelf_row.xml index 87de28650662..245d1579d5a3 100644 --- a/packages/SystemUI/res/layout/notif_half_shelf_row.xml +++ b/packages/SystemUI/res/layout/notif_half_shelf_row.xml @@ -21,61 +21,76 @@ android:layout_height="wrap_content" android:padding="8dp" android:clickable="true" - android:orientation="horizontal" - android:foreground="?android:attr/selectableItemBackground" > - - <!-- This is where an icon would go *if we wanted one* **wink** --> - <Space - android:id="@+id/icon" - android:layout_height="48dp" - android:layout_width="48dp" - android:layout_gravity="center_vertical" - android:padding="8dp" - /> + android:orientation="vertical" + android:foreground="?android:attr/selectableItemBackground" + > - <RelativeLayout - android:id="@+id/description_container" + <LinearLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_width="0dp" - android:layout_weight="1" - android:layout_gravity="center_vertical" - android:gravity="left|center_vertical" - android:orientation="vertical" - > - <TextView - android:id="@+id/channel_name" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:paddingBottom="0dp" - android:paddingStart="8dp" - android:paddingEnd="8dp" - android:gravity="center_vertical|start" - android:textSize="14sp" - android:ellipsize="end" - android:maxLines="1" - style="@style/TextAppearance.NotificationImportanceChannel" + android:orientation="horizontal"> + + <!-- This is where an icon would go *if we wanted one* **wink** --> + <Space + android:id="@+id/icon" + android:layout_height="48dp" + android:layout_width="48dp" + android:layout_gravity="center_vertical" + android:padding="8dp" /> - <TextView - android:id="@+id/channel_description" + <RelativeLayout + android:id="@+id/description_container" android:layout_height="wrap_content" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_gravity="center_vertical" + android:gravity="left|center_vertical" + android:orientation="vertical" + > + <TextView + android:id="@+id/channel_name" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:paddingBottom="0dp" + android:paddingStart="8dp" + android:paddingEnd="8dp" + android:gravity="center_vertical|start" + android:ellipsize="end" + android:maxLines="1" + android:fontFamily="@*android:string/config_headlineFontFamily" + android:textColor="?android:attr/textColorPrimary" + android:textSize="16sp" + /> + + <TextView + android:id="@+id/channel_description" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:paddingStart="8dp" + android:paddingEnd="8dp" + android:gravity="center_vertical|start" + android:ellipsize="end" + android:maxLines="1" + android:layout_below="@id/channel_name" + android:fontFamily="@*android:string/config_bodyFontFamily" + android:textColor="?android:attr/textColorSecondary" + android:textSize="14sp" + /> + </RelativeLayout> + + <Switch + android:id="@+id/toggle" + android:layout_height="48dp" android:layout_width="wrap_content" - android:paddingStart="8dp" - android:paddingEnd="8dp" - android:gravity="center_vertical|start" - android:textSize="14sp" - android:ellipsize="end" - android:maxLines="1" - android:layout_below="@id/channel_name" - style="@style/TextAppearance.NotificationImportanceApp" + android:layout_gravity="center_vertical" + android:padding="8dp" /> - </RelativeLayout> - - <Switch - android:id="@+id/toggle" - android:layout_height="48dp" - android:layout_width="wrap_content" - android:layout_gravity="center_vertical" - android:padding="8dp" + </LinearLayout> + <!-- divider view --> + <View + android:layout_width="match_parent" + android:layout_height=".5dp" + android:background="@*android:color/background_device_default_light" /> </com.android.systemui.statusbar.notification.row.ChannelRow> diff --git a/packages/SystemUI/res/layout/people_tile_with_suppression_detail_content_horizontal.xml b/packages/SystemUI/res/layout/people_tile_with_suppression_detail_content_horizontal.xml new file mode 100644 index 000000000000..b53c88beee9e --- /dev/null +++ b/packages/SystemUI/res/layout/people_tile_with_suppression_detail_content_horizontal.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?><!-- +~ Copyright (C) 2021 The Android Open Source Project +~ +~ Licensed under the Apache License, Version 2.0 (the "License"); +~ you may not use this file except in compliance with the License. +~ You may obtain a copy of the License at +~ +~ http://www.apache.org/licenses/LICENSE-2.0 +~ +~ Unless required by applicable law or agreed to in writing, software +~ distributed under the License is distributed on an "AS IS" BASIS, +~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +~ See the License for the specific language governing permissions and +~ limitations under the License. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center" + android:id="@+id/item" + android:background="@drawable/people_tile_suppressed_background" + android:clipToOutline="true" + android:padding="8dp" + android:orientation="horizontal"> + + <ImageView + android:id="@+id/person_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + <TextView + android:gravity="start" + android:id="@+id/text_content" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:ellipsize="end" + android:maxLines="2" + android:singleLine="false" + android:text="@string/empty_status" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title" + android:textColor="?android:attr/textColorPrimary" + android:textSize="@dimen/content_text_size_for_medium" /> + +</LinearLayout> diff --git a/packages/SystemUI/res/layout/people_tile_with_suppression_detail_content_vertical.xml b/packages/SystemUI/res/layout/people_tile_with_suppression_detail_content_vertical.xml new file mode 100644 index 000000000000..c79cdad0d1f7 --- /dev/null +++ b/packages/SystemUI/res/layout/people_tile_with_suppression_detail_content_vertical.xml @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="utf-8"?><!-- +~ Copyright (C) 2021 The Android Open Source Project +~ +~ Licensed under the Apache License, Version 2.0 (the "License"); +~ you may not use this file except in compliance with the License. +~ You may obtain a copy of the License at +~ +~ http://www.apache.org/licenses/LICENSE-2.0 +~ +~ Unless required by applicable law or agreed to in writing, software +~ distributed under the License is distributed on an "AS IS" BASIS, +~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +~ See the License for the specific language governing permissions and +~ limitations under the License. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center" + android:id="@+id/item" + android:background="@drawable/people_tile_suppressed_background" + android:clipToOutline="true" + android:padding="8dp" + android:orientation="vertical"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="0dp" + android:layout_weight="1"/> + + <ImageView + android:id="@+id/person_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + + <TextView + android:gravity="center" + android:id="@+id/text_content" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:ellipsize="end" + android:maxLines="2" + android:singleLine="false" + android:text="@string/empty_status" + android:layout_marginTop="@dimen/padding_between_suppressed_layout_items" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title" + android:textColor="?android:attr/textColorSecondary" + android:textSize="@dimen/content_text_size_for_large" /> + + <ImageView + android:id="@+id/predefined_icon" + android:tint="?android:attr/textColorSecondary" + android:layout_marginTop="@dimen/padding_between_suppressed_layout_items" + android:layout_width="@dimen/regular_predefined_icon" + android:layout_height="@dimen/regular_predefined_icon" + tools:ignore="UseAppTint" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="0dp" + android:layout_weight="1"/> + +</LinearLayout> diff --git a/packages/SystemUI/res/layout/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-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 492e673a6f5c..f8d06ce76c24 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"Voeg gebruiker by"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Nuwe gebruiker"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Verwyder gas?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Stel gassessie terug?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle programme en data in hierdie sessie sal uitgevee word."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Verwyder"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Stel terug"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welkom terug, gas!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Wiil jy jou sessie voortsit?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Begin van voor af"</string> 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-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 72936da5d4fe..fa528fd9037a 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/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-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index e67d0a36efa1..9502454d1c8c 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -89,7 +89,7 @@ <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Skrinşot çəkməyə tətbiq və ya təşkilat tərəfindən icazə verilmir"</string> <string name="screenshot_edit_label" msgid="8754981973544133050">"Redaktə edin"</string> <string name="screenshot_edit_description" msgid="3333092254706788906">"Skrinşota düzəliş edin"</string> - <string name="screenshot_scroll_label" msgid="2930198809899329367">"Daha çoxunu əhatə edin"</string> + <string name="screenshot_scroll_label" msgid="2930198809899329367">"Genişləndirin"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Ekran şəklini ötürün"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekran şəklinə önbaxış"</string> <string name="screenshot_top_boundary_pct" msgid="2520148599096479332">"Yuxarı sərhəd <xliff:g id="PERCENT">%1$d</xliff:g> faiz"</string> @@ -100,7 +100,7 @@ <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Ekran çəkilişi emal edilir"</string> <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekranın video çəkimi ərzində silinməyən bildiriş"</string> <string name="screenrecord_start_label" msgid="1750350278888217473">"Yazmağa başlanılsın?"</string> - <string name="screenrecord_description" msgid="1123231719680353736">"Yazarkən Android Sistemi ekranınızda görünən və ya cihazınızda göstərilən istənilən həssas məlumatı qeydə ala bilər. Buraya parollar, ödəniş məlumatı, fotolar, mesajlar və audio daxildir."</string> + <string name="screenrecord_description" msgid="1123231719680353736">"Ekranda görünən və ya cihazda oxudulan şəxsi məlumat (parol, bank hesabı, mesaj, fotoşəkil və sair) videoyazıya düşə bilər."</string> <string name="screenrecord_audio_label" msgid="6183558856175159629">"Audio yazın"</string> <string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Cihaz audiosu"</string> <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Cihazınızdan gələn musiqi, zənglər və zəng melodiyaları kimi səslər"</string> @@ -109,7 +109,7 @@ <string name="screenrecord_start" msgid="330991441575775004">"Başlayın"</string> <string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"Ekran yazılır"</string> <string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"Ekran və audio yazılır"</string> - <string name="screenrecord_taps_label" msgid="1595690528298857649">"Ekranda toxunuşları göstərin"</string> + <string name="screenrecord_taps_label" msgid="1595690528298857649">"Ekrana toxunuş göstərilsin"</string> <string name="screenrecord_stop_text" msgid="6549288689506057686">"Dayandırmaq üçün toxunun"</string> <string name="screenrecord_stop_label" msgid="72699670052087989">"Dayandırın"</string> <string name="screenrecord_pause_label" msgid="6004054907104549857">"Dayandırın"</string> @@ -480,12 +480,10 @@ <string name="user_add_user" msgid="4336657383006913022">"İstifadəçi əlavə edin"</string> <string name="user_new_user_name" msgid="2019166282704195789">"Yeni istifadəçi"</string> <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Qonaq silinsin?"</string> - <!-- no translation found for guest_reset_guest_dialog_title (8904781614074479690) --> - <skip /> + <string name="guest_reset_guest_dialog_title" msgid="8904781614074479690">"Qonaq sessiyası sıfırlansın?"</string> <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu sessiyada bütün tətbiqlər və data silinəcək."</string> <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Yığışdır"</string> - <!-- no translation found for guest_reset_guest_dialog_remove (4359825585658228699) --> - <skip /> + <string name="guest_reset_guest_dialog_remove" msgid="4359825585658228699">"Sıfırlayın"</string> <string name="guest_wipe_session_title" msgid="7147965814683990944">"Xoş gəlmisiniz!"</string> <string name="guest_wipe_session_message" msgid="3393823610257065457">"Sessiya davam etsin?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Yenidən başlayın"</string> @@ -609,15 +607,15 @@ <string name="accessibility_output_chooser" msgid="7807898688967194183">"Çıxış cihazına keçin"</string> <string name="screen_pinning_title" msgid="9058007390337841305">"Tətbiq bərkidilib"</string> <string name="screen_pinning_description" msgid="8699395373875667743">"Sancaq götürülənə qədər bu görünəcək. Sancağı götürmək üçün Geri və İcmal düymələrinə basıb saxlayın."</string> - <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"Sancaq götürülənə qədər bu görünəcək. Sancağı götürmək üçün Geri və Əsas səhifə düymələrinə basıb saxlayın."</string> - <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Bu, onu çıxarana qədər görünəcək. Çıxarmaq üçün yuxarı sürüşdürün & basıb saxlayın."</string> - <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"Sancaq götürülənə qədər bu görünəcək. Sancağı götürmək üçün Geri düyməsinə basıb saxlayın."</string> - <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Sancaq götürülənə qədər bu görünəcək. Sancağı götürmək üçün Əsas səhifə düyməsinə basıb saxlayın."</string> - <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Şəxsi məlumatlar (məsələn, kontaktlar və e-poçt məzmunu) əlçatan ola bilər."</string> + <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"\"Geri\" və \"Əsas ekran\" düymələrinin davamlı basılması ilə çıxarılana qədər tətbiq göz önündə qalır."</string> + <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Yuxarı sürüşdürülüb saxlanılana ilə çıxarılana qədər tətbiq göz önündə qalır."</string> + <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"\"İcmal\" düyməsinin davamlı basılması ilə çıxarılana qədər tətbiq göz önündə qalır."</string> + <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"\"Əsas ekran\" düyməsinin davamlı basılması ilə çıxarılana qədər tətbiq göz önündə qalır."</string> + <string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Şəxsi məlumatlar (məsələn, kontaktlar və e-poçt məzmunu) ekranda görünə bilər."</string> <string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"Bərkidilmiş tətbiq digər tətbiqləri aça bilər."</string> <string name="screen_pinning_toast" msgid="8177286912533744328">"Bu tətbiqi çıxarmaq üçün Geri və İcmal düymələrinə basıb saxlayın"</string> <string name="screen_pinning_toast_recents_invisible" msgid="6850978077443052594">"Bu tətbiqi çıxarmaq üçün Geri və Əsas ekran düymələrinə basıb saxlayın"</string> - <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Bu tətbiqi çıxarmaq üçün yuxarı sürüşdürüb saxlayın"</string> + <string name="screen_pinning_toast_gesture_nav" msgid="170699893395336705">"Tətbiqi çıxarmaq üçün yuxarı sürüşdürüb saxlayın"</string> <string name="screen_pinning_positive" msgid="3285785989665266984">"Anladım!"</string> <string name="screen_pinning_negative" msgid="6882816864569211666">"Yox, çox sağ olun"</string> <string name="screen_pinning_start" msgid="7483998671383371313">"Tətbiq bərkidildi"</string> @@ -1037,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> @@ -1048,7 +1046,7 @@ <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Kənara daşıyıb göstərin"</string> <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"keçirin"</string> <string name="quick_controls_title" msgid="7095074621086860062">"Əsas səhifə nizamlayıcıları"</string> - <string name="controls_providers_title" msgid="6879775889857085056">"Nizamlayıcıları əlavə etmək üçün tətbiq seçin"</string> + <string name="controls_providers_title" msgid="6879775889857085056">"Kontrol əlavə etmək üçün tətbiq seçin"</string> <plurals name="controls_number_of_favorites" formatted="false" msgid="1057347832073807380"> <item quantity="other"><xliff:g id="NUMBER_1">%s</xliff:g> nizamlayıcı əlavə edilib.</item> <item quantity="one"><xliff:g id="NUMBER_0">%s</xliff:g> nizamlayıcı əlavə edilib.</item> @@ -1062,8 +1060,8 @@ <string name="accessibility_control_move" msgid="8980344493796647792">"<xliff:g id="NUMBER">%d</xliff:g> mövqeyinə keçirin"</string> <string name="controls_favorite_default_title" msgid="967742178688938137">"Nizamlayıcılar"</string> <string name="controls_favorite_subtitle" msgid="6481675111056961083">"Sürətli Ayarlardan giriş üçün nizamlayıcıları seçin"</string> - <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Nizamlayıcıları yenidən tənzimləmək üçün tutub sürüşdürün"</string> - <string name="controls_favorite_removed" msgid="5276978408529217272">"Bütün nizamlayıcılar silindi"</string> + <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Vidcetləri daşıyaraq yerini dəyişin"</string> + <string name="controls_favorite_removed" msgid="5276978408529217272">"Kontrol vidcetləri silindi"</string> <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Dəyişikliklər yadda saxlanmadı"</string> <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Digər tətbiqlərə baxın"</string> <string name="controls_favorite_load_error" msgid="5126216176144877419">"Nizamlayıcıları yükləmək mümkün olmadı. <xliff:g id="APP">%s</xliff:g> tətbiqinə toxunaraq tətbiq ayarlarının dəyişmədiyinə əmin olun."</string> @@ -1105,8 +1103,8 @@ <string name="controls_error_failed" msgid="960228639198558525">"Xəta, yenidən cəhd edin"</string> <string name="controls_in_progress" msgid="4421080500238215939">"Davam edir"</string> <string name="controls_added_tooltip" msgid="5866098408470111984">"Yeni nizamlayıcılara baxmaq üçün Sürətli Ayarları açın"</string> - <string name="controls_menu_add" msgid="4447246119229920050">"Nizamlayıcılar əlavə edin"</string> - <string name="controls_menu_edit" msgid="890623986951347062">"Nizamlayıcıları redaktə edin"</string> + <string name="controls_menu_add" msgid="4447246119229920050">"Vidcet əlavə edin"</string> + <string name="controls_menu_edit" msgid="890623986951347062">"Vidcetlərə düzəliş edin"</string> <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Nəticələri əlavə edin"</string> <string name="media_output_dialog_group" msgid="5571251347877452212">"Qrup"</string> <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1 cihaz seçilib"</string> 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-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 56bec427a090..3f9269af1f50 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/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-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 b3caef3d695a..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> @@ -1048,8 +1046,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-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-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index 8e6293aa2e04..b98694e2fae7 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -21,8 +21,8 @@ It's fine to override this color since at that point the shade was dark. --> <color name="notification_legacy_background_color">@color/GM2_grey_900</color> - <!-- The color of the dividing line between grouped notifications while . --> - <color name="notification_divider_color">#212121</color> + <!-- The color of the dividing line between grouped notifications. --> + <color name="notification_divider_color">@*android:color/background_device_default_dark</color> <!-- The color of the gear shown behind a notification --> <color name="notification_gear_color">@color/GM2_grey_500</color> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 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-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index f503b38fdad2..89be1299e836 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/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-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/colors.xml b/packages/SystemUI/res/values/colors.xml index 2cf30581326b..e7edb0e6a57d 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -93,7 +93,7 @@ <color name="notification_material_background_dark_color">#ff333333</color> <!-- The color of the dividing line between grouped notifications. --> - <color name="notification_divider_color">#FF616161</color> + <color name="notification_divider_color">@*android:color/background_device_default_light</color> <!-- The color of the ripples on the untinted notifications --> <color name="notification_ripple_untinted_color">#28000000</color> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 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 9b860c75f476..4053ac3d586f 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -685,7 +685,7 @@ <dimen name="notification_shadow_radius">0dp</dimen> <!-- The alpha of the dividing line between child notifications of a notification group. --> - <item name="notification_divider_alpha" format="float" type="dimen">0.5</item> + <item name="notification_divider_alpha" format="float" type="dimen">1</item> <!-- The height of the divider between the individual notifications in a notification group. --> @@ -735,8 +735,8 @@ <!-- Minimum distance the user has to drag down to go to the full shade. --> <dimen name="keyguard_drag_down_min_distance">100dp</dimen> - <!-- The margin between the clock and the notifications on Keyguard.--> - <dimen name="keyguard_clock_notifications_margin">30dp</dimen> + <!-- The margin between the status view and the notifications on Keyguard.--> + <dimen name="keyguard_status_view_bottom_margin">20dp</dimen> <!-- Minimum margin between clock and status bar --> <dimen name="keyguard_clock_top_margin">36dp</dimen> <!-- The margin between top of clock and bottom of lock icon. --> @@ -1451,7 +1451,7 @@ <dimen name="people_space_messages_count_radius">12dp</dimen> <dimen name="people_space_widget_background_padding">6dp</dimen> <dimen name="required_width_for_medium">136dp</dimen> - <dimen name="required_width_for_large">138dp</dimen> + <dimen name="required_width_for_large">120dp</dimen> <dimen name="required_height_for_large">168dp</dimen> <dimen name="default_width">146dp</dimen> <dimen name="default_height">92dp</dimen> @@ -1470,10 +1470,12 @@ <dimen name="below_name_text_padding">16dp</dimen> <dimen name="above_notification_text_padding">22dp</dimen> <dimen name="regular_predefined_icon">18dp</dimen> - <dimen name="large_predefined_icon">24dp</dimen> + <dimen name="larger_predefined_icon">24dp</dimen> + <dimen name="largest_predefined_icon">32dp</dimen> <dimen name="availability_dot_status_padding">8dp</dimen> <dimen name="availability_dot_notification_padding">12dp</dimen> <dimen name="medium_content_padding_above_name">4dp</dimen> + <dimen name="padding_between_suppressed_layout_items">8dp</dimen> <!-- Accessibility floating menu --> <dimen name="accessibility_floating_menu_elevation">3dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 83dbad1677aa..5ab0f49e3ffe 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1148,18 +1148,12 @@ <!-- Title of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] --> <string name="guest_exit_guest_dialog_title">Remove guest?</string> - <!-- Title of the confirmation dialog when resetting guest session [CHAR LIMIT=NONE] --> - <string name="guest_reset_guest_dialog_title">Reset guest?</string> - <!-- Message of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] --> <string name="guest_exit_guest_dialog_message">All apps and data in this session will be deleted.</string> <!-- Label for button in confirmation dialog when exiting guest session [CHAR LIMIT=35] --> <string name="guest_exit_guest_dialog_remove">Remove</string> - <!-- Label for button in confirmation dialog when resetting guest session [CHAR LIMIT=35] --> - <string name="guest_reset_guest_dialog_remove">Reset</string> - <!-- Title of the notification when resuming an existing guest session [CHAR LIMIT=NONE] --> <string name="guest_wipe_session_title">Welcome back, guest!</string> @@ -2952,6 +2946,8 @@ <string name="people_tile_description">See recent messages, missed calls, and status updates</string> <!-- Title text displayed for the Conversation widget [CHAR LIMIT=50] --> <string name="people_tile_title">Conversation</string> + <!-- Text when the Conversation widget when Do Not Disturb is suppressing the notification. [CHAR LIMIT=50] --> + <string name="paused_by_dnd">Paused by Do Not Disturb</string> <!-- Content description text on the Conversation widget when a person has sent a new text message [CHAR LIMIT=150] --> <string name="new_notification_text_content_description"><xliff:g id="name" example="Anna">%1$s</xliff:g> sent a message</string> <!-- Content description text on the Conversation widget when a person has sent a new image message [CHAR LIMIT=150] --> 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/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index bb44b09f1bce..bc4ced452630 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -71,7 +71,6 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.plugins.GlobalActionsPanelPlugin; -import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -103,7 +102,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite private final LockPatternUtils mLockPatternUtils; private final KeyguardStateController mKeyguardStateController; - private final NotificationShadeDepthController mDepthController; private final SysUiState mSysUiState; private final ActivityStarter mActivityStarter; private final SysuiColorExtractor mSysuiColorExtractor; @@ -164,7 +162,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite IActivityManager iActivityManager, @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger, - NotificationShadeDepthController depthController, SysuiColorExtractor colorExtractor, IStatusBarService statusBarService, NotificationShadeWindowController notificationShadeWindowController, @@ -196,7 +193,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite iActivityManager, telecomManager, metricsLogger, - depthController, colorExtractor, statusBarService, notificationShadeWindowController, @@ -212,7 +208,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite mLockPatternUtils = lockPatternUtils; mKeyguardStateController = keyguardStateController; - mDepthController = depthController; mSysuiColorExtractor = colorExtractor; mStatusBarService = statusBarService; mNotificationShadeWindowController = notificationShadeWindowController; @@ -267,9 +262,8 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite protected ActionsDialogLite createDialog() { initDialogItems(); - mDepthController.setShowingHomeControls(true); ActionsDialog dialog = new ActionsDialog(getContext(), mAdapter, mOverflowAdapter, - this::getWalletViewController, mDepthController, mSysuiColorExtractor, + this::getWalletViewController, mSysuiColorExtractor, mStatusBarService, mNotificationShadeWindowController, mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter, getEventLogger(), getStatusBar()); @@ -336,16 +330,15 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite ActionsDialog(Context context, MyAdapter adapter, MyOverflowAdapter overflowAdapter, Provider<GlobalActionsPanelPlugin.PanelViewController> walletFactory, - NotificationShadeDepthController depthController, SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService, NotificationShadeWindowController notificationShadeWindowController, SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing, MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger, StatusBar statusBar) { super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions, - adapter, overflowAdapter, depthController, sysuiColorExtractor, - statusBarService, notificationShadeWindowController, sysuiState, - onRotateCallback, keyguardShowing, powerAdapter, uiEventLogger, null, + adapter, overflowAdapter, sysuiColorExtractor, statusBarService, + notificationShadeWindowController, sysuiState, onRotateCallback, + keyguardShowing, powerAdapter, uiEventLogger, null, statusBar); mWalletFactory = walletFactory; @@ -494,8 +487,6 @@ public class GlobalActionsDialog extends GlobalActionsDialogLite float animatedValue = animation.getAnimatedFraction(); int alpha = (int) (animatedValue * mScrimAlpha * 255); mBackgroundDrawable.setAlpha(alpha); - mDepthController.updateGlobalDialogVisibility(animatedValue, - mGlobalActionsLayout); }); ObjectAnimator xAnimator = diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index f30d6b13ad02..5acb3038b91b 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -119,7 +119,6 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.plugins.GlobalActionsPanelPlugin; import com.android.systemui.scrim.ScrimDrawable; -import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -190,7 +189,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene private final TelecomManager mTelecomManager; private final MetricsLogger mMetricsLogger; private final UiEventLogger mUiEventLogger; - private final NotificationShadeDepthController mDepthController; private final SysUiState mSysUiState; private final GlobalActionsInfoProvider mInfoProvider; @@ -329,7 +327,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene IActivityManager iActivityManager, @Nullable TelecomManager telecomManager, MetricsLogger metricsLogger, - NotificationShadeDepthController depthController, SysuiColorExtractor colorExtractor, IStatusBarService statusBarService, NotificationShadeWindowController notificationShadeWindowController, @@ -362,7 +359,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mMetricsLogger = metricsLogger; mUiEventLogger = uiEventLogger; mInfoProvider = infoProvider; - mDepthController = depthController; mSysuiColorExtractor = colorExtractor; mStatusBarService = statusBarService; mNotificationShadeWindowController = notificationShadeWindowController; @@ -652,11 +648,9 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene protected ActionsDialogLite createDialog() { initDialogItems(); - mDepthController.setShowingHomeControls(false); ActionsDialogLite dialog = new ActionsDialogLite(mContext, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite, - mAdapter, mOverflowAdapter, - mDepthController, mSysuiColorExtractor, + mAdapter, mOverflowAdapter, mSysuiColorExtractor, mStatusBarService, mNotificationShadeWindowController, mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter, mUiEventLogger, mInfoProvider, mStatusBar); @@ -2125,7 +2119,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene protected boolean mShowing; protected float mScrimAlpha; protected final NotificationShadeWindowController mNotificationShadeWindowController; - protected final NotificationShadeDepthController mDepthController; protected final SysUiState mSysUiState; private ListPopupWindow mOverflowPopup; private Dialog mPowerOptionsDialog; @@ -2181,7 +2174,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene ActionsDialogLite(Context context, int themeRes, MyAdapter adapter, MyOverflowAdapter overflowAdapter, - NotificationShadeDepthController depthController, SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService, NotificationShadeWindowController notificationShadeWindowController, SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing, @@ -2192,7 +2184,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mAdapter = adapter; mOverflowAdapter = overflowAdapter; mPowerOptionsAdapter = powerAdapter; - mDepthController = depthController; mColorExtractor = sysuiColorExtractor; mStatusBarService = statusBarService; mNotificationShadeWindowController = notificationShadeWindowController; @@ -2409,7 +2400,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene float animatedValue = animation.getAnimatedFraction(); int alpha = (int) (animatedValue * mScrimAlpha * 255); mBackgroundDrawable.setAlpha(alpha); - mDepthController.updateGlobalDialogVisibility(animatedValue, mGlobalActionsLayout); }); ObjectAnimator xAnimator = @@ -2439,7 +2429,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene float animatedValue = 1f - animation.getAnimatedFraction(); int alpha = (int) (animatedValue * mScrimAlpha * 255); mBackgroundDrawable.setAlpha(alpha); - mDepthController.updateGlobalDialogVisibility(animatedValue, mGlobalActionsLayout); }); float xOffset = mGlobalActionsLayout.getAnimationOffsetX(); @@ -2476,7 +2465,6 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene dismissOverflow(true); dismissPowerOptions(true); mNotificationShadeWindowController.setRequestTopUi(false, TAG); - mDepthController.updateGlobalDialogVisibility(0, null /* view */); mSysUiState.setFlag(SYSUI_STATE_GLOBAL_ACTIONS_SHOWING, false) .commitUpdate(mContext.getDisplayId()); super.dismiss(); diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index 1004e257c750..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. @@ -156,6 +157,12 @@ class MediaCarouselController @Inject constructor( } } + /** + * Update MediaCarouselScrollHandler.visibleToUser to reflect media card container visibility. + * It will be called when the container is out of view. + */ + lateinit var updateUserVisibility: () -> Unit + init { mediaFrame = inflateMediaCarousel() mediaCarousel = mediaFrame.requireViewById(R.id.media_carousel_scroller) @@ -177,6 +184,12 @@ class MediaCarouselController @Inject constructor( keysNeedRemoval.forEach { removePlayer(it) } keysNeedRemoval.clear() + // Update user visibility so that no extra impression will be logged when + // activeMediaIndex resets to 0 + if (this::updateUserVisibility.isInitialized) { + updateUserVisibility() + } + // Let's reset our scroll position mediaCarouselScrollHandler.scrollToStart() } @@ -187,16 +200,24 @@ class MediaCarouselController @Inject constructor( key: String, oldKey: String?, data: MediaData, - immediately: Boolean + immediately: Boolean, + isSsReactivated: Boolean ) { if (addOrUpdatePlayer(key, oldKey, data)) { MediaPlayerData.getMediaPlayer(key, null)?.let { logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED it.mInstanceId, /* isRecommendationCard */ false, - it.surfaceForSmartspaceLogging) + it.surfaceForSmartspaceLogging, + rank = MediaPlayerData.getMediaPlayerIndex(key)) } } + if (mediaCarouselScrollHandler.visibleToUser && + isSsReactivated && !mediaCarouselScrollHandler.qsExpanded) { + // It could happen that reactived media player isn't visible to user because + // of it is a resumption card. + logSmartspaceImpression(mediaCarouselScrollHandler.qsExpanded) + } val canRemove = data.isPlaying?.let { !it } ?: data.isClearable && !data.active if (canRemove && !Utils.useMediaResumption(context)) { // This view isn't playing, let's remove this! This happens e.g when @@ -217,17 +238,24 @@ 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 { logSmartspaceCardReported(759, // SMARTSPACE_CARD_RECEIVED it.mInstanceId, /* isRecommendationCard */ true, - it.surfaceForSmartspaceLogging) - } - if (mediaCarouselScrollHandler.visibleToUser) { - logSmartspaceImpression() + it.surfaceForSmartspaceLogging, + rank = MediaPlayerData.getMediaPlayerIndex(key)) + + if (mediaCarouselScrollHandler.visibleToUser && + mediaCarouselScrollHandler.visibleMediaIndex == + MediaPlayerData.getMediaPlayerIndex(key)) { + logSmartspaceCardReported(800, // SMARTSPACE_CARD_SEEN + it.mInstanceId, + /* isRecommendationCard */ true, + it.surfaceForSmartspaceLogging) + } } } else { onSmartspaceMediaDataRemoved(data.targetId, immediately = true) @@ -239,7 +267,7 @@ 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 { @@ -357,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 @@ -644,17 +672,17 @@ class MediaCarouselController @Inject constructor( } /** - * Log the user impression for media card. + * Log the user impression for media card at visibleMediaIndex. */ - fun logSmartspaceImpression() { + fun logSmartspaceImpression(qsExpanded: Boolean) { val visibleMediaIndex = mediaCarouselScrollHandler.visibleMediaIndex if (MediaPlayerData.players().size > visibleMediaIndex) { val mediaControlPanel = MediaPlayerData.players().elementAt(visibleMediaIndex) - val isMediaActive = - MediaPlayerData.playerKeys().elementAt(visibleMediaIndex).data?.active + val hasActiveMediaOrRecommendationCard = + MediaPlayerData.hasActiveMediaOrRecommendationCard() val isRecommendationCard = mediaControlPanel.recommendationViewHolder != null - if (!isRecommendationCard && !isMediaActive) { - // Media control card time out or swiped away + if (!hasActiveMediaOrRecommendationCard && !qsExpanded) { + // Skip logging if on LS or QQS, and there is no active media card return } logSmartspaceCardReported(800, // SMARTSPACE_CARD_SEEN @@ -672,6 +700,13 @@ class MediaCarouselController @Inject constructor( surface: Int, rank: Int = mediaCarouselScrollHandler.visibleMediaIndex ) { + // Only log media resume card when Smartspace data is available + if (!isRecommendationCard && + !mediaManager.smartspaceMediaData.isActive && + MediaPlayerData.smartspaceMediaData == null) { + return + } + /* ktlint-disable max-line-length */ SysUiStatsLog.write(SysUiStatsLog.SMARTSPACE_CARD_REPORTED, eventId, @@ -770,6 +805,16 @@ internal object MediaPlayerData { return mediaData.get(key)?.let { mediaPlayers.get(it) } } + fun getMediaPlayerIndex(key: String): Int { + val sortKey = mediaData.get(key) + mediaPlayers.entries.forEachIndexed { index, e -> + if (e.key == sortKey) { + return index + } + } + return -1 + } + fun removeMediaPlayer(key: String) = mediaData.remove(key)?.let { if (it.isSsMediaRec) { smartspaceMediaData = null @@ -808,4 +853,15 @@ internal object MediaPlayerData { mediaData.clear() mediaPlayers.clear() } + + /* Returns true if there is active media player card or recommendation card */ + fun hasActiveMediaOrRecommendationCard(): Boolean { + if (smartspaceMediaData != null && smartspaceMediaData?.isActive!!) { + return true + } + if (firstActiveMediaIndex() != -1) { + return true + } + return false + } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt index eb3549789c19..b0d4cb1c9818 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt @@ -62,7 +62,7 @@ class MediaCarouselScrollHandler( private val closeGuts: (immediate: Boolean) -> Unit, private val falsingCollector: FalsingCollector, private val falsingManager: FalsingManager, - private val logSmartspaceImpression: () -> Unit + private val logSmartspaceImpression: (Boolean) -> Unit ) { /** * Is the view in RTL @@ -195,18 +195,22 @@ class MediaCarouselScrollHandler( if (playerWidthPlusPadding == 0) { return } + val relativeScrollX = scrollView.relativeScrollX onMediaScrollingChanged(relativeScrollX / playerWidthPlusPadding, relativeScrollX % playerWidthPlusPadding) } } + /** + * Whether the media card is visible to user if any + */ var visibleToUser: Boolean = false - set(value) { - if (field != value) { - field = value - } - } + + /** + * Whether the quick setting is expanded or not + */ + var qsExpanded: Boolean = false init { gestureDetector = GestureDetectorCompat(scrollView.context, gestureListener) @@ -471,7 +475,7 @@ class MediaCarouselScrollHandler( val oldIndex = visibleMediaIndex visibleMediaIndex = newIndex if (oldIndex != visibleMediaIndex && visibleToUser) { - logSmartspaceImpression() + logSmartspaceImpression(qsExpanded) } closeGuts(false) updatePlayerVisibilities() diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 19190cd4a17d..19a67e95a496 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -266,7 +266,7 @@ public class MediaControlPanel { } mKey = key; MediaSession.Token token = data.getToken(); - mInstanceId = data.getPackageName().hashCode(); + mInstanceId = SmallHash.hash(data.getPackageName()); mBackgroundColor = data.getBackgroundColor(); if (mToken == null || !mToken.equals(token)) { @@ -504,7 +504,7 @@ public class MediaControlPanel { return; } - mInstanceId = data.getTargetId().hashCode(); + mInstanceId = SmallHash.hash(data.getTargetId()); mBackgroundColor = data.getBackgroundColor(); TransitionLayout recommendationCard = mRecommendationViewHolder.getRecommendations(); recommendationCard.setBackgroundTintList(ColorStateList.valueOf(mBackgroundColor)); diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt index ee1d3ea87da8..296bfda89432 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt @@ -31,7 +31,8 @@ class MediaDataCombineLatest @Inject constructor() : MediaDataManager.Listener, key: String, oldKey: String?, data: MediaData, - immediately: Boolean + immediately: Boolean, + isSsReactivated: Boolean ) { if (oldKey != null && oldKey != key && entries.contains(oldKey)) { entries[key] = data to entries.remove(oldKey)?.second diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt index a611b600f47f..c8deb014f781 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt @@ -83,7 +83,8 @@ class MediaDataFilter @Inject constructor( key: String, oldKey: String?, data: MediaData, - immediately: Boolean + immediately: Boolean, + isSsReactivated: Boolean ) { if (oldKey != null && oldKey != key) { allEntries.remove(oldKey) @@ -101,7 +102,7 @@ class MediaDataFilter @Inject constructor( // Notify listeners listeners.forEach { - it.onMediaDataLoaded(key, oldKey, data) + it.onMediaDataLoaded(key, oldKey, data, isSsReactivated = isSsReactivated) } } @@ -118,6 +119,8 @@ class MediaDataFilter @Inject constructor( // Override the pass-in value here, as the order of Smartspace card is only determined here. var shouldPrioritizeMutable = false smartspaceMediaData = data + // Override the pass-in value here, as the Smartspace reactivation could only happen here. + var isSsReactivated = false // Before forwarding the smartspace target, first check if we have recently inactive media val sorted = userEntries.toSortedMap(compareBy { @@ -137,9 +140,13 @@ class MediaDataFilter @Inject constructor( // Notify listeners to consider this media active Log.d(TAG, "reactivating $lastActiveKey instead of smartspace") reactivatedKey = lastActiveKey + if (MediaPlayerData.firstActiveMediaIndex() == -1) { + isSsReactivated = true + } val mediaData = sorted.get(lastActiveKey)!!.copy(active = true) listeners.forEach { - it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData) + it.onMediaDataLoaded(lastActiveKey, lastActiveKey, mediaData, + isSsReactivated = isSsReactivated) } } else { // Mark to prioritize Smartspace card if no recent media. diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 28d336ea18d5..df1b07f3e0b2 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -148,7 +148,7 @@ class MediaDataManager( private val internalListeners: MutableSet<Listener> = mutableSetOf() private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap() // There should ONLY be at most one Smartspace media recommendation. - private var smartspaceMediaData: SmartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA + var smartspaceMediaData: SmartspaceMediaData = EMPTY_SMARTSPACE_MEDIA_DATA private var smartspaceSession: SmartspaceSession? = null private var allowMediaRecommendations = Utils.allowMediaRecommendations(context) @@ -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 @@ -824,12 +826,16 @@ class MediaDataManager( * @param immediately indicates should apply the UI changes immediately, otherwise wait * until the next refresh-round before UI becomes visible. True by default to take in place * immediately. + * + * @param isSsReactivated indicates transition from a state with no active media players to + * a state with active media players upon receiving Smartspace media data. */ fun onMediaDataLoaded( key: String, oldKey: String?, data: MediaData, - immediately: Boolean = true + immediately: Boolean = true, + isSsReactivated: Boolean = false ) {} /** @@ -879,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) { @@ -889,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/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt index 52ecbea05924..292b0e291244 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt @@ -67,7 +67,8 @@ class MediaDeviceManager @Inject constructor( key: String, oldKey: String?, data: MediaData, - immediately: Boolean + immediately: Boolean, + isSsReactivated: Boolean ) { if (oldKey != null && oldKey != key) { val oldEntry = entries.remove(oldKey) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt index edbf18789e28..186f961ff1b6 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt @@ -220,14 +220,11 @@ class MediaHierarchyManager @Inject constructor( set(value) { if (field != value) { field = value + mediaCarouselController.mediaCarouselScrollHandler.qsExpanded = value } // qs is expanded on LS shade and HS shade if (value && (isLockScreenShadeVisibleToUser() || isHomeScreenShadeVisibleToUser())) { - mediaCarouselController.logSmartspaceImpression() - } - // Release shade and back to lock screen - if (isLockScreenVisibleToUser()) { - mediaCarouselController.logSmartspaceImpression() + mediaCarouselController.logSmartspaceImpression(value) } mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser() } @@ -409,7 +406,7 @@ class MediaHierarchyManager @Inject constructor( updateTargetState() // Enters shade from lock screen if (newState == StatusBarState.SHADE_LOCKED && isLockScreenShadeVisibleToUser()) { - mediaCarouselController.logSmartspaceImpression() + mediaCarouselController.logSmartspaceImpression(qsExpanded) } mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser() } @@ -423,7 +420,7 @@ class MediaHierarchyManager @Inject constructor( dozeAnimationRunning = false // Enters lock screen from screen off if (isLockScreenVisibleToUser()) { - mediaCarouselController.logSmartspaceImpression() + mediaCarouselController.logSmartspaceImpression(qsExpanded) } } else { updateDesiredLocation() @@ -436,11 +433,7 @@ class MediaHierarchyManager @Inject constructor( override fun onExpandedChanged(isExpanded: Boolean) { // Enters shade from home screen if (isHomeScreenShadeVisibleToUser()) { - mediaCarouselController.logSmartspaceImpression() - } - // Back to lock screen from bouncer - if (isLockScreenVisibleToUser()) { - mediaCarouselController.logSmartspaceImpression() + mediaCarouselController.logSmartspaceImpression(qsExpanded) } mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser() } @@ -465,6 +458,10 @@ class MediaHierarchyManager @Inject constructor( goingToSleep = false } }) + + mediaCarouselController.updateUserVisibility = { + mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser() + } } private fun updateConfiguration() { @@ -663,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/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt index 43e21424c45e..ff085c36ef9c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt @@ -60,7 +60,8 @@ class MediaHost constructor( key: String, oldKey: String?, data: MediaData, - immediately: Boolean + immediately: Boolean, + isSsReactivated: Boolean ) { if (immediately) { updateViewVisibility() diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt index 0da84fbac600..ab568c8c5a85 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt @@ -159,7 +159,8 @@ class MediaResumeListener @Inject constructor( key: String, oldKey: String?, data: MediaData, - immediately: Boolean + immediately: Boolean, + isSsReactivated: Boolean ) { if (useMediaResumption) { // If this had been started from a resume state, disconnect now that it's live diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt index a4f33e354b68..8bddde839817 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt @@ -95,7 +95,8 @@ class MediaSessionBasedFilter @Inject constructor( key: String, oldKey: String?, data: MediaData, - immediately: Boolean + immediately: Boolean, + isSsReactivated: Boolean ) { backgroundExecutor.execute { data.token?.let { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt index bbea140ecfaf..9a3919326cbd 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt @@ -54,7 +54,8 @@ class MediaTimeoutListener @Inject constructor( key: String, oldKey: String?, data: MediaData, - immediately: Boolean + immediately: Boolean, + isSsReactivated: Boolean ) { var reusedListener: PlaybackStateListener? = null diff --git a/packages/SystemUI/src/com/android/systemui/media/SmallHash.java b/packages/SystemUI/src/com/android/systemui/media/SmallHash.java new file mode 100644 index 000000000000..de7aac609955 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/SmallHash.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media; + +import java.util.Objects; + +/** + * A simple hash function for use in privacy-sensitive logging. + */ +public final class SmallHash { + // Hashes will be in the range [0, MAX_HASH). + public static final int MAX_HASH = (1 << 13); + + /** Return Small hash of the string, if non-null, or 0 otherwise. */ + public static int hash(String in) { + return hash(Objects.hashCode(in)); + } + + /** + * Maps in to the range [0, MAX_HASH), keeping similar values distinct. + * + * @param in An arbitrary integer. + * @return in mod MAX_HASH, signs chosen to stay in the range [0, MAX_HASH). + */ + public static int hash(int in) { + return Math.abs(Math.floorMod(in, MAX_HASH)); + } + + private SmallHash() {} +} diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java index 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/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index e53cc0b72e3b..917a060f1f1d 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java @@ -494,7 +494,8 @@ public class PeopleSpaceUtils { * match the data by {@link ContactsContract.ContactsColumns#LOOKUP_KEY} key to ensure proper * matching across all the Contacts DB tables. */ - private static List<String> getContactLookupKeysWithBirthdaysToday(Context context) { + @VisibleForTesting + public static List<String> getContactLookupKeysWithBirthdaysToday(Context context) { List<String> lookupKeysWithBirthdaysToday = new ArrayList<>(1); String today = new SimpleDateFormat("MM-dd").format(new Date()); String[] projection = new String[]{ @@ -503,14 +504,20 @@ public class PeopleSpaceUtils { String where = ContactsContract.Data.MIMETYPE + "= ? AND " + ContactsContract.CommonDataKinds.Event.TYPE + "=" - + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY + " AND substr(" - + ContactsContract.CommonDataKinds.Event.START_DATE + ",6) = ?"; + + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY + " AND (substr(" + // Birthdays stored with years will match this format + + ContactsContract.CommonDataKinds.Event.START_DATE + ",6) = ? OR substr(" + // Birthdays stored without years will match this format + + ContactsContract.CommonDataKinds.Event.START_DATE + ",3) = ? )"; String[] selection = - new String[]{ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE, today}; + new String[]{ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE, today, + today}; Cursor cursor = null; try { - cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, - projection, where, selection, null); + cursor = context + .getContentResolver() + .query(ContactsContract.Data.CONTENT_URI, + projection, where, selection, null); while (cursor != null && cursor.moveToNext()) { String lookupKey = cursor.getString( cursor.getColumnIndex(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY)); diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java b/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java index 96aeb60ae93c..4ee951f3cdb1 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java @@ -211,7 +211,8 @@ class PeopleStoryIconFactory implements AutoCloseable { @Override public void setColorFilter(ColorFilter colorFilter) { - // unimplemented + if (mAvatar != null) mAvatar.setColorFilter(colorFilter); + if (mBadgeIcon != null) mBadgeIcon.setColorFilter(colorFilter); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java index 32fdf0ea4de6..4a8775fc5bb4 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java @@ -29,6 +29,8 @@ import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT; import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH; import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT; import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH; +import static android.util.TypedValue.COMPLEX_UNIT_DIP; +import static android.util.TypedValue.COMPLEX_UNIT_PX; import static com.android.systemui.people.PeopleSpaceUtils.STARRED_CONTACT; import static com.android.systemui.people.PeopleSpaceUtils.VALID_CONTACT; @@ -43,11 +45,16 @@ import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; +import android.graphics.text.LineBreaker; import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; +import android.text.StaticLayout; +import android.text.TextPaint; import android.text.TextUtils; import android.util.IconDrawableFactory; import android.util.Log; @@ -58,8 +65,11 @@ import android.view.View; import android.widget.RemoteViews; import android.widget.TextView; +import androidx.annotation.DimenRes; +import androidx.annotation.Px; import androidx.core.graphics.drawable.RoundedBitmapDrawable; import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; +import androidx.core.math.MathUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.launcher3.icons.FastBitmapDrawable; @@ -197,12 +207,16 @@ public class PeopleTileViewHelper { */ private RemoteViews getViewForTile() { if (DEBUG) Log.d(TAG, "Creating view for tile key: " + mKey.toString()); - if (mTile == null || mTile.isPackageSuspended() || mTile.isUserQuieted() - || isDndBlockingTileData(mTile)) { + if (mTile == null || mTile.isPackageSuspended() || mTile.isUserQuieted()) { if (DEBUG) Log.d(TAG, "Create suppressed view: " + mTile); return createSuppressedView(); } + if (isDndBlockingTileData(mTile)) { + if (DEBUG) Log.d(TAG, "Create dnd view"); + return createDndRemoteViews().mRemoteViews; + } + if (Objects.equals(mTile.getNotificationCategory(), CATEGORY_MISSED_CALL)) { if (DEBUG) Log.d(TAG, "Create missed call view"); return createMissedCallRemoteViews(); @@ -236,7 +250,9 @@ public class PeopleTileViewHelper { return createLastInteractionRemoteViews(); } - private boolean isDndBlockingTileData(PeopleSpaceTile tile) { + private static boolean isDndBlockingTileData(@Nullable PeopleSpaceTile tile) { + if (tile == null) return false; + int notificationPolicyState = tile.getNotificationPolicyState(); if ((notificationPolicyState & PeopleSpaceTile.SHOW_CONVERSATIONS) != 0) { // Not in DND, or all conversations @@ -265,7 +281,7 @@ public class PeopleTileViewHelper { private RemoteViews createSuppressedView() { RemoteViews views; - if (mTile.isUserQuieted()) { + if (mTile != null && mTile.isUserQuieted()) { views = new RemoteViews(mContext.getPackageName(), R.layout.people_tile_work_profile_quiet_layout); } else { @@ -413,6 +429,11 @@ public class PeopleTileViewHelper { int avatarWidthSpace = mWidth - (14 + 14); avatarSize = Math.min(avatarHeightSpace, avatarWidthSpace); } + + if (isDndBlockingTileData(mTile)) { + avatarSize = createDndRemoteViews().mAvatarSize; + } + return Math.min(avatarSize, getSizeInDp(R.dimen.max_people_avatar_size)); } @@ -473,6 +494,87 @@ public class PeopleTileViewHelper { return views; } + private RemoteViewsAndSizes createDndRemoteViews() { + boolean isHorizontal = mLayoutSize == LAYOUT_MEDIUM; + int layoutId = isHorizontal + ? R.layout.people_tile_with_suppression_detail_content_horizontal + : R.layout.people_tile_with_suppression_detail_content_vertical; + RemoteViews views = new RemoteViews(mContext.getPackageName(), layoutId); + + int outerPadding = mLayoutSize == LAYOUT_LARGE ? 16 : 8; + int outerPaddingPx = dpToPx(outerPadding); + views.setViewPadding( + R.id.item, + outerPaddingPx, + outerPaddingPx, + outerPaddingPx, + outerPaddingPx); + + int mediumAvatarSize = getSizeInDp(R.dimen.avatar_size_for_medium); + int maxAvatarSize = getSizeInDp(R.dimen.max_people_avatar_size); + + String text = mContext.getString(R.string.paused_by_dnd); + views.setTextViewText(R.id.text_content, text); + + int textSizeResId = + mLayoutSize == LAYOUT_LARGE + ? R.dimen.content_text_size_for_large + : R.dimen.content_text_size_for_medium; + float textSizePx = mContext.getResources().getDimension(textSizeResId); + views.setTextViewTextSize(R.id.text_content, COMPLEX_UNIT_PX, textSizePx); + int lineHeight = getLineHeightFromResource(textSizeResId); + + int avatarSize; + if (isHorizontal) { + int maxTextHeight = mHeight - outerPadding; + views.setInt(R.id.text_content, "setMaxLines", maxTextHeight / lineHeight); + avatarSize = mediumAvatarSize; + } else { + int iconSize = + getSizeInDp( + mLayoutSize == LAYOUT_SMALL + ? R.dimen.regular_predefined_icon + : R.dimen.largest_predefined_icon); + int heightWithoutIcon = mHeight - 2 * outerPadding - iconSize; + int paddingBetweenElements = + getSizeInDp(R.dimen.padding_between_suppressed_layout_items); + int maxTextWidth = mWidth - outerPadding * 2; + int maxTextHeight = heightWithoutIcon - mediumAvatarSize - paddingBetweenElements * 2; + + int availableAvatarHeight; + int textHeight = estimateTextHeight(text, textSizeResId, maxTextWidth); + if (textHeight <= maxTextHeight) { + // If the text will fit, then display it and deduct its height from the space we + // have for the avatar. + availableAvatarHeight = heightWithoutIcon - textHeight - paddingBetweenElements * 2; + views.setViewVisibility(R.id.text_content, View.VISIBLE); + views.setInt(R.id.text_content, "setMaxLines", maxTextHeight / lineHeight); + views.setContentDescription(R.id.predefined_icon, null); + } else { + // If the height doesn't fit, then hide it. The dnd icon will still show. + availableAvatarHeight = heightWithoutIcon - paddingBetweenElements; + views.setViewVisibility(R.id.text_content, View.GONE); + // If we don't show the dnd text, set it as the content description on the icon + // for a11y. + views.setContentDescription(R.id.predefined_icon, text); + } + + int availableAvatarWidth = mWidth - outerPadding * 2; + avatarSize = + MathUtils.clamp( + /* value= */ Math.min(availableAvatarWidth, availableAvatarHeight), + /* min= */ dpToPx(10), + /* max= */ maxAvatarSize); + + views.setViewLayoutWidth(R.id.predefined_icon, iconSize, COMPLEX_UNIT_DIP); + views.setViewLayoutHeight(R.id.predefined_icon, iconSize, COMPLEX_UNIT_DIP); + views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_qs_dnd_on); + } + + return new RemoteViewsAndSizes(views, avatarSize); + } + + private RemoteViews createMissedCallRemoteViews() { RemoteViews views = setViewForContentLayout(new RemoteViews(mContext.getPackageName(), getLayoutForContent())); @@ -486,8 +588,8 @@ public class PeopleTileViewHelper { views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_phone_missed); if (mLayoutSize == LAYOUT_LARGE) { views.setInt(R.id.content, "setGravity", Gravity.BOTTOM); - views.setViewLayoutHeightDimen(R.id.predefined_icon, R.dimen.large_predefined_icon); - views.setViewLayoutWidthDimen(R.id.predefined_icon, R.dimen.large_predefined_icon); + views.setViewLayoutHeightDimen(R.id.predefined_icon, R.dimen.larger_predefined_icon); + views.setViewLayoutWidthDimen(R.id.predefined_icon, R.dimen.larger_predefined_icon); } setAvailabilityDotPadding(views, R.dimen.availability_dot_notification_padding); return views; @@ -932,6 +1034,14 @@ public class PeopleTileViewHelper { Drawable personDrawable = storyIcon.getPeopleTileDrawable(roundedDrawable, tile.getPackageName(), getUserId(tile), tile.isImportantConversation(), hasNewStory); + + if (isDndBlockingTileData(tile)) { + // If DND is blocking the conversation, then display the icon in grayscale. + ColorMatrix colorMatrix = new ColorMatrix(); + colorMatrix.setSaturation(0); + personDrawable.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); + } + return convertDrawableToBitmap(personDrawable); } @@ -960,4 +1070,69 @@ public class PeopleTileViewHelper { return context.getString(R.string.over_two_weeks_timestamp); } } + + /** + * Estimates the height (in dp) which the text will have given the text size and the available + * width. Returns Integer.MAX_VALUE if the estimation couldn't be obtained, as this is intended + * to be used an estimate of the maximum. + */ + private int estimateTextHeight( + CharSequence text, + @DimenRes int textSizeResId, + int availableWidthDp) { + StaticLayout staticLayout = buildStaticLayout(text, textSizeResId, availableWidthDp); + if (staticLayout == null) { + // Return max value (rather than e.g. -1) so the value can be used with <= bound checks. + return Integer.MAX_VALUE; + } + return pxToDp(staticLayout.getHeight()); + } + + /** + * Builds a StaticLayout for the text given the text size and available width. This can be used + * to obtain information about how TextView will lay out the text. Returns null if any error + * occurred creating a TextView. + */ + @Nullable + private StaticLayout buildStaticLayout( + CharSequence text, + @DimenRes int textSizeResId, + int availableWidthDp) { + try { + TextView textView = new TextView(mContext); + textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, + mContext.getResources().getDimension(textSizeResId)); + textView.setTextAppearance(android.R.style.TextAppearance_DeviceDefault); + TextPaint paint = textView.getPaint(); + return StaticLayout.Builder.obtain( + text, 0, text.length(), paint, dpToPx(availableWidthDp)) + // Simple break strategy avoids hyphenation unless there's a single word longer + // than the line width. We use this break strategy so that we consider text to + // "fit" only if it fits in a nice way (i.e. without hyphenation in the middle + // of words). + .setBreakStrategy(LineBreaker.BREAK_STRATEGY_SIMPLE) + .build(); + } catch (Exception e) { + Log.e(TAG, "Could not create static layout: " + e); + return null; + } + } + + private int dpToPx(float dp) { + return (int) (dp * mDensity); + } + + private int pxToDp(@Px float px) { + return (int) (px / mDensity); + } + + private static final class RemoteViewsAndSizes { + final RemoteViews mRemoteViews; + final int mAvatarSize; + + RemoteViewsAndSizes(RemoteViews remoteViews, int avatarSize) { + mRemoteViews = remoteViews; + mAvatarSize = avatarSize; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index 688884730373..4085df9a8093 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -22,6 +22,7 @@ import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL; import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE; import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; import static android.content.Intent.ACTION_BOOT_COMPLETED; +import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE; import static com.android.systemui.people.NotificationHelper.getContactUri; @@ -62,6 +63,7 @@ import android.content.pm.ShortcutInfo; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Bundle; +import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; @@ -172,6 +174,7 @@ public class PeopleSpaceWidgetManager { public void init() { synchronized (mLock) { if (!mRegisteredReceivers) { + if (DEBUG) Log.d(TAG, "Register receivers"); IntentFilter filter = new IntentFilter(); filter.addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED); filter.addAction(ACTION_BOOT_COMPLETED); @@ -185,6 +188,14 @@ public class PeopleSpaceWidgetManager { mBroadcastDispatcher.registerReceiver(mBaseBroadcastReceiver, filter, null /* executor */, UserHandle.ALL); + IntentFilter perAppFilter = new IntentFilter(ACTION_PACKAGE_REMOVED); + perAppFilter.addDataScheme("package"); + // BroadcastDispatcher doesn't allow data schemes. + mContext.registerReceiver(mBaseBroadcastReceiver, perAppFilter); + IntentFilter bootComplete = new IntentFilter(ACTION_BOOT_COMPLETED); + bootComplete.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + // BroadcastDispatcher doesn't allow priority. + mContext.registerReceiver(mBaseBroadcastReceiver, bootComplete); mRegisteredReceivers = true; } } @@ -303,10 +314,6 @@ public class PeopleSpaceWidgetManager { /** Updates tile in app widget options and the current view. */ public void updateAppWidgetOptionsAndView(int appWidgetId, PeopleSpaceTile tile) { - if (tile == null) { - if (DEBUG) Log.w(TAG, "Requested to store null tile"); - return; - } synchronized (mTiles) { mTiles.put(appWidgetId, tile); } @@ -320,6 +327,17 @@ public class PeopleSpaceWidgetManager { */ @Nullable public PeopleSpaceTile getTileForExistingWidget(int appWidgetId) { + try { + return getTileForExistingWidgetThrowing(appWidgetId); + } catch (Exception e) { + Log.e(TAG, "Failed to retrieve conversation for tile: " + e); + return null; + } + } + + @Nullable + private PeopleSpaceTile getTileForExistingWidgetThrowing(int appWidgetId) throws + PackageManager.NameNotFoundException { // First, check if tile is cached in memory. PeopleSpaceTile tile; synchronized (mTiles) { @@ -348,7 +366,8 @@ public class PeopleSpaceWidgetManager { * If a {@link PeopleTileKey} is not provided, fetch one from {@link SharedPreferences}. */ @Nullable - public PeopleSpaceTile getTileFromPersistentStorage(PeopleTileKey key, int appWidgetId) { + public PeopleSpaceTile getTileFromPersistentStorage(PeopleTileKey key, int appWidgetId) throws + PackageManager.NameNotFoundException { if (!key.isValid()) { Log.e(TAG, "PeopleTileKey invalid: " + key.toString()); return null; @@ -358,7 +377,6 @@ public class PeopleSpaceWidgetManager { Log.d(TAG, "System services are null"); return null; } - try { if (DEBUG) Log.d(TAG, "Retrieving Tile from storage: " + key.toString()); ConversationChannel channel = mIPeopleManager.getConversation( @@ -383,9 +401,9 @@ public class PeopleSpaceWidgetManager { } // Add current state. - return updateWithCurrentState(storedTile.build(), ACTION_BOOT_COMPLETED); - } catch (Exception e) { - Log.e(TAG, "Failed to retrieve conversation for tile: " + e); + return getTileWithCurrentState(storedTile.build(), ACTION_BOOT_COMPLETED); + } catch (RemoteException e) { + Log.e(TAG, "Could not retrieve data: " + e); return null; } } @@ -397,7 +415,6 @@ public class PeopleSpaceWidgetManager { public void updateWidgetsWithNotificationChanged(StatusBarNotification sbn, PeopleSpaceUtils.NotificationAction notificationAction) { if (DEBUG) { - Log.d(TAG, "updateWidgetsWithNotificationChanged called"); if (notificationAction == PeopleSpaceUtils.NotificationAction.POSTED) { Log.d(TAG, "Notification posted, key: " + sbn.getKey()); } else { @@ -414,7 +431,6 @@ public class PeopleSpaceWidgetManager { PeopleTileKey key = new PeopleTileKey( sbn.getShortcutId(), sbn.getUser().getIdentifier(), sbn.getPackageName()); if (!key.isValid()) { - Log.d(TAG, "Sbn doesn't contain valid PeopleTileKey: " + key.toString()); return; } int[] widgetIds = mAppWidgetManager.getAppWidgetIds( @@ -775,7 +791,13 @@ public class PeopleSpaceWidgetManager { /** Adds a widget based on {@code key} mapped to {@code appWidgetId}. */ public void addNewWidget(int appWidgetId, PeopleTileKey key) { if (DEBUG) Log.d(TAG, "addNewWidget called with key for appWidgetId: " + appWidgetId); - PeopleSpaceTile tile = getTileFromPersistentStorage(key, appWidgetId); + PeopleSpaceTile tile = null; + try { + tile = getTileFromPersistentStorage(key, appWidgetId); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Cannot add widget since app was uninstalled"); + return; + } if (tile == null) { return; } @@ -1017,72 +1039,85 @@ public class PeopleSpaceWidgetManager { @Override public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (DEBUG) Log.d(TAG, "Update widgets from: " + action); - mBgExecutor.execute(() -> updateWidgetsOnStateChange(action)); + if (DEBUG) Log.d(TAG, "Update widgets from: " + intent.getAction()); + mBgExecutor.execute(() -> updateWidgetsFromBroadcastInBackground(intent.getAction())); } }; - /** Updates any app widget based on the current state. */ + /** Updates any app widget to the current state, triggered by a broadcast update. */ @VisibleForTesting - void updateWidgetsOnStateChange(String entryPoint) { + void updateWidgetsFromBroadcastInBackground(String entryPoint) { int[] appWidgetIds = mAppWidgetManager.getAppWidgetIds( new ComponentName(mContext, PeopleSpaceWidgetProvider.class)); if (appWidgetIds == null) { return; } - synchronized (mLock) { - for (int appWidgetId : appWidgetIds) { - PeopleSpaceTile tile = getTileForExistingWidget(appWidgetId); - if (tile == null) { - Log.e(TAG, "Matching conversation not found for shortcut ID"); - } else { - tile = updateWithCurrentState(tile, entryPoint); + for (int appWidgetId : appWidgetIds) { + PeopleSpaceTile existingTile = null; + PeopleSpaceTile updatedTile = null; + try { + synchronized (mLock) { + existingTile = getTileForExistingWidgetThrowing(appWidgetId); + if (existingTile == null) { + Log.e(TAG, "Matching conversation not found for shortcut ID"); + return; + } + updatedTile = getTileWithCurrentState(existingTile, entryPoint); + updateAppWidgetOptionsAndView(appWidgetId, updatedTile); } - updateAppWidgetOptionsAndView(appWidgetId, tile); + } catch (PackageManager.NameNotFoundException e) { + // Delete data for uninstalled widgets. + Log.e(TAG, "Package no longer found for tile: " + e); + synchronized (mLock) { + updateAppWidgetOptionsAndView(appWidgetId, updatedTile); + } + deleteWidgets(new int[]{appWidgetId}); } } } - /** Checks the current state of {@code tile} dependencies, updating fields as necessary. */ + /** Checks the current state of {@code tile} dependencies, modifying fields as necessary. */ @Nullable - private PeopleSpaceTile updateWithCurrentState(PeopleSpaceTile tile, - String entryPoint) { + private PeopleSpaceTile getTileWithCurrentState(PeopleSpaceTile tile, + String entryPoint) throws + PackageManager.NameNotFoundException { PeopleSpaceTile.Builder updatedTile = tile.toBuilder(); - try { - switch (entryPoint) { - case NotificationManager - .ACTION_INTERRUPTION_FILTER_CHANGED: - updatedTile.setNotificationPolicyState(getNotificationPolicyState()); - break; - case Intent.ACTION_PACKAGES_SUSPENDED: - case Intent.ACTION_PACKAGES_UNSUSPENDED: - updatedTile.setIsPackageSuspended(getPackageSuspended(tile)); - break; - case Intent.ACTION_MANAGED_PROFILE_AVAILABLE: - case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE: - case Intent.ACTION_USER_UNLOCKED: - updatedTile.setIsUserQuieted(getUserQuieted(tile)); - break; - case Intent.ACTION_LOCALE_CHANGED: - break; - case ACTION_BOOT_COMPLETED: - default: - updatedTile.setIsUserQuieted(getUserQuieted(tile)).setIsPackageSuspended( - getPackageSuspended(tile)).setNotificationPolicyState( - getNotificationPolicyState()); - } - } catch (Exception e) { - Log.e(TAG, "Package no longer found for tile: " + tile.toString() + e); - return null; + switch (entryPoint) { + case NotificationManager + .ACTION_INTERRUPTION_FILTER_CHANGED: + updatedTile.setNotificationPolicyState(getNotificationPolicyState()); + break; + case Intent.ACTION_PACKAGES_SUSPENDED: + case Intent.ACTION_PACKAGES_UNSUSPENDED: + updatedTile.setIsPackageSuspended(getPackageSuspended(tile)); + break; + case Intent.ACTION_MANAGED_PROFILE_AVAILABLE: + case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE: + case Intent.ACTION_USER_UNLOCKED: + updatedTile.setIsUserQuieted(getUserQuieted(tile)); + break; + case Intent.ACTION_LOCALE_CHANGED: + break; + case ACTION_BOOT_COMPLETED: + default: + updatedTile.setIsUserQuieted(getUserQuieted(tile)).setIsPackageSuspended( + getPackageSuspended(tile)).setNotificationPolicyState( + getNotificationPolicyState()); } return updatedTile.build(); } - private boolean getPackageSuspended(PeopleSpaceTile tile) throws Exception { + private boolean getPackageSuspended(PeopleSpaceTile tile) throws + PackageManager.NameNotFoundException { boolean packageSuspended = !TextUtils.isEmpty(tile.getPackageName()) && mPackageManager.isPackageSuspended(tile.getPackageName()); if (DEBUG) Log.d(TAG, "Package suspended: " + packageSuspended); + // isPackageSuspended() only throws an exception if the app has been uninstalled, and the + // app data has also been cleared. We want to empty the layout when the app is uninstalled + // regardless of app data clearing, which getApplicationInfoAsUser() handles. + mPackageManager.getApplicationInfoAsUser( + tile.getPackageName(), PackageManager.GET_META_DATA, + PeopleSpaceUtils.getUserId(tile)); return packageSuspended; } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/PrivacyChipDrawable.java b/packages/SystemUI/src/com/android/systemui/privacy/television/PrivacyChipDrawable.java new file mode 100644 index 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/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index e24acf27c973..659475d19277 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -228,24 +228,5 @@ public class QuickQSPanel extends QSPanel { } } } - - @Override - public void setExpansion(float expansion) { - if (expansion > 0f && expansion < 1f) { - return; - } - boolean selected = expansion == 0f; - // Expansion == 0f is when QQS is fully showing (as opposed to 1f, which is QS). At this - // point we want them to be selected so the tiles will marquee (but not at other points - // of expansion. - // We set it as not important while we change this, so setting each tile as selected - // will not cause them to announce themselves until the user has actually selected the - // item. - setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); - for (int i = 0; i < getChildCount(); i++) { - getChildAt(i).setSelected(selected); - } - setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); - } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/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/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java index aa51771864b2..1d791f5d632c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java @@ -57,6 +57,12 @@ public class TileLifecycleManager extends BroadcastReceiver implements private static final String TAG = "TileLifecycleManager"; + private static final int META_DATA_QUERY_FLAGS = + PackageManager.GET_META_DATA + | PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_DIRECT_BOOT_AWARE; + private static final int MSG_ON_ADDED = 0; private static final int MSG_ON_REMOVED = 1; private static final int MSG_ON_CLICK = 2; @@ -130,7 +136,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements public boolean isActiveTile() { try { ServiceInfo info = mPackageManagerAdapter.getServiceInfo(mIntent.getComponent(), - PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA); + META_DATA_QUERY_FLAGS); return info.metaData != null && info.metaData.getBoolean(TileService.META_DATA_ACTIVE_TILE, false); } catch (PackageManager.NameNotFoundException e) { @@ -148,7 +154,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements public boolean isToggleableTile() { try { ServiceInfo info = mPackageManagerAdapter.getServiceInfo(mIntent.getComponent(), - PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA); + META_DATA_QUERY_FLAGS); return info.metaData != null && info.metaData.getBoolean(TileService.META_DATA_TOGGLEABLE_TILE, false); } catch (PackageManager.NameNotFoundException e) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt index 8aed0a88fa35..d31e67c55777 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt @@ -190,6 +190,9 @@ open class QSTileViewImpl @JvmOverloads constructor( if (collapsed) { labelContainer.ignoreLastView = true secondaryLabel.alpha = 0f + // Do not marque in QQS + label.ellipsize = TextUtils.TruncateAt.END + secondaryLabel.ellipsize = TextUtils.TruncateAt.END } setLabelColor(getLabelColorForState(QSTile.State.DEFAULT_STATE)) setSecondaryLabelColor(getSecondaryLabelColorForState(QSTile.State.DEFAULT_STATE)) @@ -492,11 +495,7 @@ open class QSTileViewImpl @JvmOverloads constructor( } return if (state.state == Tile.STATE_UNAVAILABLE || state is BooleanState) { - val resName = "$TILE_STATE_RES_PREFIX${state.spec}" - var arrayResId = resources.getIdentifier(resName, "array", context.packageName) - if (arrayResId == 0) { - arrayResId = R.array.tile_states_default - } + var arrayResId = SubtitleArrayMapping.getSubtitleId(state.spec) val array = resources.getStringArray(arrayResId) array[state.state] } else { @@ -557,6 +556,41 @@ open class QSTileViewImpl @JvmOverloads constructor( private fun getChevronColorForState(state: Int): Int = getSecondaryLabelColorForState(state) } +@VisibleForTesting +internal object SubtitleArrayMapping { + private val subtitleIdsMap = mapOf<String?, Int>( + "internet" to R.array.tile_states_internet, + "wifi" to R.array.tile_states_wifi, + "cell" to R.array.tile_states_cell, + "battery" to R.array.tile_states_battery, + "dnd" to R.array.tile_states_dnd, + "flashlight" to R.array.tile_states_flashlight, + "rotation" to R.array.tile_states_rotation, + "bt" to R.array.tile_states_bt, + "airplane" to R.array.tile_states_airplane, + "location" to R.array.tile_states_location, + "hotspot" to R.array.tile_states_hotspot, + "inversion" to R.array.tile_states_inversion, + "saver" to R.array.tile_states_saver, + "dark" to R.array.tile_states_dark, + "work" to R.array.tile_states_work, + "cast" to R.array.tile_states_cast, + "night" to R.array.tile_states_night, + "screenrecord" to R.array.tile_states_screenrecord, + "reverse" to R.array.tile_states_reverse, + "reduce_brightness" to R.array.tile_states_reduce_brightness, + "cameratoggle" to R.array.tile_states_cameratoggle, + "mictoggle" to R.array.tile_states_mictoggle, + "controls" to R.array.tile_states_controls, + "wallet" to R.array.tile_states_wallet, + "alarm" to R.array.tile_states_alarm + ) + + fun getSubtitleId(spec: String?): Int { + return subtitleIdsMap.getOrDefault(spec, R.array.tile_states_default) + } +} + private fun colorValuesHolder(name: String, vararg values: Int): PropertyValuesHolder { return PropertyValuesHolder.ofInt(name, *values).apply { setEvaluator(ArgbEvaluator.getInstance()) diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index c6b5eb7508af..5bb3413595ba 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -291,19 +291,24 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis ? res.getString(R.string.screenrecord_ongoing_screen_only) : res.getString(R.string.screenrecord_ongoing_screen_and_audio); - Intent stopIntent = getNotificationIntent(this); + PendingIntent pendingIntent = PendingIntent.getService( + this, + REQUEST_CODE, + getNotificationIntent(this), + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + Notification.Action stopAction = new Notification.Action.Builder( + Icon.createWithResource(this, R.drawable.ic_android), + getResources().getString(R.string.screenrecord_stop_label), + pendingIntent).build(); Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_screenrecord) .setContentTitle(notificationTitle) - .setContentText(getResources().getString(R.string.screenrecord_stop_text)) .setUsesChronometer(true) .setColorized(true) .setColor(getResources().getColor(R.color.GM2_red_700)) .setOngoing(true) .setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE) - .setContentIntent( - PendingIntent.getService(this, REQUEST_CODE, stopIntent, - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)) + .addAction(stopAction) .addExtras(extras); startForeground(NOTIFICATION_RECORDING_ID, builder.build()); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java index 4aead817fe8c..55602a98b8c5 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java @@ -111,30 +111,21 @@ class ImageExporter { } /** - * Stores the given Bitmap to a temp file. + * Writes the given Bitmap to outputFile. */ - ListenableFuture<File> exportAsTempFile(Executor executor, Bitmap bitmap) { + ListenableFuture<File> exportToRawFile(Executor executor, Bitmap bitmap, + final File outputFile) { return CallbackToFutureAdapter.getFuture( (completer) -> { executor.execute(() -> { - File cachePath; - try { - cachePath = File.createTempFile("long_screenshot_cache_", ".tmp"); - try (FileOutputStream stream = new FileOutputStream(cachePath)) { - bitmap.compress(mCompressFormat, mQuality, stream); - } catch (IOException e) { - if (cachePath.exists()) { - //noinspection ResultOfMethodCallIgnored - cachePath.delete(); - cachePath = null; - } - completer.setException(e); - } - if (cachePath != null) { - completer.set(cachePath); - } + try (FileOutputStream stream = new FileOutputStream(outputFile)) { + bitmap.compress(mCompressFormat, mQuality, stream); + completer.set(outputFile); } catch (IOException e) { - // Failed to create a new file + if (outputFile.exists()) { + //noinspection ResultOfMethodCallIgnored + outputFile.delete(); + } completer.setException(e); } }); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java index 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/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java index 25ec1d74008e..07f6d36cbec1 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java @@ -32,8 +32,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; import android.text.TextUtils; -import android.transition.Transition; -import android.transition.TransitionListenerAdapter; import android.util.Log; import android.view.ScrollCaptureResponse; import android.view.View; @@ -74,7 +72,7 @@ public class LongScreenshotActivity extends Activity { private final Executor mUiExecutor; private final Executor mBackgroundExecutor; private final ImageExporter mImageExporter; - private final LongScreenshotHolder mLongScreenshotHolder; + private final LongScreenshotData mLongScreenshotHolder; private ImageView mPreview; private ImageView mTransitionView; @@ -103,7 +101,7 @@ public class LongScreenshotActivity extends Activity { @Inject public LongScreenshotActivity(UiEventLogger uiEventLogger, ImageExporter imageExporter, @Main Executor mainExecutor, @Background Executor bgExecutor, - LongScreenshotHolder longScreenshotHolder) { + LongScreenshotData longScreenshotHolder) { mUiEventLogger = uiEventLogger; mUiExecutor = mainExecutor; mBackgroundExecutor = bgExecutor; @@ -116,7 +114,6 @@ public class LongScreenshotActivity extends Activity { public void onCreate(Bundle savedInstanceState) { Log.d(TAG, "onCreate(savedInstanceState = " + savedInstanceState + ")"); super.onCreate(savedInstanceState); - postponeEnterTransition(); setContentView(R.layout.long_screenshot); mPreview = requireViewById(R.id.preview); @@ -210,32 +207,29 @@ public class LongScreenshotActivity extends Activity { public boolean onPreDraw() { mEnterTransitionView.getViewTreeObserver().removeOnPreDrawListener(this); updateImageDimensions(); - startPostponedEnterTransition(); - if (isActivityTransitionRunning()) { - getWindow().getSharedElementEnterTransition().addListener( - new TransitionListenerAdapter() { - @Override - public void onTransitionEnd(Transition transition) { - super.onTransitionEnd(transition); - mPreview.animate().alpha(1f); - mCropView.setBoundaryPosition( - CropView.CropBoundary.TOP, topFraction); - mCropView.setBoundaryPosition( - CropView.CropBoundary.BOTTOM, bottomFraction); - mCropView.animateEntrance(); - mCropView.setVisibility(View.VISIBLE); - setButtonsEnabled(true); - mEnterTransitionView.setVisibility(View.GONE); - } + mEnterTransitionView.post(() -> { + Rect dest = new Rect(); + mEnterTransitionView.getBoundsOnScreen(dest); + mLongScreenshotHolder.takeTransitionDestinationCallback() + .setTransitionDestination(dest, () -> { + mPreview.animate().alpha(1f); + mCropView.setBoundaryPosition( + CropView.CropBoundary.TOP, topFraction); + mCropView.setBoundaryPosition( + CropView.CropBoundary.BOTTOM, bottomFraction); + mCropView.animateEntrance(); + mCropView.setVisibility(View.VISIBLE); + setButtonsEnabled(true); + mEnterTransitionView.setVisibility(View.GONE); }); - } + }); return true; } }); // Immediately export to temp image file for saved state - mCacheSaveFuture = mImageExporter.exportAsTempFile(mBackgroundExecutor, - mLongScreenshot.toBitmap()); + mCacheSaveFuture = mImageExporter.exportToRawFile(mBackgroundExecutor, + mLongScreenshot.toBitmap(), new File(getCacheDir(), "long_screenshot_cache.png")); mCacheSaveFuture.addListener(() -> { try { // Get the temp file path to persist, used in onSavedInstanceState diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotHolder.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java index 39c6f0798988..f549faf2414a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotHolder.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java @@ -23,16 +23,19 @@ import java.util.concurrent.atomic.AtomicReference; import javax.inject.Inject; /** - * LongScreenshotHolder holds on to 1 LongScreenshot reference to facilitate indirect in-process - * passing. + * LongScreenshotData holds on to 1 LongScreenshot reference and 1 TransitionDestination + * reference, to facilitate indirect in-process passing. */ @SysUISingleton -public class LongScreenshotHolder { +public class LongScreenshotData { private final AtomicReference<ScrollCaptureController.LongScreenshot> mLongScreenshot; + private final AtomicReference<ScreenshotController.TransitionDestination> + mTransitionDestinationCallback; @Inject - public LongScreenshotHolder() { + public LongScreenshotData() { mLongScreenshot = new AtomicReference<>(); + mTransitionDestinationCallback = new AtomicReference<>(); } /** @@ -56,4 +59,18 @@ public class LongScreenshotHolder { return mLongScreenshot.getAndSet(null); } + /** + * Set the holder's TransitionDestination callback. + */ + public void setTransitionDestinationCallback( + ScreenshotController.TransitionDestination destination) { + mTransitionDestinationCallback.set(destination); + } + + /** + * Return the current TransitionDestination callback and clear. + */ + public ScreenshotController.TransitionDestination takeTransitionDestinationCallback() { + return mTransitionDestinationCallback.getAndSet(null); + } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index aea8849adc24..2e138362aedb 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -64,6 +64,7 @@ import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; import android.view.KeyEvent; import android.view.LayoutInflater; +import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.ScrollCaptureResponse; import android.view.SurfaceControl; @@ -72,6 +73,7 @@ import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowInsets; import android.view.WindowManager; +import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.widget.Toast; @@ -203,6 +205,15 @@ public class ScreenshotController { void onActionsReady(ScreenshotController.QuickShareData quickShareData); } + interface TransitionDestination { + /** + * Allows the long screenshot activity to call back with a destination location (the bounds + * on screen of the destination for the transitioning view) and a Runnable to be run once + * the transition animation is complete. + */ + void setTransitionDestination(Rect transitionDestination, Runnable onTransitionEnd); + } + // These strings are used for communicating the action invoked to // ScreenshotNotificationSmartActionsProvider. static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type"; @@ -241,7 +252,7 @@ public class ScreenshotController { private final PhoneWindow mWindow; private final DisplayManager mDisplayManager; private final ScrollCaptureController mScrollCaptureController; - private final LongScreenshotHolder mLongScreenshotHolder; + private final LongScreenshotData mLongScreenshotHolder; private ScreenshotView mScreenshotView; private Bitmap mScreenBitmap; @@ -286,7 +297,7 @@ public class ScreenshotController { ImageExporter imageExporter, @Main Executor mainExecutor, ScrollCaptureController scrollCaptureController, - LongScreenshotHolder longScreenshotHolder) { + LongScreenshotData longScreenshotHolder) { mScreenshotSmartActions = screenshotSmartActions; mNotificationsController = screenshotNotificationsController; mScrollCaptureClient = scrollCaptureClient; @@ -648,17 +659,29 @@ public class ScreenshotController { } mLongScreenshotHolder.setLongScreenshot(longScreenshot); + mLongScreenshotHolder.setTransitionDestinationCallback( + (transitionDestination, onTransitionEnd) -> + mScreenshotView.startLongScreenshotTransition( + transitionDestination, onTransitionEnd, + longScreenshot)); final Intent intent = new Intent(mContext, LongScreenshotActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); Pair<ActivityOptions, ExitTransitionCoordinator> transition = - ActivityOptions.startSharedElementAnimation( - mWindow, new ScreenshotExitTransitionCallbacks(), null, - Pair.create(mScreenshotView.getScrollablePreview(), - ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME)); + ActivityOptions.startSharedElementAnimation(mWindow, + new ScreenshotExitTransitionCallbacksSupplier(false).get(), + null); transition.second.startExit(); mContext.startActivity(intent, transition.first.toBundle()); + RemoteAnimationAdapter runner = new RemoteAnimationAdapter( + SCREENSHOT_REMOTE_RUNNER, 0, 0); + try { + WindowManagerGlobal.getWindowManagerService() + .overridePendingAppTransitionRemote(runner, DEFAULT_DISPLAY); + } catch (Exception e) { + Log.e(TAG, "Error overriding screenshot app transition", e); + } }, mMainExecutor); }); } catch (CancellationException e) { @@ -883,8 +906,8 @@ public class ScreenshotController { return () -> { Pair<ActivityOptions, ExitTransitionCoordinator> transition = ActivityOptions.startSharedElementAnimation( - mWindow, new ScreenshotExitTransitionCallbacks(), null, - Pair.create(mScreenshotView.getScreenshotPreview(), + mWindow, new ScreenshotExitTransitionCallbacksSupplier(true).get(), + null, Pair.create(mScreenshotView.getScreenshotPreview(), ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME)); transition.second.startExit(); @@ -970,19 +993,33 @@ public class ScreenshotController { return matchWithinTolerance; } - private class ScreenshotExitTransitionCallbacks implements ExitTransitionCallbacks { - @Override - public boolean isReturnTransitionAllowed() { - return false; - } + private class ScreenshotExitTransitionCallbacksSupplier implements + Supplier<ExitTransitionCallbacks> { + final boolean mDismissOnHideSharedElements; - @Override - public void hideSharedElements() { - finishDismiss(); + ScreenshotExitTransitionCallbacksSupplier(boolean dismissOnHideSharedElements) { + mDismissOnHideSharedElements = dismissOnHideSharedElements; } @Override - public void onFinish() { + public ExitTransitionCallbacks get() { + return new ExitTransitionCallbacks() { + @Override + public boolean isReturnTransitionAllowed() { + return false; + } + + @Override + public void hideSharedElements() { + if (mDismissOnHideSharedElements) { + finishDismiss(); + } + } + + @Override + public void onFinish() { + } + }; } } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index 2a2217912b58..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); @@ -479,6 +471,11 @@ public class ScreenshotView extends FrameLayout implements final PointF finalPos = new PointF(targetPosition.exactCenterX(), targetPosition.exactCenterY()); + // Shift to screen coordinates so that the animation runs on top of the entire screen, + // including e.g. bars covering the display cutout. + int[] locInScreen = mScreenshotPreview.getLocationOnScreen(); + startPos.offset(targetPosition.left - locInScreen[0], targetPosition.top - locInScreen[1]); + if (DEBUG_ANIM) { Log.d(TAG, "toCorner: startPos=" + startPos); Log.d(TAG, "toCorner: finalPos=" + finalPos); @@ -486,6 +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 = @@ -529,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); @@ -759,7 +763,53 @@ public class ScreenshotView extends FrameLayout implements return r; } + void startLongScreenshotTransition(Rect destination, Runnable onTransitionEnd, + ScrollCaptureController.LongScreenshot longScreenshot) { + mScrollablePreview.setImageBitmap(longScreenshot.toBitmap()); + ValueAnimator anim = ValueAnimator.ofFloat(0, 1); + float startX = mScrollablePreview.getX(); + float startY = mScrollablePreview.getY(); + int[] locInScreen = mScrollablePreview.getLocationOnScreen(); + destination.offset((int) startX - locInScreen[0], (int) startY - locInScreen[1]); + mScrollablePreview.setPivotX(0); + mScrollablePreview.setPivotY(0); + mScrollablePreview.setAlpha(1f); + float currentScale = mScrollablePreview.getWidth() / (float) longScreenshot.getWidth(); + Matrix matrix = new Matrix(); + matrix.setScale(currentScale, currentScale); + matrix.postTranslate( + longScreenshot.getLeft() * currentScale, longScreenshot.getTop() * currentScale); + mScrollablePreview.setImageMatrix(matrix); + float destinationScale = destination.width() / (float) mScrollablePreview.getWidth(); + anim.addUpdateListener(animation -> { + float t = animation.getAnimatedFraction(); + mScrollingScrim.setAlpha(1 - t); + float currScale = MathUtils.lerp(1, destinationScale, t); + mScrollablePreview.setScaleX(currScale); + mScrollablePreview.setScaleY(currScale); + mScrollablePreview.setX(MathUtils.lerp(startX, destination.left, t)); + mScrollablePreview.setY(MathUtils.lerp(startY, destination.top, t)); + }); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + onTransitionEnd.run(); + mScrollablePreview.animate().alpha(0).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + mCallbacks.onDismiss(); + } + }); + } + }); + anim.start(); + } + void prepareScrollingTransition(ScrollCaptureResponse response, Bitmap screenBitmap) { + mScrollingScrim.setImageBitmap(screenBitmap); + mScrollingScrim.setVisibility(View.VISIBLE); Rect scrollableArea = scrollableAreaOnScreen(response); float scale = mCornerSizeX / (mOrientationPortrait ? screenBitmap.getWidth() : screenBitmap.getHeight()); @@ -770,14 +820,12 @@ public class ScreenshotView extends FrameLayout implements params.height = (int) (scale * scrollableArea.height()); Matrix matrix = new Matrix(); matrix.setScale(scale, scale); - matrix.postTranslate(0, -scrollableArea.top * scale); + matrix.postTranslate(-scrollableArea.left * scale, -scrollableArea.top * scale); mScrollablePreview.setTranslationX(scale * scrollableArea.left); mScrollablePreview.setTranslationY(scale * scrollableArea.top); mScrollablePreview.setImageMatrix(matrix); - mScrollingScrim.setImageBitmap(screenBitmap); - mScrollingScrim.setVisibility(View.VISIBLE); mScrollablePreview.setImageBitmap(screenBitmap); mScrollablePreview.setVisibility(View.VISIBLE); createScreenshotFadeDismissAnimation(true).start(); 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..a79316d6f5a9 100644 --- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt @@ -32,6 +32,7 @@ import android.widget.ImageView import com.android.internal.app.AlertActivity import com.android.internal.widget.DialogTitle import com.android.systemui.R +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController import com.android.systemui.statusbar.policy.KeyguardStateController @@ -46,13 +47,15 @@ import javax.inject.Inject class SensorUseStartedActivity @Inject constructor( private val sensorPrivacyController: IndividualSensorPrivacyController, private val keyguardStateController: KeyguardStateController, - private val keyguardDismissUtil: KeyguardDismissUtil + private val keyguardDismissUtil: KeyguardDismissUtil, + @Background private val bgHandler: Handler ) : AlertActivity(), DialogInterface.OnClickListener { companion object { private val LOG_TAG = SensorUseStartedActivity::class.java.simpleName private const val SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS = 2000L + private const val UNLOCK_DELAY_MILLIS = 200L private const val CAMERA = SensorPrivacyManager.Sensors.CAMERA private const val MICROPHONE = SensorPrivacyManager.Sensors.MICROPHONE @@ -179,9 +182,12 @@ class SensorUseStartedActivity @Inject constructor( BUTTON_POSITIVE -> { if (keyguardStateController.isMethodSecure && keyguardStateController.isShowing) { keyguardDismissUtil.executeWhenUnlocked({ - disableSensorPrivacy() + bgHandler.postDelayed({ + disableSensorPrivacy() + }, UNLOCK_DELAY_MILLIS) + false - }, false, false) + }, false, true) } else { disableSensorPrivacy() } @@ -201,7 +207,7 @@ class SensorUseStartedActivity @Inject constructor( sensorPrivacyController .suppressSensorPrivacyReminders(sensorUsePackageName, false) } else { - Handler(mainLooper).postDelayed({ + bgHandler.postDelayed({ sensorPrivacyController .suppressSensorPrivacyReminders(sensorUsePackageName, false) }, SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS) @@ -218,7 +224,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/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt index ec648ad519a3..5d8bed57563b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt @@ -51,7 +51,7 @@ object LiftReveal : LightRevealEffect { private const val OVAL_INITIAL_WIDTH_PERCENT = 0.5f /** The initial top value of the light oval, in percent of scrim height. */ - private const val OVAL_INITIAL_TOP_PERCENT = 1.05f + private const val OVAL_INITIAL_TOP_PERCENT = 1.1f /** The initial bottom value of the light oval, in percent of scrim height. */ private const val OVAL_INITIAL_BOTTOM_PERCENT = 1.2f 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/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index a69b8d60681c..25cbdc5c2187 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -245,7 +245,8 @@ public class NotificationMediaManager implements Dumpable { mMediaDataManager.addListener(new MediaDataManager.Listener() { @Override public void onMediaDataLoaded(@NonNull String key, - @Nullable String oldKey, @NonNull MediaData data, boolean immediately) { + @Nullable String oldKey, @NonNull MediaData data, boolean immediately, + boolean isSsReactivated) { } @Override @@ -318,7 +319,8 @@ public class NotificationMediaManager implements Dumpable { mMediaDataManager.addListener(new MediaDataManager.Listener() { @Override public void onMediaDataLoaded(@NonNull String key, - @Nullable String oldKey, @NonNull MediaData data, boolean immediately) { + @Nullable String oldKey, @NonNull MediaData data, boolean immediately, + boolean isSsReactivated) { } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index 89005513d3d0..e5c3b82d06cd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -93,8 +93,13 @@ class NotificationShadeDepthController @Inject constructor( var shadeAnimation = DepthAnimation() @VisibleForTesting - var globalActionsSpring = DepthAnimation() - var showingHomeControls: Boolean = false + var brightnessMirrorSpring = DepthAnimation() + var brightnessMirrorVisible: Boolean = false + set(value) { + field = value + brightnessMirrorSpring.animateTo(if (value) blurUtils.blurRadiusOfRatio(1f) + else 0) + } var qsPanelExpansion = 0f set(value) { @@ -177,22 +182,19 @@ class NotificationShadeDepthController @Inject constructor( } } - // Home controls have black background, this means that we should not have blur when they - // are fully visible, otherwise we'll enter Client Composition unnecessarily. - var globalActionsRadius = globalActionsSpring.radius - if (showingHomeControls) { - globalActionsRadius = 0 - } - var blur = max(shadeRadius.toInt(), globalActionsRadius) + var blur = shadeRadius.toInt() // Make blur be 0 if it is necessary to stop blur effect. - if (scrimsVisible) { + 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) @@ -259,7 +261,7 @@ class NotificationShadeDepthController @Inject constructor( if (isDozing) { shadeSpring.finishIfRunning() shadeAnimation.finishIfRunning() - globalActionsSpring.finishIfRunning() + brightnessMirrorSpring.finishIfRunning() } } @@ -414,17 +416,13 @@ class NotificationShadeDepthController @Inject constructor( !keyguardStateController.isKeyguardFadingAway } - fun updateGlobalDialogVisibility(visibility: Float, dialogView: View?) { - globalActionsSpring.animateTo(blurUtils.blurRadiusOfRatio(visibility), dialogView) - } - override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { IndentingPrintWriter(pw, " ").let { it.println("StatusBarWindowBlurController:") it.increaseIndent() it.println("shadeRadius: ${shadeSpring.radius}") it.println("shadeAnimation: ${shadeAnimation.radius}") - it.println("globalActionsRadius: ${globalActionsSpring.radius}") + it.println("brightnessMirrorRadius: ${brightnessMirrorSpring.radius}") it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius") it.println("ignoreShadeBlurUntilHidden: $ignoreShadeBlurUntilHidden") } 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/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index c24c2be3faa3..4f3406c405ad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -203,6 +203,14 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mBackgroundNormal.setCustomBackground(R.drawable.notification_material_bg); } + protected boolean hideBackground() { + return false; + } + + protected void updateBackground() { + mBackgroundNormal.setVisibility(hideBackground() ? INVISIBLE : VISIBLE); + } + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { 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 ba28dc59def4..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) { @@ -2890,6 +2905,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mShowNoBackground = false; } updateOutline(); + updateBackground(); + } + + @Override + protected boolean hideBackground() { + return mShowNoBackground || super.hideBackground(); } public int getPositionOfChild(ExpandableNotificationRow childRow) { @@ -2975,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(); @@ -2991,7 +3012,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView isExpanded = isExpanded(); } } - if (expandable) { + if (expandable && !mIsSnoozed) { if (isExpanded) { info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE); } else { @@ -3079,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/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index f2e39e78df36..c2716b949b71 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -725,7 +725,7 @@ public class NotificationChildrenContainer extends ViewGroup { tmpState.yTranslation = viewState.yTranslation - mDividerHeight; float alpha = mChildrenExpanded && viewState.alpha != 0 ? mDividerAlpha : 0; if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) { - alpha = NotificationUtils.interpolate(0, 0.5f, + alpha = NotificationUtils.interpolate(0, mDividerAlpha, Math.min(viewState.alpha, expandFraction)); } tmpState.hidden = !dividersVisible; @@ -802,9 +802,9 @@ public class NotificationChildrenContainer extends ViewGroup { View divider = mDividers.get(i); tmpState.initFrom(divider); tmpState.yTranslation = viewState.yTranslation - mDividerHeight; - float alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0; + float alpha = mChildrenExpanded && viewState.alpha != 0 ? mDividerAlpha : 0; if (mUserLocked && !showingAsLowPriority() && viewState.alpha != 0) { - alpha = NotificationUtils.interpolate(0, 0.5f, + alpha = NotificationUtils.interpolate(0, mDividerAlpha, Math.min(viewState.alpha, expandFraction)); } tmpState.hidden = !dividersVisible; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java index 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/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 1ebb9ddc0262..14190d86cea5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -1012,15 +1012,16 @@ public class UserSwitcherController implements Dumpable { public ExitGuestDialog(Context context, int guestId, int targetId) { super(context); - setTitle(mGuestUserAutoCreated ? R.string.guest_reset_guest_dialog_title + setTitle(mGuestUserAutoCreated + ? com.android.settingslib.R.string.guest_reset_guest_dialog_title : R.string.guest_exit_guest_dialog_title); setMessage(context.getString(R.string.guest_exit_guest_dialog_message)); setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(android.R.string.cancel), this); setButton(DialogInterface.BUTTON_POSITIVE, - context.getString( - mGuestUserAutoCreated ? R.string.guest_reset_guest_dialog_remove - : R.string.guest_exit_guest_dialog_remove), this); + context.getString(mGuestUserAutoCreated + ? com.android.settingslib.R.string.guest_reset_guest_confirm_button + : R.string.guest_exit_guest_dialog_remove), this); SystemUIDialog.setWindowOnTop(this); setCanceledOnTouchOutside(false); mGuestId = guestId; 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/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index ab4b1f10132c..e57059894786 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -128,7 +128,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private final MediaSessions mMediaSessions; protected C mCallbacks = new C(); private final State mState = new State(); - protected final MediaSessionsCallbacks mMediaSessionsCallbacksW = new MediaSessionsCallbacks(); + protected final MediaSessionsCallbacks mMediaSessionsCallbacksW; private final Optional<Vibrator> mVibrator; private final boolean mHasVibrator; private boolean mShowA11yStream; @@ -179,6 +179,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mWorkerLooper = theadFactory.buildLooperOnNewThread( VolumeDialogControllerImpl.class.getSimpleName()); mWorker = new W(mWorkerLooper); + mMediaSessionsCallbacksW = new MediaSessionsCallbacks(mContext); mMediaSessions = createMediaSessions(mContext, mWorkerLooper, mMediaSessionsCallbacksW); mAudio = audioManager; mNoMan = notificationManager; @@ -1148,83 +1149,98 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private final HashMap<Token, Integer> mRemoteStreams = new HashMap<>(); private int mNextStream = DYNAMIC_STREAM_START_INDEX; + private final boolean mShowRemoteSessions; + + public MediaSessionsCallbacks(Context context) { + mShowRemoteSessions = context.getResources().getBoolean( + com.android.internal.R.bool.config_volumeShowRemoteSessions); + } @Override public void onRemoteUpdate(Token token, String name, PlaybackInfo pi) { - addStream(token, "onRemoteUpdate"); + if (mShowRemoteSessions) { + addStream(token, "onRemoteUpdate"); - int stream = 0; - synchronized (mRemoteStreams) { - stream = mRemoteStreams.get(token); - } - Slog.d(TAG, "onRemoteUpdate: stream: " + stream + " volume: " + pi.getCurrentVolume()); - boolean changed = mState.states.indexOfKey(stream) < 0; - final StreamState ss = streamStateW(stream); - ss.dynamic = true; - ss.levelMin = 0; - ss.levelMax = pi.getMaxVolume(); - if (ss.level != pi.getCurrentVolume()) { - ss.level = pi.getCurrentVolume(); - changed = true; - } - if (!Objects.equals(ss.remoteLabel, name)) { - ss.name = -1; - ss.remoteLabel = name; - changed = true; - } - if (changed) { - Log.d(TAG, "onRemoteUpdate: " + name + ": " + ss.level + " of " + ss.levelMax); - mCallbacks.onStateChanged(mState); + int stream = 0; + synchronized (mRemoteStreams) { + stream = mRemoteStreams.get(token); + } + Slog.d(TAG, + "onRemoteUpdate: stream: " + stream + " volume: " + pi.getCurrentVolume()); + boolean changed = mState.states.indexOfKey(stream) < 0; + final StreamState ss = streamStateW(stream); + ss.dynamic = true; + ss.levelMin = 0; + ss.levelMax = pi.getMaxVolume(); + if (ss.level != pi.getCurrentVolume()) { + ss.level = pi.getCurrentVolume(); + changed = true; + } + if (!Objects.equals(ss.remoteLabel, name)) { + ss.name = -1; + ss.remoteLabel = name; + changed = true; + } + if (changed) { + Log.d(TAG, "onRemoteUpdate: " + name + ": " + ss.level + " of " + ss.levelMax); + mCallbacks.onStateChanged(mState); + } } } @Override public void onRemoteVolumeChanged(Token token, int flags) { - addStream(token, "onRemoteVolumeChanged"); - int stream = 0; - synchronized (mRemoteStreams) { - stream = mRemoteStreams.get(token); - } - final boolean showUI = shouldShowUI(flags); - Slog.d(TAG, "onRemoteVolumeChanged: stream: " + stream + " showui? " + showUI); - boolean changed = updateActiveStreamW(stream); - if (showUI) { - changed |= checkRoutedToBluetoothW(AudioManager.STREAM_MUSIC); - } - if (changed) { - Slog.d(TAG, "onRemoteChanged: updatingState"); - mCallbacks.onStateChanged(mState); - } - if (showUI) { - mCallbacks.onShowRequested(Events.SHOW_REASON_REMOTE_VOLUME_CHANGED); + if (mShowRemoteSessions) { + addStream(token, "onRemoteVolumeChanged"); + int stream = 0; + synchronized (mRemoteStreams) { + stream = mRemoteStreams.get(token); + } + final boolean showUI = shouldShowUI(flags); + Slog.d(TAG, "onRemoteVolumeChanged: stream: " + stream + " showui? " + showUI); + boolean changed = updateActiveStreamW(stream); + if (showUI) { + changed |= checkRoutedToBluetoothW(AudioManager.STREAM_MUSIC); + } + if (changed) { + Slog.d(TAG, "onRemoteChanged: updatingState"); + mCallbacks.onStateChanged(mState); + } + if (showUI) { + mCallbacks.onShowRequested(Events.SHOW_REASON_REMOTE_VOLUME_CHANGED); + } } } @Override public void onRemoteRemoved(Token token) { - int stream = 0; - synchronized (mRemoteStreams) { - if (!mRemoteStreams.containsKey(token)) { - Log.d(TAG, "onRemoteRemoved: stream doesn't exist, " - + "aborting remote removed for token:" + token.toString()); - return; + if (mShowRemoteSessions) { + int stream = 0; + synchronized (mRemoteStreams) { + if (!mRemoteStreams.containsKey(token)) { + Log.d(TAG, "onRemoteRemoved: stream doesn't exist, " + + "aborting remote removed for token:" + token.toString()); + return; + } + stream = mRemoteStreams.get(token); } - stream = mRemoteStreams.get(token); - } - mState.states.remove(stream); - if (mState.activeStream == stream) { - updateActiveStreamW(-1); + mState.states.remove(stream); + if (mState.activeStream == stream) { + updateActiveStreamW(-1); + } + mCallbacks.onStateChanged(mState); } - mCallbacks.onStateChanged(mState); } public void setStreamVolume(int stream, int level) { - final Token t = findToken(stream); - if (t == null) { - Log.w(TAG, "setStreamVolume: No token found for stream: " + stream); - return; + if (mShowRemoteSessions) { + final Token t = findToken(stream); + if (t == null) { + Log.w(TAG, "setStreamVolume: No token found for stream: " + stream); + return; + } + mMediaSessions.setVolume(t, level); } - mMediaSessions.setVolume(t, level); } private Token findToken(int stream) { diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 3af82f91af1c..bc956dc85702 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -45,6 +45,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.WMComponent; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.model.SysUiState; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.shared.tracing.ProtoTraceable; @@ -114,6 +115,7 @@ public final class WMShell extends SystemUI private final NavigationModeController mNavigationModeController; private final ScreenLifecycle mScreenLifecycle; private final SysUiState mSysUiState; + private final WakefulnessLifecycle mWakefulnessLifecycle; private final ProtoTracer mProtoTracer; private final Executor mSysUiMainExecutor; @@ -121,6 +123,7 @@ public final class WMShell extends SystemUI private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback; private KeyguardUpdateMonitorCallback mPipKeyguardCallback; private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback; + private WakefulnessLifecycle.Observer mWakefulnessObserver; @Inject public WMShell(Context context, @@ -136,6 +139,7 @@ public final class WMShell extends SystemUI ScreenLifecycle screenLifecycle, SysUiState sysUiState, ProtoTracer protoTracer, + WakefulnessLifecycle wakefulnessLifecycle, @Main Executor sysUiMainExecutor) { super(context); mCommandQueue = commandQueue; @@ -148,6 +152,7 @@ public final class WMShell extends SystemUI mSplitScreenOptional = splitScreenOptional; mOneHandedOptional = oneHandedOptional; mHideDisplayCutoutOptional = hideDisplayCutoutOptional; + mWakefulnessLifecycle = wakefulnessLifecycle; mProtoTracer = protoTracer; mShellCommandHandler = shellCommandHandler; mSysUiMainExecutor = sysUiMainExecutor; @@ -266,21 +271,8 @@ public final class WMShell extends SystemUI mOneHandedKeyguardCallback = new KeyguardUpdateMonitorCallback() { @Override - public void onKeyguardBouncerChanged(boolean bouncer) { - if (bouncer) { - oneHanded.stopOneHanded(); - } - } - - @Override public void onKeyguardVisibilityChanged(boolean showing) { - if (showing) { - // When keyguard shown, temperory lock OHM disabled to avoid mis-trigger. - oneHanded.setLockedDisabled(true /* locked */, false /* enabled */); - } else { - // Reset locked. - oneHanded.setLockedDisabled(false /* locked */, false /* enabled */); - } + oneHanded.onKeyguardVisibilityChanged(showing); oneHanded.stopOneHanded(); } @@ -291,6 +283,24 @@ public final class WMShell extends SystemUI }; mKeyguardUpdateMonitor.registerCallback(mOneHandedKeyguardCallback); + mWakefulnessObserver = + new WakefulnessLifecycle.Observer() { + @Override + public void onFinishedWakingUp() { + // Reset locked for the case keyguard not shown. + oneHanded.setLockedDisabled(false /* locked */, false /* enabled */); + } + + @Override + public void onStartedGoingToSleep() { + oneHanded.stopOneHanded(); + // When user press power button going to sleep, temperory lock OHM disabled + // to avoid mis-trigger. + oneHanded.setLockedDisabled(true /* locked */, false /* enabled */); + } + }; + mWakefulnessLifecycle.addObserver(mWakefulnessObserver); + mScreenLifecycle.addObserver(new ScreenLifecycle.Observer() { @Override public void onScreenTurningOff() { diff --git a/packages/SystemUI/tests/src/com/android/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/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java index adc8ffc1d633..578c2d976cce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java @@ -57,7 +57,6 @@ import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.GlobalActions; import com.android.systemui.settings.UserContextProvider; -import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -99,7 +98,6 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { @Mock private TrustManager mTrustManager; @Mock private IActivityManager mActivityManager; @Mock private MetricsLogger mMetricsLogger; - @Mock private NotificationShadeDepthController mDepthController; @Mock private SysuiColorExtractor mColorExtractor; @Mock private IStatusBarService mStatusBarService; @Mock private NotificationShadeWindowController mNotificationShadeWindowController; @@ -147,7 +145,6 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { mActivityManager, null, mMetricsLogger, - mDepthController, mColorExtractor, mStatusBarService, mNotificationShadeWindowController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java index e5c104e7d377..2fa67cc0be60 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java @@ -64,7 +64,6 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.GlobalActions; import com.android.systemui.plugins.GlobalActionsPanelPlugin; import com.android.systemui.settings.UserTracker; -import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -111,7 +110,6 @@ public class GlobalActionsDialogTest extends SysuiTestCase { @Mock private TrustManager mTrustManager; @Mock private IActivityManager mActivityManager; @Mock private MetricsLogger mMetricsLogger; - @Mock private NotificationShadeDepthController mDepthController; @Mock private SysuiColorExtractor mColorExtractor; @Mock private IStatusBarService mStatusBarService; @Mock private NotificationShadeWindowController mNotificationShadeWindowController; @@ -161,7 +159,6 @@ public class GlobalActionsDialogTest extends SysuiTestCase { mActivityManager, null, mMetricsLogger, - mDepthController, mColorExtractor, mStatusBarService, mNotificationShadeWindowController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java index e20b426907be..66b64708ad24 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java @@ -82,9 +82,11 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void eventNotEmittedWithoutDevice() { // WHEN data source emits an event without device data - mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */, + false /* isSsReactivated */); // THEN an event isn't emitted - verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean()); + verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(), + anyBoolean()); } @Test @@ -92,7 +94,8 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { // WHEN device source emits an event without media data mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // THEN an event isn't emitted - verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean()); + verify(mListener, never()).onMediaDataLoaded(eq(KEY), any(), any(), anyBoolean(), + anyBoolean()); } @Test @@ -100,80 +103,95 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { // GIVEN that a device event has already been received mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // WHEN media event is received - mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */, + false /* isSsReactivated */); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); - verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean()); + verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean(), + anyBoolean()); assertThat(captor.getValue().getDevice()).isNotNull(); } @Test public void emitEventAfterMediaFirst() { // GIVEN that media event has already been received - mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */, + false /* isSsReactivated */); // WHEN device event is received mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); - verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean()); + verify(mListener).onMediaDataLoaded(eq(KEY), any(), captor.capture(), anyBoolean(), + anyBoolean()); assertThat(captor.getValue().getDevice()).isNotNull(); } @Test public void migrateKeyMediaFirst() { // GIVEN that media and device info has already been received - mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */, + false /* isSsReactivated */); mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); reset(mListener); // WHEN a key migration event is received - mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */, + false /* isSsReactivated */); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); - verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean()); + verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean(), + anyBoolean()); assertThat(captor.getValue().getDevice()).isNotNull(); } @Test public void migrateKeyDeviceFirst() { // GIVEN that media and device info has already been received - mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */, + false /* isSsReactivated */); mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); reset(mListener); // WHEN a key migration event is received mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); // THEN the listener receives a combined event ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); - verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean()); + verify(mListener).onMediaDataLoaded(eq(KEY), eq(OLD_KEY), captor.capture(), anyBoolean(), + anyBoolean()); assertThat(captor.getValue().getDevice()).isNotNull(); } @Test public void migrateKeyMediaAfter() { // GIVEN that media and device info has already been received - mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */, + false /* isSsReactivated */); mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); reset(mListener); // WHEN a second key migration event is received for media - mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */, + false /* isSsReactivated */); // THEN the key has already been migrated ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); - verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean()); + verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean(), + anyBoolean()); assertThat(captor.getValue().getDevice()).isNotNull(); } @Test public void migrateKeyDeviceAfter() { // GIVEN that media and device info has already been received - mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(OLD_KEY, null, mMediaData, true /* immediately */, + false /* isSsReactivated */); mManager.onMediaDeviceChanged(OLD_KEY, null, mDeviceData); - mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(KEY, OLD_KEY, mMediaData, true /* immediately */, + false /* isSsReactivated */); reset(mListener); // WHEN a second key migration event is received for the device mManager.onMediaDeviceChanged(KEY, OLD_KEY, mDeviceData); // THEN the key has already be migrated ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); - verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean()); + verify(mListener).onMediaDataLoaded(eq(KEY), eq(KEY), captor.capture(), anyBoolean(), + anyBoolean()); assertThat(captor.getValue().getDevice()).isNotNull(); } @@ -187,7 +205,8 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void mediaDataRemovedAfterMediaEvent() { - mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */, + false /* isSsReactivated */); mManager.onMediaDataRemoved(KEY); verify(mListener).onMediaDataRemoved(eq(KEY)); } @@ -202,13 +221,15 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { @Test public void mediaDataKeyUpdated() { // GIVEN that device and media events have already been received - mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */, + false /* isSsReactivated */); mManager.onMediaDeviceChanged(KEY, null, mDeviceData); // WHEN the key is changed - mManager.onMediaDataLoaded("NEW_KEY", KEY, mMediaData, true /* immediately */); + mManager.onMediaDataLoaded("NEW_KEY", KEY, mMediaData, true /* immediately */, + false /* isSsReactivated */); // THEN the listener gets a load event with the correct keys ArgumentCaptor<MediaData> captor = ArgumentCaptor.forClass(MediaData.class); verify(mListener).onMediaDataLoaded( - eq("NEW_KEY"), any(), captor.capture(), anyBoolean()); + eq("NEW_KEY"), any(), captor.capture(), anyBoolean(), anyBoolean()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt index 17f2a07eb249..d8791867cb45 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataFilterTest.kt @@ -120,7 +120,8 @@ class MediaDataFilterTest : SysuiTestCase() { mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain) // THEN we should tell the listener - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true), + eq(false)) } @Test @@ -129,7 +130,7 @@ class MediaDataFilterTest : SysuiTestCase() { mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest) // THEN we should NOT tell the listener - verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean()) + verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyBoolean()) } @Test @@ -175,10 +176,12 @@ class MediaDataFilterTest : SysuiTestCase() { setUser(USER_GUEST) // THEN we should add back the guest user media - verify(listener).onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true), + eq(false)) // but not the main user's - verify(listener, never()).onMediaDataLoaded(eq(KEY), any(), eq(dataMain), anyBoolean()) + verify(listener, never()).onMediaDataLoaded(eq(KEY), any(), eq(dataMain), anyBoolean(), + anyBoolean()) } @Test @@ -245,7 +248,7 @@ class MediaDataFilterTest : SysuiTestCase() { mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) - verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean()) + verify(listener, never()).onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyBoolean()) verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean()) assertThat(mediaDataFilter.hasActiveMedia()).isFalse() } @@ -282,12 +285,15 @@ class MediaDataFilterTest : SysuiTestCase() { // WHEN we have media that was recently played, but not currently active val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), + eq(false)) // AND we get a smartspace signal mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) - // THEN we should tell listeners to treat the media as active instead + // THEN we should tell listeners to treat the media as not active instead + verify(listener, never()).onMediaDataLoaded(eq(KEY), eq(KEY), any(), anyBoolean(), + anyBoolean()) verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean()) assertThat(mediaDataFilter.hasActiveMedia()).isFalse() } @@ -299,14 +305,16 @@ class MediaDataFilterTest : SysuiTestCase() { // WHEN we have media that was recently played, but not currently active val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), + eq(false)) // AND we get a smartspace signal mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) // THEN we should tell listeners to treat the media as active instead val dataCurrentAndActive = dataCurrent.copy(active = true) - verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true), + eq(true)) assertThat(mediaDataFilter.hasActiveMedia()).isTrue() // Smartspace update shouldn't be propagated for the empty rec list. verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean()) @@ -317,14 +325,16 @@ class MediaDataFilterTest : SysuiTestCase() { // WHEN we have media that was recently played, but not currently active val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), + eq(false)) // AND we get a smartspace signal mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) // THEN we should tell listeners to treat the media as active instead val dataCurrentAndActive = dataCurrent.copy(active = true) - verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true), + eq(true)) assertThat(mediaDataFilter.hasActiveMedia()).isTrue() // Smartspace update should also be propagated but not prioritized. verify(listener) @@ -344,11 +354,17 @@ class MediaDataFilterTest : SysuiTestCase() { fun testOnSmartspaceMediaDataRemoved_usedMediaAndSmartspace_clearsBoth() { val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), + eq(false)) + mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) + val dataCurrentAndActive = dataCurrent.copy(active = true) + verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrentAndActive), eq(true), + eq(true)) + mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY) - verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), eq(dataCurrent), eq(true)) verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY) assertThat(mediaDataFilter.hasActiveMedia()).isFalse() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 3128db423a24..5b4e12463370 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -185,7 +185,8 @@ class MediaDataManagerTest : SysuiTestCase() { fun testOnMetaDataLoaded_callsListener() { mediaDataManager.onNotificationAdded(KEY, mediaNotification) mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java)) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), anyObject(), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), anyObject(), eq(true), + eq(false)) } @Test @@ -196,7 +197,8 @@ class MediaDataManagerTest : SysuiTestCase() { mediaDataManager.onNotificationAdded(KEY, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), + eq(false)) assertThat(mediaDataCaptor.value!!.active).isTrue() } @@ -215,7 +217,8 @@ class MediaDataManagerTest : SysuiTestCase() { mediaDataManager.onNotificationAdded(KEY, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), + eq(false)) val data = mediaDataCaptor.value assertThat(data.resumption).isFalse() mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) @@ -223,7 +226,8 @@ class MediaDataManagerTest : SysuiTestCase() { mediaDataManager.onNotificationRemoved(KEY) // THEN the media data indicates that it is for resumption verify(listener) - .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true)) + .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), + eq(false)) assertThat(mediaDataCaptor.value.resumption).isTrue() } @@ -236,7 +240,8 @@ class MediaDataManagerTest : SysuiTestCase() { assertThat(backgroundExecutor.runAllReady()).isEqualTo(2) assertThat(foregroundExecutor.runAllReady()).isEqualTo(2) verify(listener) - .onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true)) + .onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), + eq(false)) val data = mediaDataCaptor.value assertThat(data.resumption).isFalse() val resumableData = data.copy(resumeAction = Runnable {}) @@ -247,7 +252,8 @@ class MediaDataManagerTest : SysuiTestCase() { mediaDataManager.onNotificationRemoved(KEY) // THEN the data is for resumption and the key is migrated to the package name verify(listener) - .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true)) + .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), + eq(false)) assertThat(mediaDataCaptor.value.resumption).isTrue() verify(listener, never()).onMediaDataRemoved(eq(KEY)) // WHEN the second is removed @@ -255,7 +261,8 @@ class MediaDataManagerTest : SysuiTestCase() { // THEN the data is for resumption and the second key is removed verify(listener) .onMediaDataLoaded( - eq(PACKAGE_NAME), eq(PACKAGE_NAME), capture(mediaDataCaptor), eq(true)) + eq(PACKAGE_NAME), eq(PACKAGE_NAME), capture(mediaDataCaptor), eq(true), + eq(false)) assertThat(mediaDataCaptor.value.resumption).isTrue() verify(listener).onMediaDataRemoved(eq(KEY_2)) } @@ -269,7 +276,8 @@ class MediaDataManagerTest : SysuiTestCase() { mediaDataManager.onNotificationAdded(KEY, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), + eq(false)) val data = mediaDataCaptor.value val dataRemoteWithResume = data.copy(resumeAction = Runnable {}, isLocalSession = false) mediaDataManager.onMediaDataLoaded(KEY, null, dataRemoteWithResume) @@ -295,7 +303,8 @@ class MediaDataManagerTest : SysuiTestCase() { assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) // THEN the media data indicates that it is for resumption verify(listener) - .onMediaDataLoaded(eq(PACKAGE_NAME), eq(null), capture(mediaDataCaptor), eq(true)) + .onMediaDataLoaded(eq(PACKAGE_NAME), eq(null), capture(mediaDataCaptor), eq(true), + eq(false)) val data = mediaDataCaptor.value assertThat(data.resumption).isTrue() assertThat(data.song).isEqualTo(SESSION_TITLE) @@ -335,7 +344,8 @@ class MediaDataManagerTest : SysuiTestCase() { assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) verify(listener) - .onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true)) + .onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), + eq(false)) } @Test @@ -414,7 +424,8 @@ class MediaDataManagerTest : SysuiTestCase() { mediaDataManager.onNotificationAdded(KEY, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), + eq(false)) assertThat(mediaDataCaptor.value!!.lastActive).isAtLeast(currentTime) } @@ -431,7 +442,8 @@ class MediaDataManagerTest : SysuiTestCase() { mediaDataManager.setTimedOut(KEY, true, true) // THEN the last active time is not changed - verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), capture(mediaDataCaptor), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(KEY), capture(mediaDataCaptor), eq(true), + eq(false)) assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTime) } @@ -442,7 +454,8 @@ class MediaDataManagerTest : SysuiTestCase() { mediaDataManager.onNotificationAdded(KEY, mediaNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), + eq(false)) val data = mediaDataCaptor.value assertThat(data.resumption).isFalse() mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {})) @@ -454,7 +467,8 @@ class MediaDataManagerTest : SysuiTestCase() { // THEN the last active time is not changed verify(listener) - .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true)) + .onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor), eq(true), + eq(false)) assertThat(mediaDataCaptor.value.resumption).isTrue() assertThat(mediaDataCaptor.value.lastActive).isLessThan(currentTime) } @@ -480,7 +494,8 @@ class MediaDataManagerTest : SysuiTestCase() { assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) // THEN only the first MAX_COMPACT_ACTIONS are actually set - verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true)) + verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true), + eq(false)) assertThat(mediaDataCaptor.value.actionsToShowInCompact.size).isEqualTo( MediaDataManager.MAX_COMPACT_ACTIONS) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt index c6d7e92175eb..b9caab277c4c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaSessionBasedFilterTest.kt @@ -185,7 +185,8 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { filter.onMediaDataLoaded(KEY, null, mediaData1) bgExecutor.runAllReady() fgExecutor.runAllReady() - verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), + eq(false)) } @Test @@ -207,7 +208,8 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered - verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), + eq(false)) } @Test @@ -236,7 +238,8 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered - verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), + eq(false)) } @Test @@ -251,14 +254,15 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered - verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), + eq(false)) // WHEN a loaded event is received that matches the local session filter.onMediaDataLoaded(KEY, null, mediaData2) bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is filtered verify(mediaListener, never()).onMediaDataLoaded( - eq(KEY), eq(null), eq(mediaData2), anyBoolean()) + eq(KEY), eq(null), eq(mediaData2), anyBoolean(), anyBoolean()) } @Test @@ -274,7 +278,8 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { fgExecutor.runAllReady() // THEN the event is not filtered because there isn't a notification for the remote // session. - verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), + eq(false)) } @Test @@ -291,14 +296,15 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered - verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true), + eq(false)) // WHEN a loaded event is received that matches the local session filter.onMediaDataLoaded(key2, null, mediaData2) bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is filtered verify(mediaListener, never()) - .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean()) + .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean(), anyBoolean()) // AND there should be a removed event for key2 verify(mediaListener).onMediaDataRemoved(eq(key2)) } @@ -317,13 +323,15 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered - verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(key1), eq(null), eq(mediaData1), eq(true), + eq(false)) // WHEN a loaded event is received that matches the remote session filter.onMediaDataLoaded(key2, null, mediaData2) bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered - verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), eq(true), + eq(false)) } @Test @@ -339,13 +347,15 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered - verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), + eq(false)) // WHEN a loaded event is received that matches the local session filter.onMediaDataLoaded(KEY, null, mediaData2) bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered - verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData2), eq(true), + eq(false)) } @Test @@ -363,7 +373,8 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the event is not filtered - verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(KEY), eq(null), eq(mediaData1), eq(true), + eq(false)) } @Test @@ -385,7 +396,8 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the key migration event is fired - verify(mediaListener).onMediaDataLoaded(eq(key2), eq(key1), eq(mediaData2), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(key2), eq(key1), eq(mediaData2), eq(true), + eq(false)) } @Test @@ -415,12 +427,13 @@ public class MediaSessionBasedFilterTest : SysuiTestCase() { fgExecutor.runAllReady() // THEN the key migration event is filtered verify(mediaListener, never()) - .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean()) + .onMediaDataLoaded(eq(key2), eq(null), eq(mediaData2), anyBoolean(), anyBoolean()) // WHEN a loaded event is received that matches the remote session filter.onMediaDataLoaded(key2, null, mediaData1) bgExecutor.runAllReady() fgExecutor.runAllReady() // THEN the key migration event is fired - verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData1), eq(true)) + verify(mediaListener).onMediaDataLoaded(eq(key2), eq(null), eq(mediaData1), eq(true), + eq(false)) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java index 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/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java index 007a3b93eca8..33c7a571ce27 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java @@ -17,6 +17,7 @@ package com.android.systemui.people; import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME; +import static com.android.systemui.people.PeopleSpaceUtils.getContactLookupKeysWithBirthdaysToday; import static com.google.common.truth.Truth.assertThat; @@ -35,6 +36,7 @@ import android.app.Person; import android.app.people.IPeopleManager; import android.app.people.PeopleSpaceTile; import android.appwidget.AppWidgetManager; +import android.content.ContentProviderOperation; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -64,12 +66,17 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.text.SimpleDateFormat; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Map; import java.util.Optional; @@ -112,6 +119,7 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .setNotificationDataUri(URI) .setMessagesCount(1) .build(); + private static final String TEST_DISPLAY_NAME = "Display Name"; private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext, SHORTCUT_ID_1).setLongLabel( @@ -227,6 +235,12 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { .thenReturn(List.of(mNotificationEntry1, mNotificationEntry2, mNotificationEntry3)); } + @After + public void tearDown() { + cleanupTestContactFromContactProvider(); + } + + @Test public void testAugmentTileFromNotification() { PeopleSpaceTile tile = @@ -467,4 +481,82 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase { eq(WIDGET_ID_WITH_SHORTCUT), any()); } + + @Test + public void testBirthdayQueriesWithYear() throws Exception { + String birthdayToday = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); + addBirthdayToContactsDatabase(birthdayToday); + + List<String> lookupKeys = getContactLookupKeysWithBirthdaysToday(mContext); + + assertThat(lookupKeys).hasSize(1); + } + + @Test + public void testBirthdayQueriesWithoutYear() throws Exception { + String birthdayToday = new SimpleDateFormat("--MM-dd").format(new Date()); + addBirthdayToContactsDatabase(birthdayToday); + + List<String> lookupKeys = getContactLookupKeysWithBirthdaysToday(mContext); + + assertThat(lookupKeys).hasSize(1); + } + + @Test + public void testBirthdayQueriesWithDifferentDates() throws Exception { + Date yesterday = new Date(System.currentTimeMillis() - Duration.ofDays(1).toMillis()); + String birthdayYesterday = new SimpleDateFormat("--MM-dd").format(yesterday); + addBirthdayToContactsDatabase(birthdayYesterday); + + List<String> lookupKeys = getContactLookupKeysWithBirthdaysToday(mContext); + + assertThat(lookupKeys).isEmpty(); + } + + private void addBirthdayToContactsDatabase(String birthdayDate) throws Exception { + ContentResolver resolver = mContext.getContentResolver(); + ArrayList<ContentProviderOperation> ops = new ArrayList<>(3); + ops.add(ContentProviderOperation + .newInsert(ContactsContract.RawContacts.CONTENT_URI) + .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, "com.google") + .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, "fakeAccountName") + .build()); + ops.add(ContentProviderOperation + .newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, + TEST_DISPLAY_NAME) + .build()); + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference( + ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue( + ContactsContract.Data.MIMETYPE, + ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE) + .withValue( + ContactsContract.CommonDataKinds.Event.TYPE, + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY) + .withValue( + ContactsContract.CommonDataKinds.Event.START_DATE, birthdayDate) + .build()); + resolver.applyBatch(ContactsContract.AUTHORITY, ops); + } + + private void cleanupTestContactFromContactProvider() { + Cursor cursor = mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, + null, + ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + "=?", + new String[]{TEST_DISPLAY_NAME}, + null); + while (cursor.moveToNext()) { + String contactId = cursor.getString(cursor.getColumnIndex( + ContactsContract.Contacts.NAME_RAW_CONTACT_ID)); + mContext.getContentResolver().delete(ContactsContract.Data.CONTENT_URI, + ContactsContract.Data.RAW_CONTACT_ID + "=?", + new String[]{contactId}); + } + cursor.close(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java index 67505c42ef9a..75ae9cbdee44 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java @@ -35,7 +35,6 @@ import static com.android.systemui.people.PeopleSpaceUtils.VALID_CONTACT; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -541,7 +540,9 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { tileWithDndBlocking, mOptions).getViews(); View result = views.apply(mContext, null); - assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout); + assertResourcesEqual( + result.getSourceLayoutResId(), + R.layout.people_tile_with_suppression_detail_content_horizontal); tileWithDndBlocking = PERSON_TILE.toBuilder() .setNotificationPolicyState(BLOCK_CONVERSATIONS) @@ -551,7 +552,9 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { tileWithDndBlocking, mOptions).getViews(); result = views.apply(mContext, null); - assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout); + assertResourcesNotEqual( + result.getSourceLayoutResId(), + R.layout.people_tile_with_suppression_detail_content_horizontal); tileWithDndBlocking = PERSON_TILE.toBuilder() .setNotificationPolicyState(SHOW_IMPORTANT_CONVERSATIONS) @@ -560,7 +563,9 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { tileWithDndBlocking, mOptions).getViews(); result = views.apply(mContext, null); - assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout); + assertResourcesEqual( + result.getSourceLayoutResId(), + R.layout.people_tile_with_suppression_detail_content_horizontal); tileWithDndBlocking = PERSON_TILE.toBuilder() .setNotificationPolicyState(SHOW_IMPORTANT_CONVERSATIONS) @@ -570,7 +575,9 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { tileWithDndBlocking, mOptions).getViews(); result = views.apply(mContext, null); - assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout); + assertResourcesNotEqual( + result.getSourceLayoutResId(), + R.layout.people_tile_with_suppression_detail_content_horizontal); tileWithDndBlocking = PERSON_TILE.toBuilder() .setNotificationPolicyState(SHOW_STARRED_CONTACTS) @@ -580,7 +587,9 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { tileWithDndBlocking, mOptions).getViews(); result = views.apply(mContext, null); - assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout); + assertResourcesEqual( + result.getSourceLayoutResId(), + R.layout.people_tile_with_suppression_detail_content_horizontal); tileWithDndBlocking = PERSON_TILE.toBuilder() .setNotificationPolicyState(SHOW_STARRED_CONTACTS) @@ -590,7 +599,9 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { tileWithDndBlocking, mOptions).getViews(); result = views.apply(mContext, null); - assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout); + assertResourcesNotEqual( + result.getSourceLayoutResId(), + R.layout.people_tile_with_suppression_detail_content_horizontal); tileWithDndBlocking = PERSON_TILE.toBuilder() .setNotificationPolicyState(SHOW_CONTACTS) @@ -600,7 +611,9 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { tileWithDndBlocking, mOptions).getViews(); result = views.apply(mContext, null); - assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout); + assertResourcesNotEqual( + result.getSourceLayoutResId(), + R.layout.people_tile_with_suppression_detail_content_horizontal); tileWithDndBlocking = PERSON_TILE.toBuilder() .setNotificationPolicyState(SHOW_CONTACTS) @@ -610,7 +623,9 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { tileWithDndBlocking, mOptions).getViews(); result = views.apply(mContext, null); - assertNotEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout); + assertResourcesNotEqual( + result.getSourceLayoutResId(), + R.layout.people_tile_with_suppression_detail_content_horizontal); tileWithDndBlocking = PERSON_TILE.toBuilder() .setNotificationPolicyState(SHOW_CONTACTS) @@ -619,7 +634,11 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { tileWithDndBlocking, mOptions).getViews(); result = views.apply(mContext, null); - assertEquals(result.getSourceLayoutResId(), R.layout.people_tile_suppressed_layout); + assertResourcesEqual( + result.getSourceLayoutResId(), + R.layout.people_tile_with_suppression_detail_content_horizontal); + assertThat(result.<TextView>findViewById(R.id.text_content).getText().toString()) + .isEqualTo(mContext.getString(R.string.paused_by_dnd)); } @Test @@ -1068,4 +1087,21 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { return new PeopleTileViewHelper(mContext, tile, 0, options, new PeopleTileKey(tile.getId(), 0, tile.getPackageName())); } + + private void assertResourcesEqual(int expected, int actual) { + assertThat(getResourceName(actual)).isEqualTo(getResourceName(expected)); + } + + private void assertResourcesNotEqual(int expected, int actual) { + assertThat(getResourceName(actual)).isNotEqualTo(getResourceName(expected)); + } + + private String getResourceName(int resId) { + Resources resources = mContext.getResources(); + try { + return resources.getResourceEntryName(resId); + } catch (Resources.NotFoundException e) { + return String.valueOf(resId); + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java index 7af374308bef..c48f26b8c853 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java @@ -43,6 +43,7 @@ import static android.app.people.PeopleSpaceTile.SHOW_IMPORTANT_CONVERSATIONS; import static android.app.people.PeopleSpaceTile.SHOW_STARRED_CONTACTS; import static android.content.Intent.ACTION_BOOT_COMPLETED; import static android.content.Intent.ACTION_PACKAGES_SUSPENDED; +import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static android.content.PermissionChecker.PERMISSION_GRANTED; import static android.content.PermissionChecker.PERMISSION_HARD_DENIED; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE; @@ -88,7 +89,6 @@ import android.content.pm.ShortcutInfo; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Bundle; -import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.service.notification.ConversationChannelWrapper; @@ -1104,7 +1104,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testGetPeopleTileFromPersistentStorageNoConversation() throws RemoteException { + public void testGetPeopleTileFromPersistentStorageNoConversation() throws Exception { when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(null); PeopleTileKey key = new PeopleTileKey(SHORTCUT_ID, 0, TEST_PACKAGE_A); PeopleSpaceTile tile = mManager.getTileFromPersistentStorage(key, WIDGET_ID_WITH_SHORTCUT); @@ -1223,8 +1223,38 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testUpdateWidgetsOnStateChange() { - mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED); + public void testUpdateWidgetsFromBroadcastInBackgroundBootCompleteWithPackageUninstalled() + throws Exception { + when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenThrow( + PackageManager.NameNotFoundException.class); + + // We should remove widgets if the package is uninstalled at next reboot if we missed the + // package removed broadcast. + mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED); + + PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); + assertThat(tile).isNull(); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + } + + @Test + public void testUpdateWidgetsFromBroadcastInBackgroundPackageRemovedWithPackageUninstalled() + throws Exception { + when(mPackageManager.getApplicationInfoAsUser(any(), anyInt(), anyInt())).thenThrow( + PackageManager.NameNotFoundException.class); + + mManager.updateWidgetsFromBroadcastInBackground(ACTION_PACKAGE_REMOVED); + + PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); + assertThat(tile).isNull(); + verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT), + any()); + } + + @Test + public void testUpdateWidgetsFromBroadcastInBackground() { + mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); assertThat(tile.isPackageSuspended()).isFalse(); @@ -1236,10 +1266,10 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testUpdateWidgetsOnStateChangeWithUserQuieted() { + public void testUpdateWidgetsFromBroadcastInBackgroundWithUserQuieted() { when(mUserManager.isQuietModeEnabled(any())).thenReturn(true); - mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED); + mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); assertThat(tile.isPackageSuspended()).isFalse(); @@ -1248,10 +1278,10 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testUpdateWidgetsOnStateChangeWithPackageSuspended() throws Exception { + public void testUpdateWidgetsFromBroadcastInBackgroundWithPackageSuspended() throws Exception { when(mPackageManager.isPackageSuspended(any())).thenReturn(true); - mManager.updateWidgetsOnStateChange(ACTION_PACKAGES_SUSPENDED); + mManager.updateWidgetsFromBroadcastInBackground(ACTION_PACKAGES_SUSPENDED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); assertThat(tile.isPackageSuspended()).isTrue(); @@ -1260,9 +1290,9 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testUpdateWidgetsOnStateChangeNotInDnd() { + public void testUpdateWidgetsFromBroadcastInBackgroundNotInDnd() { int expected = 0; - mManager.updateWidgetsOnStateChange(NotificationManager + mManager.updateWidgetsFromBroadcastInBackground(NotificationManager .ACTION_INTERRUPTION_FILTER_CHANGED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); @@ -1270,14 +1300,14 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testUpdateWidgetsOnStateChangeAllConversations() { + public void testUpdateWidgetsFromBroadcastInBackgroundAllConversations() { int expected = 0; when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( INTERRUPTION_FILTER_PRIORITY); when(mNotificationPolicy.allowConversations()).thenReturn(true); setFinalField("priorityConversationSenders", CONVERSATION_SENDERS_ANYONE); - mManager.updateWidgetsOnStateChange(NotificationManager + mManager.updateWidgetsFromBroadcastInBackground(NotificationManager .ACTION_INTERRUPTION_FILTER_CHANGED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); @@ -1285,7 +1315,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testUpdateWidgetsOnStateChangeAllowOnlyImportantConversations() { + public void testUpdateWidgetsFromBroadcastInBackgroundAllowOnlyImportantConversations() { int expected = 0; // Only allow important conversations. when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( @@ -1293,7 +1323,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mNotificationPolicy.allowConversations()).thenReturn(true); setFinalField("priorityConversationSenders", CONVERSATION_SENDERS_IMPORTANT); - mManager.updateWidgetsOnStateChange(NotificationManager + mManager.updateWidgetsFromBroadcastInBackground(NotificationManager .ACTION_INTERRUPTION_FILTER_CHANGED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); @@ -1302,13 +1332,13 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testUpdateWidgetsOnStateChangeAllowNoConversations() { + public void testUpdateWidgetsFromBroadcastInBackgroundAllowNoConversations() { int expected = 0; when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( INTERRUPTION_FILTER_PRIORITY); when(mNotificationPolicy.allowConversations()).thenReturn(false); - mManager.updateWidgetsOnStateChange(NotificationManager + mManager.updateWidgetsFromBroadcastInBackground(NotificationManager .ACTION_INTERRUPTION_FILTER_CHANGED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); @@ -1316,7 +1346,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testUpdateWidgetsOnStateChangeAllowNoConversationsAllowContactMessages() { + public void testUpdateWidgetsFromBroadcastInBackgroundAllowNoConversationsAllowContactMessages() { int expected = 0; when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( INTERRUPTION_FILTER_PRIORITY); @@ -1324,14 +1354,14 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mNotificationPolicy.allowMessagesFrom()).thenReturn(ZenModeConfig.SOURCE_CONTACT); when(mNotificationPolicy.allowMessages()).thenReturn(true); - mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED); + mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONTACTS); } @Test - public void testUpdateWidgetsOnStateChangeAllowNoConversationsAllowStarredContactMessages() { + public void testUpdateWidgetsFromBroadcastInBackgroundAllowNoConversationsAllowStarredContactMessages() { int expected = 0; when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( INTERRUPTION_FILTER_PRIORITY); @@ -1339,26 +1369,26 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mNotificationPolicy.allowMessagesFrom()).thenReturn(ZenModeConfig.SOURCE_STAR); when(mNotificationPolicy.allowMessages()).thenReturn(true); - mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED); + mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_STARRED_CONTACTS); setFinalField("suppressedVisualEffects", SUPPRESSED_EFFECT_FULL_SCREEN_INTENT | SUPPRESSED_EFFECT_AMBIENT); - mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED); + mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED); tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONVERSATIONS); } @Test - public void testUpdateWidgetsOnStateChangeAllowAlarmsOnly() { + public void testUpdateWidgetsFromBroadcastInBackgroundAllowAlarmsOnly() { int expected = 0; when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( INTERRUPTION_FILTER_ALARMS); - mManager.updateWidgetsOnStateChange(NotificationManager + mManager.updateWidgetsFromBroadcastInBackground(NotificationManager .ACTION_INTERRUPTION_FILTER_CHANGED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); @@ -1366,7 +1396,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { } @Test - public void testUpdateWidgetsOnStateChangeAllowVisualEffectsAndAllowAlarmsOnly() { + public void testUpdateWidgetsFromBroadcastInBackgroundAllowVisualEffectsAndAllowAlarmsOnly() { int expected = 0; // If we show visuals, but just only make sounds for alarms, still show content in tiles. when(mNotificationManager.getCurrentInterruptionFilter()).thenReturn( @@ -1374,7 +1404,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { setFinalField("suppressedVisualEffects", SUPPRESSED_EFFECT_FULL_SCREEN_INTENT | SUPPRESSED_EFFECT_AMBIENT); - mManager.updateWidgetsOnStateChange(ACTION_BOOT_COMPLETED); + mManager.updateWidgetsFromBroadcastInBackground(ACTION_BOOT_COMPLETED); PeopleSpaceTile tile = mManager.mTiles.get(WIDGET_ID_WITH_SHORTCUT); assertThat(tile.getNotificationPolicyState()).isEqualTo(expected | SHOW_CONVERSATIONS); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt index 19ffa4991eaf..b8d018f5f850 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.qs.tileimpl import android.testing.AndroidTestingRunner -import androidx.test.filters.SmallTest +import androidx.test.filters.MediumTest import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -27,7 +27,7 @@ import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidTestingRunner::class) -@SmallTest +@MediumTest class TilesStatesTextTest : SysuiTestCase() { @Test @@ -51,4 +51,29 @@ class TilesStatesTextTest : SysuiTestCase() { assertThat(array.size).isEqualTo(3) } + + @Test + fun testStockTilesSubtitlesMap() { + val tiles = mContext.getString(R.string.quick_settings_tiles_stock).split(",") + tiles.forEach { spec -> + val resName = "${QSTileViewImpl.TILE_STATE_RES_PREFIX}$spec" + val resId = mContext.resources.getIdentifier(resName, "array", mContext.packageName) + + assertNotEquals("Missing resource for $resName", 0, resId) + + assertThat(SubtitleArrayMapping.getSubtitleId(spec)).isEqualTo(resId) + } + } + + @Test + fun testStockTilesSubtitlesReturnsDefault_unknown() { + assertThat(SubtitleArrayMapping.getSubtitleId("unknown")) + .isEqualTo(R.array.tile_states_default) + } + + @Test + fun testStockTilesSubtitlesReturnsDefault_null() { + assertThat(SubtitleArrayMapping.getSubtitleId(null)) + .isEqualTo(R.array.tile_states_default) + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/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..d9f14263abcf 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 @@ -70,7 +71,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { @Mock private lateinit var windowToken: IBinder @Mock private lateinit var shadeSpring: NotificationShadeDepthController.DepthAnimation @Mock private lateinit var shadeAnimation: NotificationShadeDepthController.DepthAnimation - @Mock private lateinit var globalActionsSpring: NotificationShadeDepthController.DepthAnimation + @Mock private lateinit var brightnessSpring: NotificationShadeDepthController.DepthAnimation @Mock private lateinit var listener: NotificationShadeDepthController.DepthListener @Mock private lateinit var dozeParameters: DozeParameters @Captor private lateinit var scrimVisibilityCaptor: ArgumentCaptor<Consumer<Int>> @@ -90,7 +91,11 @@ 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,7 +104,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { notificationShadeWindowController, dozeParameters, dumpManager) notificationShadeDepthController.shadeSpring = shadeSpring notificationShadeDepthController.shadeAnimation = shadeAnimation - notificationShadeDepthController.globalActionsSpring = globalActionsSpring + notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring notificationShadeDepthController.root = root val captor = ArgumentCaptor.forClass(StatusBarStateController.StateListener::class.java) @@ -191,22 +196,9 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { } @Test - fun updateGlobalDialogVisibility_animatesBlur() { - notificationShadeDepthController.updateGlobalDialogVisibility(0.5f, root) - verify(globalActionsSpring).animateTo(eq(maxBlur / 2), eq(root)) - } - - @Test - fun updateGlobalDialogVisibility_appliesBlur_withoutHomeControls() { - `when`(globalActionsSpring.radius).thenReturn(maxBlur) - notificationShadeDepthController.updateBlurCallback.doFrame(0) - verify(blurUtils).applyBlur(any(), eq(maxBlur), eq(false)) - } - - @Test - fun updateGlobalDialogVisibility_appliesBlur_unlessHomeControls() { - notificationShadeDepthController.showingHomeControls = true - `when`(globalActionsSpring.radius).thenReturn(maxBlur) + fun setFullShadeTransition_appliesBlur_onlyIfSupported() { + reset(blurUtils) + notificationShadeDepthController.transitionToFullShadeProgress = 1f notificationShadeDepthController.updateBlurCallback.doFrame(0) verify(blurUtils).applyBlur(any(), eq(0), eq(false)) } @@ -267,6 +259,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/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index ff15d0151ea4..5691660b4882 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -27,6 +27,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.SysuiTestCase; import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.model.SysUiState; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.statusbar.CommandQueue; @@ -70,6 +71,7 @@ public class WMShellTest extends SysuiTestCase { @Mock LegacySplitScreen mLegacySplitScreen; @Mock OneHanded mOneHanded; @Mock HideDisplayCutout mHideDisplayCutout; + @Mock WakefulnessLifecycle mWakefulnessLifecycle; @Mock ProtoTracer mProtoTracer; @Mock ShellCommandHandler mShellCommandHandler; @Mock ShellExecutor mSysUiMainExecutor; @@ -82,7 +84,8 @@ public class WMShellTest extends SysuiTestCase { Optional.of(mOneHanded), Optional.of(mHideDisplayCutout), Optional.of(mShellCommandHandler), mCommandQueue, mConfigurationController, mKeyguardUpdateMonitor, mNavigationModeController, - mScreenLifecycle, mSysUiState, mProtoTracer, mSysUiMainExecutor); + mScreenLifecycle, mSysUiState, mProtoTracer, mWakefulnessLifecycle, + mSysUiMainExecutor); } @Test 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/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 05131d44b01e..906edb30ff12 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -753,9 +753,19 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } return notMatch; })); + restartBleScan(); } void onAssociationPreRemove(Association association) { + if (association.isNotifyOnDeviceNearby()) { + ServiceConnector<ICompanionDeviceService> serviceConnector = + mDeviceListenerServiceConnectors.forUser(association.getUserId()) + .get(association.getPackageName()); + if (serviceConnector != null) { + serviceConnector.unbind(); + } + } + String deviceProfile = association.getDeviceProfile(); if (deviceProfile != null) { Association otherAssociationWithDeviceProfile = find( @@ -787,16 +797,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } } } - - if (association.isNotifyOnDeviceNearby()) { - ServiceConnector<ICompanionDeviceService> serviceConnector = - mDeviceListenerServiceConnectors.forUser(association.getUserId()) - .get(association.getPackageName()); - if (serviceConnector != null) { - serviceConnector.unbind(); - restartBleScan(); - } - } } private void updateSpecialAccessPermissionForAssociatedPackage(Association association) { diff --git a/services/companion/java/com/android/server/companion/OWNERS b/services/companion/java/com/android/server/companion/OWNERS index da723b3b67da..734d8b6c5f43 100644 --- a/services/companion/java/com/android/server/companion/OWNERS +++ b/services/companion/java/com/android/server/companion/OWNERS @@ -1 +1 @@ -eugenesusla@google.com
\ No newline at end of file +include /core/java/android/companion/OWNERS
\ No newline at end of file diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 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/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 5e388d94869d..89781d3fbb0a 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1976,7 +1976,7 @@ public final class ActiveServices { r.foregroundId = 0; r.foregroundNoti = null; } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) { - r.stripForegroundServiceFlagFromNotification(); + dropFgsNotificationStateLocked(r); if ((flags & Service.STOP_FOREGROUND_DETACH) != 0) { r.foregroundId = 0; r.foregroundNoti = null; @@ -4225,9 +4225,10 @@ public final class ActiveServices { } r.isForeground = false; + r.mFgsNotificationWasDeferred = false; + dropFgsNotificationStateLocked(r); r.foregroundId = 0; r.foregroundNoti = null; - r.mFgsNotificationWasDeferred = false; resetFgsRestrictionLocked(r); // Clear start entries. @@ -4291,6 +4292,35 @@ public final class ActiveServices { smap.ensureNotStartingBackgroundLocked(r); } + private void dropFgsNotificationStateLocked(ServiceRecord r) { + // If this is the only FGS using this notification, clear its FGS flag + boolean shared = false; + final ServiceMap smap = mServiceMap.get(r.userId); + if (smap != null) { + // Is any other FGS using this notification? + final int numServices = smap.mServicesByInstanceName.size(); + for (int i = 0; i < numServices; i++) { + final ServiceRecord sr = smap.mServicesByInstanceName.valueAt(i); + if (sr == r) { + continue; + } + if (sr.isForeground + && r.foregroundId == sr.foregroundId + && r.appInfo.packageName.equals(sr.appInfo.packageName)) { + shared = true; + break; + } + } + } else { + Slog.wtf(TAG, "FGS " + r + " not found!"); + } + + // No other FGS is sharing this notification, so we're done with it + if (!shared) { + r.stripForegroundServiceFlagFromNotification(); + } + } + void removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp, ActivityServiceConnectionsHolder skipAct, boolean enqueueOomAdj) { IBinder binder = c.conn.asBinder(); @@ -6165,8 +6195,10 @@ public final class ActiveServices { && code != REASON_UID_VISIBLE; } - // TODO: remove this notification after feature development is done private void showFgsBgRestrictedNotificationLocked(ServiceRecord r) { + if (!mAm.mConstants.mFgsStartRestrictionNotificationEnabled /* default is false */) { + return; + } final Context context = mAm.mContext; final String title = "Foreground Service BG-Launch Restricted"; final String content = "App restricted: " + r.mRecentCallingPackage; diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 0d19efc20785..445d0ba2ee6d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -198,6 +198,13 @@ final class ActivityManagerConstants extends ContentObserver { "default_fgs_starts_restriction_enabled"; /** + * Default value for mFgsStartRestrictionNotificationEnabled if not explicitly set in + * Settings.Global. + */ + private static final String KEY_DEFAULT_FGS_STARTS_RESTRICTION_NOTIFICATION_ENABLED = + "default_fgs_starts_restriction_notification_enabled"; + + /** * Default value for mFgsStartRestrictionCheckCallerTargetSdk if not explicitly set in * Settings.Global. */ @@ -432,6 +439,10 @@ final class ActivityManagerConstants extends ContentObserver { // at all. volatile boolean mFlagFgsStartRestrictionEnabled = true; + // Whether to display a notification when a service is restricted from startForeground due to + // foreground service background start restriction. + volatile boolean mFgsStartRestrictionNotificationEnabled = false; + /** * Indicates whether the foreground service background start restriction is enabled for * caller app that is targeting S+. @@ -652,6 +663,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED: updateFgsStartsRestriction(); break; + case KEY_DEFAULT_FGS_STARTS_RESTRICTION_NOTIFICATION_ENABLED: + updateFgsStartsRestrictionNotification(); + break; case KEY_DEFAULT_FGS_STARTS_RESTRICTION_CHECK_CALLER_TARGET_SDK: updateFgsStartsRestrictionCheckCallerTargetSdk(); break; @@ -953,6 +967,13 @@ final class ActivityManagerConstants extends ContentObserver { /*defaultValue*/ true); } + private void updateFgsStartsRestrictionNotification() { + mFgsStartRestrictionNotificationEnabled = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_DEFAULT_FGS_STARTS_RESTRICTION_NOTIFICATION_ENABLED, + /*defaultValue*/ false); + } + private void updateFgsStartsRestrictionCheckCallerTargetSdk() { mFgsStartRestrictionCheckCallerTargetSdk = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -1272,6 +1293,9 @@ final class ActivityManagerConstants extends ContentObserver { pw.print("="); pw.println(mFlagBackgroundFgsStartRestrictionEnabled); pw.print(" "); pw.print(KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED); pw.print("="); pw.println(mFlagFgsStartRestrictionEnabled); + pw.print(" "); pw.print(KEY_DEFAULT_FGS_STARTS_RESTRICTION_NOTIFICATION_ENABLED); + pw.print("="); + pw.println(mFgsStartRestrictionNotificationEnabled); pw.print(" "); pw.print(KEY_DEFAULT_FGS_STARTS_RESTRICTION_CHECK_CALLER_TARGET_SDK); pw.print("="); pw.println(mFgsStartRestrictionCheckCallerTargetSdk); pw.print(" "); pw.print(KEY_FGS_ATOM_SAMPLE_RATE); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 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/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 141f081ab9a7..804e442bc8de 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -20,6 +20,7 @@ import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; import static android.os.PowerExemptionManager.REASON_DENIED; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_SERVICE; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -928,13 +929,17 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN public void postNotification() { final int appUid = appInfo.uid; final int appPid = app.getPid(); - if (foregroundId != 0 && foregroundNoti != null) { + if (isForeground && foregroundNoti != null) { // Do asynchronous communication with notification manager to // avoid deadlocks. final String localPackageName = packageName; final int localForegroundId = foregroundId; final Notification _foregroundNoti = foregroundNoti; final ServiceRecord record = this; + if (DEBUG_FOREGROUND_SERVICE) { + Slog.d(TAG, "Posting notification " + _foregroundNoti + + " for foreground service " + this); + } ams.mHandler.post(new Runnable() { public void run() { NotificationManagerInternal nm = LocalServices.getService( @@ -1066,10 +1071,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } public void stripForegroundServiceFlagFromNotification() { - if (foregroundId == 0) { - return; - } - final int localForegroundId = foregroundId; final int localUserId = userId; final String localPackageName = packageName; diff --git a/services/core/java/com/android/server/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 66142bff0453..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 @@ -46,6 +46,8 @@ final class AidlConversionUtils { return BiometricFingerprintConstants.FINGERPRINT_ERROR_UNABLE_TO_REMOVE; } else if (aidlError == Error.VENDOR) { return BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR; + } else if (aidlError == Error.BAD_CALIBRATION) { + return BiometricFingerprintConstants.FINGERPRINT_ERROR_BAD_CALIBARTION; } else { return BiometricFingerprintConstants.FINGERPRINT_ERROR_UNKNOWN; } @@ -78,8 +80,7 @@ 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/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index 3c9d802cb4b1..85995a5cc99c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -178,6 +178,9 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } + + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + mCallback.onClientFinished(this, false /* success */); } @Override @@ -192,5 +195,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } + + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + mCallback.onClientFinished(this, false /* success */); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/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/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index c2375351aee9..de6e494a5581 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -603,6 +603,10 @@ final class CompatConfig { try (InputStream in = new BufferedInputStream(new FileInputStream(overridesFile))) { Overrides overrides = com.android.server.compat.overrides.XmlParser.read(in); + if (overrides == null) { + Slog.w(TAG, "Parsing " + overridesFile.getPath() + " failed"); + return; + } for (ChangeOverrides changeOverrides : overrides.getChangeOverrides()) { long changeId = changeOverrides.getChangeId(); CompatChange compatChange = mChanges.get(changeId); diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 19e858c25c41..0d317f4ba164 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -36,6 +36,7 @@ import android.content.pm.PackageManagerInternal; import android.net.Uri; import android.os.Binder; import android.os.Build; +import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.util.Slog; @@ -359,7 +360,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { private ApplicationInfo getApplicationInfo(String packageName, int userId) { return LocalServices.getService(PackageManagerInternal.class).getApplicationInfo( - packageName, 0, userId, userId); + packageName, 0, Process.myUid(), userId); } private void killPackage(String packageName) { diff --git a/services/core/java/com/android/server/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 a8b493d68a74..2419873cdc41 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -436,8 +436,10 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.nio.charset.StandardCharsets; import java.security.DigestException; import java.security.DigestInputStream; @@ -1918,14 +1920,65 @@ public class PackageManagerService extends IPackageManager.Stub } /** - * A computer provides the functional interface to the snapshot. + * A {@link Computer} provides a set of functions that can operate on live data or snapshot + * data. At this time, the {@link Computer} is implemented by the + * {@link ComputerEngine}, which is in turn extended by {@link ComputerLocked}. + * + * New functions must be added carefully. + * <ol> + * <li> New functions must be true functions with respect to data collected in a + * {@link Snapshot}. Such data may never be modified from inside a {@link Computer} + * function. + * </li> + * + * <li> A new function must be implemented in {@link ComputerEngine}. + * </li> + * + * <li> A new function must be overridden in {@link ComputerLocked} if the function + * cannot safely access live data without holding the PackageManagerService lock. The + * form of the {@link ComputerLocked} function must be a single call to the + * {@link ComputerEngine} implementation, wrapped in a <code>synchronized</code> + * block. Functions in {@link ComputerLocked} should never include any other code. + * </li> + * + * Care must be taken when deciding if a function should be overridden in + * {@link ComputerLocked}. The complex lock relationships of PackageManagerService + * and other managers (like PermissionManager) mean deadlock is possible. On the + * other hand, not overriding in {@link ComputerLocked} may leave a function walking + * unstable data. + * + * To coax developers to consider such issues carefully, all methods in + * {@link Computer} must be annotated with <code>@LiveImplementation(override = + * MANDATORY)</code> or <code>LiveImplementation(locked = NOT_ALLOWED)</code>. A unit + * test verifies the annotation and that the annotation corresponds to the code in + * {@link ComputerEngine} and {@link ComputerLocked}. */ - private interface Computer { + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + protected interface Computer { + + /** + * Every method must be annotated. + */ + @Target({ ElementType.METHOD }) + @Retention(RetentionPolicy.RUNTIME) + public @interface LiveImplementation { + // A Computer method must be annotated with one of the following values: + // MANDATORY - the method must be overridden in ComputerEngineLive. The + // format of the override is a call to the super method, wrapped in a + // synchronization block. + // NOT_ALLOWED - the method may not appear in the live computer. It must + // be final in the ComputerEngine. + int MANDATORY = 1; + int NOT_ALLOWED = 2; + int override() default MANDATORY; + String rationale() default ""; + } /** * Administrative statistics: record that the snapshot has been used. Every call * to use() increments the usage counter. */ + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) default void use() { } @@ -1933,95 +1986,154 @@ public class PackageManagerService extends IPackageManager.Stub * Fetch the snapshot usage counter. * @return The number of times this snapshot was used. */ + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) default int getUsed() { return 0; } + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, @PrivateResolveFlags int privateResolveFlags, int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid, boolean includeInstantApps); + @LiveImplementation(override = LiveImplementation.MANDATORY) @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(Intent intent, String resolvedType, int flags, int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits, String pkgName, String instantAppPkgName); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ActivityInfo getActivityInfo(ComponentName component, int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ActivityInfo getActivityInfoInternal(ComponentName component, int flags, int filterCallingUid, int userId); + @LiveImplementation(override = LiveImplementation.MANDATORY) AndroidPackage getPackage(String packageName); + @LiveImplementation(override = LiveImplementation.MANDATORY) AndroidPackage getPackage(int uid); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags, int filterCallingUid, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ApplicationInfo getApplicationInfo(String packageName, int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ApplicationInfo getApplicationInfoInternal(String packageName, int flags, int filterCallingUid, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ComponentName getDefaultHomeActivity(int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent, String resolvedType, int flags, int sourceUserId, int parentUserId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) Intent getHomeIntent(); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent, String resolvedType, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos, String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid, boolean resolveForStart, int userId, Intent intent); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) PackageInfo getPackageInfo(String packageName, int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) PackageInfo getPackageInfoInternal(String packageName, long versionCode, int flags, int filterCallingUid, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) PackageSetting getPackageSetting(String packageName); + @LiveImplementation(override = LiveImplementation.MANDATORY) PackageSetting getPackageSettingInternal(String packageName, int callingUid); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter, int sourceUserId, int targetUserId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) ServiceInfo getServiceInfo(ComponentName component, int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version); + @LiveImplementation(override = LiveImplementation.MANDATORY) String getInstantAppPackageName(int callingUid); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) String resolveExternalPackageNameLPr(AndroidPackage pkg); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) String resolveInternalPackageNameLPr(String packageName, long versionCode); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) String[] getPackagesForUid(int uid); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) UserInfo getProfileParent(int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean canViewInstantApps(int callingUid, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId, int flags); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean isCallerSameApp(String packageName, int uid); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean isComponentVisibleToInstantApp(@Nullable ComponentName component); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean isComponentVisibleToInstantApp(@Nullable ComponentName component, @ComponentType int type); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId, String resolvedType, int flags); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean isInstantApp(String packageName, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean isInstantAppInternal(String packageName, @UserIdInt int userId, int callingUid); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid, @Nullable ComponentName component, @ComponentType int componentType, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus, int callingUid, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) int checkUidPermission(String permName, int uid); + @LiveImplementation(override = LiveImplementation.MANDATORY) int getPackageUidInternal(String packageName, int flags, int userId, int callingUid); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) int updateFlagsForApplication(int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) int updateFlagsForComponent(int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) int updateFlagsForPackage(int flags, int userId); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps, boolean isImplicitImageCaptureIntentAndNotSetByDpc); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps, boolean onlyExposedExplicitly, boolean isImplicitImageCaptureIntentAndNotSetByDpc); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId, boolean requireFullPermission, boolean checkShell, String message); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, boolean requireFullPermission, boolean checkShell, String message); + @LiveImplementation(override = LiveImplementation.NOT_ALLOWED) void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, boolean requireFullPermission, boolean checkShell, boolean requirePermissionWhenSameUser, String message); + @LiveImplementation(override = LiveImplementation.MANDATORY) SigningDetails getSigningDetails(@NonNull String packageName); + @LiveImplementation(override = LiveImplementation.MANDATORY) SigningDetails getSigningDetails(int uid); + @LiveImplementation(override = LiveImplementation.MANDATORY) boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId); + @LiveImplementation(override = LiveImplementation.MANDATORY) boolean filterAppAccess(String packageName, int callingUid, int userId); + @LiveImplementation(override = LiveImplementation.MANDATORY) void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState); } @@ -2030,7 +2142,8 @@ public class PackageManagerService extends IPackageManager.Stub * is entirely self-contained - it has no implicit access to * PackageManagerService. */ - private static class ComputerEngine implements Computer { + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + protected static class ComputerEngine implements Computer { // The administrative use counter. private int mUsed = 0; @@ -2076,7 +2189,7 @@ public class PackageManagerService extends IPackageManager.Stub // PackageManagerService attributes that are primitives are referenced through the // pms object directly. Primitives are the only attributes so referenced. protected final PackageManagerService mService; - protected boolean safeMode() { + private boolean safeMode() { return mService.mSafeMode; } protected ComponentName resolveComponentName() { @@ -2130,18 +2243,18 @@ public class PackageManagerService extends IPackageManager.Stub /** * Record that the snapshot was used. */ - public void use() { + public final void use() { mUsed++; } /** * Return the usage counter. */ - public int getUsed() { + public final int getUsed() { return mUsed; } - public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, + public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, @PrivateResolveFlags int privateResolveFlags, int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits) { @@ -2240,14 +2353,14 @@ public class PackageManagerService extends IPackageManager.Stub resolveForStart, userId, intent); } - public @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, + public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int userId) { return queryIntentActivitiesInternal( intent, resolvedType, flags, 0 /*privateResolveFlags*/, Binder.getCallingUid(), userId, false /*resolveForStart*/, true /*allowDynamicSplits*/); } - public @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent, + public final @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent, String resolvedType, int flags, int userId, int callingUid, boolean includeInstantApps) { if (!mUserManager.exists(userId)) return Collections.emptyList(); @@ -2471,7 +2584,7 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) { + public final ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) { return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId); } @@ -2481,7 +2594,7 @@ public class PackageManagerService extends IPackageManager.Stub * to clearing. Because it can only be provided by trusted code, its value can be * trusted and will be used as-is; unlike userId which will be validated by this method. */ - public ActivityInfo getActivityInfoInternal(ComponentName component, int flags, + public final ActivityInfo getActivityInfoInternal(ComponentName component, int flags, int filterCallingUid, int userId) { if (!mUserManager.exists(userId)) return null; flags = updateFlagsForComponent(flags, userId); @@ -2535,8 +2648,8 @@ public class PackageManagerService extends IPackageManager.Stub return pkg; } - public ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags, - int filterCallingUid, int userId) { + public final ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, + int flags, int filterCallingUid, int userId) { if (!mUserManager.exists(userId)) return null; PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null) { @@ -2563,7 +2676,7 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) { + public final ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) { return getApplicationInfoInternal(packageName, flags, Binder.getCallingUid(), userId); } @@ -2573,7 +2686,7 @@ public class PackageManagerService extends IPackageManager.Stub * to clearing. Because it can only be provided by trusted code, its value can be * trusted and will be used as-is; unlike userId which will be validated by this method. */ - public ApplicationInfo getApplicationInfoInternal(String packageName, int flags, + public final ApplicationInfo getApplicationInfoInternal(String packageName, int flags, int filterCallingUid, int userId) { if (!mUserManager.exists(userId)) return null; flags = updateFlagsForApplication(flags, userId); @@ -2765,7 +2878,7 @@ public class PackageManagerService extends IPackageManager.Stub * Report the 'Home' activity which is currently set as "always use this one". If non is set * then reports the most likely home activity or null if there are more than one. */ - public ComponentName getDefaultHomeActivity(int userId) { + public final ComponentName getDefaultHomeActivity(int userId) { List<ResolveInfo> allHomeCandidates = new ArrayList<>(); ComponentName cn = getHomeActivitiesAsUser(allHomeCandidates, userId); if (cn != null) { @@ -2793,7 +2906,7 @@ public class PackageManagerService extends IPackageManager.Stub return lastComponent; } - public ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates, + public final ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates, int userId) { Intent intent = getHomeIntent(); List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null, @@ -2822,7 +2935,7 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - public CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent, + public final CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent, String resolvedType, int flags, int sourceUserId, int parentUserId) { if (!mUserManager.hasUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, sourceUserId)) { @@ -2868,15 +2981,15 @@ public class PackageManagerService extends IPackageManager.Stub return result; } - public Intent getHomeIntent() { + public final Intent getHomeIntent() { Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); intent.addCategory(Intent.CATEGORY_DEFAULT); return intent; } - public List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent, - String resolvedType, int userId) { + public final List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters( + Intent intent, String resolvedType, int userId) { CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolver(userId); if (resolver != null) { return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId); @@ -2895,7 +3008,8 @@ public class PackageManagerService extends IPackageManager.Stub * @param intent * @return A filtered list of resolved activities. */ - public List<ResolveInfo> applyPostResolutionFilter(@NonNull List<ResolveInfo> resolveInfos, + public final List<ResolveInfo> applyPostResolutionFilter( + @NonNull List<ResolveInfo> resolveInfos, String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid, boolean resolveForStart, int userId, Intent intent) { final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId); @@ -3041,7 +3155,7 @@ public class PackageManagerService extends IPackageManager.Stub return resolveInfos; } - public List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent, + private List<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPr(Intent intent, int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo, int userId) { final boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0; @@ -3188,7 +3302,7 @@ public class PackageManagerService extends IPackageManager.Stub return result; } - public PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) { + public final PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) { if (!mUserManager.exists(userId)) return null; if (ps == null) { return null; @@ -3258,7 +3372,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - public PackageInfo getPackageInfo(String packageName, int flags, int userId) { + public final PackageInfo getPackageInfo(String packageName, int flags, int userId) { return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST, flags, Binder.getCallingUid(), userId); } @@ -3269,7 +3383,7 @@ public class PackageManagerService extends IPackageManager.Stub * to clearing. Because it can only be provided by trusted code, its value can be * trusted and will be used as-is; unlike userId which will be validated by this method. */ - public PackageInfo getPackageInfoInternal(String packageName, long versionCode, + public final PackageInfo getPackageInfoInternal(String packageName, long versionCode, int flags, int filterCallingUid, int userId) { if (!mUserManager.exists(userId)) return null; flags = updateFlagsForPackage(flags, userId); @@ -3340,7 +3454,7 @@ public class PackageManagerService extends IPackageManager.Stub } @Nullable - public PackageSetting getPackageSetting(String packageName) { + public final PackageSetting getPackageSetting(String packageName) { return getPackageSettingInternal(packageName, Binder.getCallingUid()); } @@ -3350,7 +3464,7 @@ public class PackageManagerService extends IPackageManager.Stub return mSettings.getPackageLPr(packageName); } - public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) { + public final ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) { final int callingUid = Binder.getCallingUid(); if (getInstantAppPackageName(callingUid) != null) { return ParceledListSlice.emptyList(); @@ -3489,7 +3603,7 @@ public class PackageManagerService extends IPackageManager.Stub return new CrossProfileDomainInfo(forwardingInfo, highestApprovalLevel); } - public ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter, + public final ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter, int sourceUserId, int targetUserId) { ResolveInfo forwardingResolveInfo = new ResolveInfo(); final long ident = Binder.clearCallingIdentity(); @@ -3601,7 +3715,7 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) { + public final ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) { if (!mUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId); @@ -3635,7 +3749,7 @@ public class PackageManagerService extends IPackageManager.Stub } @Nullable - public SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) { + public final SharedLibraryInfo getSharedLibraryInfoLPr(String name, long version) { return getSharedLibraryInfo(name, version, mSharedLibraries, null); } @@ -3671,7 +3785,7 @@ public class PackageManagerService extends IPackageManager.Stub return ownerUid; } - public String resolveExternalPackageNameLPr(AndroidPackage pkg) { + public final String resolveExternalPackageNameLPr(AndroidPackage pkg) { if (pkg.getStaticSharedLibName() != null) { return pkg.getManifestPackageName(); } @@ -3745,7 +3859,7 @@ public class PackageManagerService extends IPackageManager.Stub return packageName; } - public String resolveInternalPackageNameLPr(String packageName, long versionCode) { + public final String resolveInternalPackageNameLPr(String packageName, long versionCode) { final int callingUid = Binder.getCallingUid(); return resolveInternalPackageNameInternalLocked(packageName, versionCode, callingUid); @@ -3766,7 +3880,7 @@ public class PackageManagerService extends IPackageManager.Stub * calls to invalidateGetPackagesForUidCache() to locate the points at * which the cache is invalidated. */ - public String[] getPackagesForUid(int uid) { + public final String[] getPackagesForUid(int uid) { return getPackagesForUidInternal(uid, Binder.getCallingUid()); } @@ -3807,7 +3921,7 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - public UserInfo getProfileParent(int userId) { + public final UserInfo getProfileParent(int userId) { final long identity = Binder.clearCallingIdentity(); try { return mUserManager.getProfileParent(userId); @@ -3837,7 +3951,7 @@ public class PackageManagerService extends IPackageManager.Stub * <li>The calling application is the default app prediction service.</li> * </ol> */ - public boolean canViewInstantApps(int callingUid, int userId) { + public final boolean canViewInstantApps(int callingUid, int userId) { if (callingUid < Process.FIRST_APPLICATION_UID) { return true; } @@ -3861,8 +3975,8 @@ public class PackageManagerService extends IPackageManager.Stub return false; } - public boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId, - int flags) { + public final boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, + int userId, int flags) { // Callers can access only the libs they depend on, otherwise they need to explicitly // ask for the shared libraries given the caller is allowed to access all static libs. if ((flags & PackageManager.MATCH_STATIC_SHARED_LIBRARIES) != 0) { @@ -3945,13 +4059,13 @@ public class PackageManagerService extends IPackageManager.Stub == PackageManager.PERMISSION_GRANTED; } - public boolean isCallerSameApp(String packageName, int uid) { + public final boolean isCallerSameApp(String packageName, int uid) { AndroidPackage pkg = mPackages.get(packageName); return pkg != null && UserHandle.getAppId(uid) == pkg.getUid(); } - public boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) { + public final boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) { if (isComponentVisibleToInstantApp(component, TYPE_ACTIVITY)) { return true; } @@ -3964,7 +4078,7 @@ public class PackageManagerService extends IPackageManager.Stub return false; } - public boolean isComponentVisibleToInstantApp( + public final boolean isComponentVisibleToInstantApp( @Nullable ComponentName component, @ComponentType int type) { if (type == TYPE_ACTIVITY) { final ParsedActivity activity = mComponentResolver.getActivity(component); @@ -4012,13 +4126,13 @@ public class PackageManagerService extends IPackageManager.Stub * @return {@code true} if the intent is a camera intent and the persistent preferred * activity was not set by the DPC. */ - public boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, int userId, - String resolvedType, int flags) { + public final boolean isImplicitImageCaptureIntentAndNotSetByDpcLocked(Intent intent, + int userId, String resolvedType, int flags) { return intent.isImplicitImageCaptureIntent() && !isPersistentPreferredActivitySetByDpm( intent, userId, resolvedType, flags); } - public boolean isInstantApp(String packageName, int userId) { + public final boolean isInstantApp(String packageName, int userId) { final int callingUid = Binder.getCallingUid(); enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "isInstantApp"); @@ -4026,7 +4140,7 @@ public class PackageManagerService extends IPackageManager.Stub return isInstantAppInternal(packageName, userId, callingUid); } - public boolean isInstantAppInternal(String packageName, @UserIdInt int userId, + public final boolean isInstantAppInternal(String packageName, @UserIdInt int userId, int callingUid) { if (HIDE_EPHEMERAL_APIS) { return false; @@ -4160,7 +4274,8 @@ public class PackageManagerService extends IPackageManager.Stub } } - public boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) { + public final boolean isSameProfileGroup(@UserIdInt int callerUserId, + @UserIdInt int userId) { final long identity = Binder.clearCallingIdentity(); try { return UserManagerService.getInstance().isSameProfileGroup(callerUserId, userId); @@ -4187,7 +4302,8 @@ public class PackageManagerService extends IPackageManager.Stub * * @see #canViewInstantApps(int, int) */ - public boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid, + public final boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, + int callingUid, @Nullable ComponentName component, @ComponentType int componentType, int userId) { // if we're in an isolated process, get the real calling UID if (Process.isIsolated(callingUid)) { @@ -4247,7 +4363,7 @@ public class PackageManagerService extends IPackageManager.Stub /** * @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int) */ - public boolean shouldFilterApplicationLocked( + public final boolean shouldFilterApplicationLocked( @Nullable PackageSetting ps, int callingUid, int userId) { return shouldFilterApplicationLocked(ps, callingUid, null, TYPE_UNKNOWN, userId); } @@ -4255,8 +4371,8 @@ public class PackageManagerService extends IPackageManager.Stub /** * @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int) */ - public boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus, int callingUid, - int userId) { + public final boolean shouldFilterApplicationLocked(@NonNull SharedUserSetting sus, + int callingUid, int userId) { boolean filterApp = true; for (int index = sus.packages.size() - 1; index >= 0 && filterApp; index--) { filterApp &= shouldFilterApplicationLocked(sus.packages.valueAt(index), @@ -4280,7 +4396,7 @@ public class PackageManagerService extends IPackageManager.Stub } // NOTE: Can't remove without a major refactor. Keep around for now. - public int checkUidPermission(String permName, int uid) { + public final int checkUidPermission(String permName, int uid) { return mPermissionManager.checkUidPermission(uid, permName); } @@ -4330,21 +4446,21 @@ public class PackageManagerService extends IPackageManager.Stub /** * Update given flags when being used to request {@link ApplicationInfo}. */ - public int updateFlagsForApplication(int flags, int userId) { + public final int updateFlagsForApplication(int flags, int userId) { return updateFlagsForPackage(flags, userId); } /** * Update given flags when being used to request {@link ComponentInfo}. */ - public int updateFlagsForComponent(int flags, int userId) { + public final int updateFlagsForComponent(int flags, int userId) { return updateFlags(flags, userId); } /** * Update given flags when being used to request {@link PackageInfo}. */ - public int updateFlagsForPackage(int flags, int userId) { + public final int updateFlagsForPackage(int flags, int userId) { final boolean isCallerSystemUser = UserHandle.getCallingUserId() == UserHandle.USER_SYSTEM; if ((flags & PackageManager.MATCH_ANY_USER) != 0) { @@ -4380,14 +4496,14 @@ public class PackageManagerService extends IPackageManager.Stub * action and a {@code android.intent.category.BROWSABLE} category</li> * </ul> */ - public int updateFlagsForResolve(int flags, int userId, int callingUid, + public final int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps, boolean isImplicitImageCaptureIntentAndNotSetByDpc) { return updateFlagsForResolve(flags, userId, callingUid, wantInstantApps, false /*onlyExposedExplicitly*/, isImplicitImageCaptureIntentAndNotSetByDpc); } - public int updateFlagsForResolve(int flags, int userId, int callingUid, + public final int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps, boolean onlyExposedExplicitly, boolean isImplicitImageCaptureIntentAndNotSetByDpc) { // Safe mode means we shouldn't match any third-party components @@ -4429,7 +4545,7 @@ public class PackageManagerService extends IPackageManager.Stub * @param checkShell whether to prevent shell from access if there's a debugging restriction * @param message the message to log on security exception */ - public void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId, + public final void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId, boolean requireFullPermission, boolean checkShell, String message) { if (userId < 0) { throw new IllegalArgumentException("Invalid userId " + userId); @@ -4467,7 +4583,7 @@ public class PackageManagerService extends IPackageManager.Stub * @param checkShell whether to prevent shell from access if there's a debugging restriction * @param message the message to log on security exception */ - public void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, + public final void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, boolean requireFullPermission, boolean checkShell, String message) { enforceCrossUserPermission(callingUid, userId, requireFullPermission, checkShell, false, message); @@ -4484,7 +4600,7 @@ public class PackageManagerService extends IPackageManager.Stub * reference the same user. * @param message the message to log on security exception */ - public void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, + public final void enforceCrossUserPermission(int callingUid, @UserIdInt int userId, boolean requireFullPermission, boolean checkShell, boolean requirePermissionWhenSameUser, String message) { if (userId < 0) { @@ -4736,31 +4852,14 @@ public class PackageManagerService extends IPackageManager.Stub } /** - * The live computer differs from the ComputerEngine in the methods that fetch data - * from PackageManagerService. - **/ - private static class ComputerEngineLive extends ComputerEngine { - ComputerEngineLive(Snapshot args) { - super(args); - } - protected ComponentName resolveComponentName() { - return mService.mResolveComponentName; - } - protected ActivityInfo instantAppInstallerActivity() { - return mService.mInstantAppInstallerActivity; - } - protected ApplicationInfo androidApplication() { - return mService.mAndroidApplication; - } - } - - /** - * This subclass is the external interface to the live computer. For each - * interface, it takes the PM lock and then delegates to the live - * computer engine. This is required because there are no locks taken in - * the engine itself. + * This subclass is the external interface to the live computer. Some internal helper + * methods are overridden to fetch live data instead of snapshot data. For each + * Computer interface that is overridden in this class, the override takes the PM lock + * and then delegates to the live computer engine. This is required because there are + * no locks taken in the engine itself. */ - private static class ComputerLocked extends ComputerEngineLive { + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + protected static class ComputerLocked extends ComputerEngine { private final Object mLock; ComputerLocked(Snapshot args) { @@ -4768,18 +4867,17 @@ public class PackageManagerService extends IPackageManager.Stub mLock = mService.mLock; } - /** - * Explicilty snapshot {@link Settings#mPackages} for cases where the caller must not lock - * in order to get package data. It is expected that the caller locks itself to be able - * to block on changes to the package data and bring itself up to date once the change - * propagates to it. Use with heavy caution. - * @return - */ - private Map<String, PackageSetting> snapshotPackageSettings() { - return mSettings.snapshot().mPackages; + protected final ComponentName resolveComponentName() { + return mService.mResolveComponentName; + } + protected final ActivityInfo instantAppInstallerActivity() { + return mService.mInstantAppInstallerActivity; + } + protected final ApplicationInfo androidApplication() { + return mService.mAndroidApplication; } - public @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent, + public final @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent, String resolvedType, int flags, int userId, int callingUid, String instantAppPkgName) { synchronized (mLock) { @@ -4787,8 +4885,8 @@ public class PackageManagerService extends IPackageManager.Stub callingUid, instantAppPkgName); } } - public @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(Intent intent, - String resolvedType, int flags, int filterCallingUid, int userId, + public final @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody( + Intent intent, String resolvedType, int flags, int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits, String pkgName, String instantAppPkgName) { synchronized (mLock) { @@ -4797,31 +4895,31 @@ public class PackageManagerService extends IPackageManager.Stub instantAppPkgName); } } - public ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags, + public final ActivityInfo getActivityInfoInternalBody(ComponentName component, int flags, int filterCallingUid, int userId) { synchronized (mLock) { return super.getActivityInfoInternalBody(component, flags, filterCallingUid, userId); } } - public AndroidPackage getPackage(String packageName) { + public final AndroidPackage getPackage(String packageName) { synchronized (mLock) { return super.getPackage(packageName); } } - public AndroidPackage getPackage(int uid) { + public final AndroidPackage getPackage(int uid) { synchronized (mLock) { return super.getPackage(uid); } } - public ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags, + public final ApplicationInfo getApplicationInfoInternalBody(String packageName, int flags, int filterCallingUid, int userId) { synchronized (mLock) { return super.getApplicationInfoInternalBody(packageName, flags, filterCallingUid, userId); } } - public ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody( + public final ArrayList<ResolveInfo> filterCandidatesWithDomainPreferredActivitiesLPrBody( Intent intent, int matchFlags, List<ResolveInfo> candidates, CrossProfileDomainInfo xpDomainInfo, int userId, boolean debug) { synchronized (mLock) { @@ -4829,49 +4927,49 @@ public class PackageManagerService extends IPackageManager.Stub matchFlags, candidates, xpDomainInfo, userId, debug); } } - public PackageInfo getPackageInfoInternalBody(String packageName, long versionCode, + public final PackageInfo getPackageInfoInternalBody(String packageName, long versionCode, int flags, int filterCallingUid, int userId) { synchronized (mLock) { return super.getPackageInfoInternalBody(packageName, versionCode, flags, filterCallingUid, userId); } } - public PackageSetting getPackageSettingInternal(String packageName, int callingUid) { + public final PackageSetting getPackageSettingInternal(String packageName, int callingUid) { synchronized (mLock) { return super.getPackageSettingInternal(packageName, callingUid); } } - public ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId, + public final ParceledListSlice<PackageInfo> getInstalledPackagesBody(int flags, int userId, int callingUid) { synchronized (mLock) { return super.getInstalledPackagesBody(flags, userId, callingUid); } } - public ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId, + public final ServiceInfo getServiceInfoBody(ComponentName component, int flags, int userId, int callingUid) { synchronized (mLock) { return super.getServiceInfoBody(component, flags, userId, callingUid); } } - public String getInstantAppPackageName(int callingUid) { + public final String getInstantAppPackageName(int callingUid) { synchronized (mLock) { return super.getInstantAppPackageName(callingUid); } } - public String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId, + public final String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId, boolean isCallerInstantApp) { synchronized (mLock) { return super.getPackagesForUidInternalBody(callingUid, userId, appId, isCallerInstantApp); } } - public boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId, + public final boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId, int callingUid) { synchronized (mLock) { return super.isInstantAppInternalBody(packageName, userId, callingUid); } } - public boolean isInstantAppResolutionAllowedBody(Intent intent, + public final boolean isInstantAppResolutionAllowedBody(Intent intent, List<ResolveInfo> resolvedActivities, int userId, boolean skipPackageCheck, int flags) { synchronized (mLock) { @@ -4879,33 +4977,33 @@ public class PackageManagerService extends IPackageManager.Stub skipPackageCheck, flags); } } - public int getPackageUidInternal(String packageName, int flags, int userId, + public final int getPackageUidInternal(String packageName, int flags, int userId, int callingUid) { synchronized (mLock) { return super.getPackageUidInternal(packageName, flags, userId, callingUid); } } - public SigningDetails getSigningDetails(@NonNull String packageName) { + public final SigningDetails getSigningDetails(@NonNull String packageName) { synchronized (mLock) { return super.getSigningDetails(packageName); } } - public SigningDetails getSigningDetails(int uid) { + public final SigningDetails getSigningDetails(int uid) { synchronized (mLock) { return super.getSigningDetails(uid); } } - public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) { + public final boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) { synchronized (mLock) { return super.filterAppAccess(pkg, callingUid, userId); } } - public boolean filterAppAccess(String packageName, int callingUid, int userId) { + public final boolean filterAppAccess(String packageName, int callingUid, int userId) { synchronized (mLock) { return super.filterAppAccess(packageName, callingUid, userId); } } - public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) { + public final void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) { synchronized (mLock) { super.dump(type, fd, pw, dumpState); } @@ -11554,8 +11652,31 @@ public class PackageManagerService extends IPackageManager.Stub if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return; } + final List<String> names = new ArrayList<>(); + final List<ProviderInfo> infos = new ArrayList<>(); + final int callingUserId = UserHandle.getCallingUserId(); mComponentResolver.querySyncProviders( - outNames, outInfo, mSafeMode, UserHandle.getCallingUserId()); + names, infos, mSafeMode, callingUserId); + synchronized (mLock) { + for (int i = infos.size() - 1; i >= 0; i--) { + final ProviderInfo providerInfo = infos.get(i); + final PackageSetting ps = mSettings.getPackageLPr(providerInfo.packageName); + final ComponentName component = + new ComponentName(providerInfo.packageName, providerInfo.name); + if (!shouldFilterApplicationLocked(ps, Binder.getCallingUid(), component, + TYPE_PROVIDER, callingUserId)) { + continue; + } + infos.remove(i); + names.remove(i); + } + } + if (!names.isEmpty()) { + outNames.addAll(names); + } + if (!infos.isEmpty()) { + outInfo.addAll(infos); + } } @Override @@ -12677,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; @@ -12988,6 +13094,15 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void dumpProfiles(String packageName) { + /* Only the shell, root, or the app user should be able to dump profiles. */ + final int callingUid = Binder.getCallingUid(); + final String[] callerPackageNames = getPackagesForUid(callingUid); + if (callingUid != Process.SHELL_UID + && callingUid != Process.ROOT_UID + && !ArrayUtils.contains(callerPackageNames, packageName)) { + throw new SecurityException("dumpProfiles"); + } + AndroidPackage pkg; synchronized (mLock) { pkg = mPackages.get(packageName); @@ -12995,13 +13110,6 @@ public class PackageManagerService extends IPackageManager.Stub throw new IllegalArgumentException("Unknown package: " + packageName); } } - /* Only the shell, root, or the app user should be able to dump profiles. */ - int callingUid = Binder.getCallingUid(); - if (callingUid != Process.SHELL_UID && - callingUid != Process.ROOT_UID && - callingUid != pkg.getUid()) { - throw new SecurityException("dumpProfiles"); - } synchronized (mInstallLock) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles"); @@ -17127,10 +17235,7 @@ public class PackageManagerService extends IPackageManager.Stub callerPackageName); synchronized (mLock) { PackageSetting ps = mSettings.getPackageLPr(packageName); - if (ps == null) { - throw new IllegalArgumentException("Unknown target package " + packageName); - } - if (shouldFilterApplicationLocked( + if (ps == null || shouldFilterApplicationLocked( ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) { throw new IllegalArgumentException("Unknown target package " + packageName); } @@ -21375,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); @@ -21419,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) { @@ -21497,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; @@ -23191,16 +23318,17 @@ public class PackageManagerService extends IPackageManager.Stub if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) { return; } + final String[] callerPackageNames = getPackagesForUid(callingUid); + if (!ArrayUtils.contains(callerPackageNames, pkg)) { + throw new SecurityException("Calling uid " + callingUid + + " does not own package " + pkg); + } final int callingUserId = UserHandle.getUserId(callingUid); PackageInfo pi = getPackageInfo(pkg, 0, callingUserId); if (pi == null) { throw new IllegalArgumentException("Unknown package " + pkg + " on user " + callingUserId); } - if (!UserHandle.isSameApp(pi.applicationInfo.uid, callingUid)) { - throw new SecurityException("Calling uid " + callingUid - + " does not own package " + pkg); - } } @Override @@ -24016,6 +24144,13 @@ public class PackageManagerService extends IPackageManager.Stub final int permission = mContext.checkCallingOrSelfPermission( android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE); final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED); + if (!allowedByPermission + && !ArrayUtils.contains(getPackagesForUid(callingUid), packageName)) { + throw new SecurityException( + "Permission Denial: attempt to change stopped state from pid=" + + Binder.getCallingPid() + + ", uid=" + callingUid + ", package=" + packageName); + } enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, true /* checkShell */, "stop package"); boolean shouldUnhibernate = false; @@ -24026,8 +24161,7 @@ public class PackageManagerService extends IPackageManager.Stub shouldUnhibernate = true; } if (!shouldFilterApplicationLocked(ps, callingUid, userId) - && mSettings.setPackageStoppedStateLPw(this, packageName, stopped, - allowedByPermission, callingUid, userId)) { + && mSettings.setPackageStoppedStateLPw(this, packageName, stopped, userId)) { scheduleWritePackageRestrictionsLocked(userId); } } @@ -26461,16 +26595,12 @@ public class PackageManagerService extends IPackageManager.Stub } synchronized(mLock) { final AndroidPackage pkg = mPackages.get(packageName); - if (pkg == null) { + if (pkg == null + || shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()), + Binder.getCallingUid(), UserHandle.getCallingUserId())) { Slog.w(TAG, "KeySet requested for unknown package: " + packageName); throw new IllegalArgumentException("Unknown package: " + packageName); } - final PackageSetting ps = getPackageSetting(pkg.getPackageName()); - if (shouldFilterApplicationLocked( - ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) { - Slog.w(TAG, "KeySet requested for filtered package: " + packageName); - throw new IllegalArgumentException("Unknown package: " + packageName); - } final KeySetManagerService ksms = mSettings.getKeySetManagerService(); return new KeySet(ksms.getKeySetByAliasAndPackageNameLPr(packageName, alias)); } @@ -26485,14 +26615,10 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final AndroidPackage pkg = mPackages.get(packageName); - if (pkg == null) { - Slog.w(TAG, "KeySet requested for unknown package: " + packageName); - throw new IllegalArgumentException("Unknown package: " + packageName); - } - final PackageSetting ps = getPackageSetting(pkg.getPackageName()); - if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) { - // filter and pretend the package doesn't exist - Slog.w(TAG, "KeySet requested for filtered package: " + packageName + if (pkg == null + || shouldFilterApplicationLocked(getPackageSetting(pkg.getPackageName()), + callingUid, callingUserId)) { + Slog.w(TAG, "KeySet requested for unknown package: " + packageName + ", uid:" + callingUid); throw new IllegalArgumentException("Unknown package: " + packageName); } @@ -27932,7 +28058,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public List<String> getMimeGroup(String packageName, String mimeGroup) { - return PackageManagerService.this.getMimeGroup(packageName, mimeGroup); + return PackageManagerService.this.getMimeGroupInternal(packageName, mimeGroup); } @Override @@ -28022,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 @@ -28558,9 +28684,11 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void setMimeGroup(String packageName, String mimeGroup, List<String> mimeTypes) { - boolean changed = mSettings.getPackageLPr(packageName) - .setMimeGroup(mimeGroup, mimeTypes); - + enforceOwnerRights(packageName, Binder.getCallingUid()); + final boolean changed; + synchronized (mLock) { + changed = mSettings.getPackageLPr(packageName).setMimeGroup(mimeGroup, mimeTypes); + } if (changed) { applyMimeGroupChanges(packageName, mimeGroup); } @@ -28568,7 +28696,14 @@ public class PackageManagerService extends IPackageManager.Stub @Override public List<String> getMimeGroup(String packageName, String mimeGroup) { - return mSettings.getPackageLPr(packageName).getMimeGroup(mimeGroup); + enforceOwnerRights(packageName, Binder.getCallingUid()); + return getMimeGroupInternal(packageName, mimeGroup); + } + + private List<String> getMimeGroupInternal(String packageName, String mimeGroup) { + synchronized (mLock) { + return mSettings.getPackageLPr(packageName).getMimeGroup(mimeGroup); + } } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 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/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 7aa1c3ab8154..26aebbc1ea93 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4188,18 +4188,11 @@ public final class Settings implements Watchable, Snappable { } boolean setPackageStoppedStateLPw(PackageManagerService pm, String packageName, - boolean stopped, boolean allowedByPermission, int uid, int userId) { - int appId = UserHandle.getAppId(uid); + boolean stopped, int userId) { final PackageSetting pkgSetting = mPackages.get(packageName); if (pkgSetting == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } - if (!allowedByPermission && (appId != pkgSetting.appId)) { - throw new SecurityException( - "Permission Denial: attempt to change stopped state from pid=" - + Binder.getCallingPid() - + ", uid=" + uid + ", package uid=" + pkgSetting.appId); - } if (DEBUG_STOPPED) { if (stopped) { RuntimeException e = new RuntimeException("here"); diff --git a/services/core/java/com/android/server/pm/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/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING index 47cce9f9e019..878eb920717e 100644 --- a/services/core/java/com/android/server/pm/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/TEST_MAPPING @@ -13,6 +13,9 @@ "name": "CtsAppEnumerationTestCases" }, { + "name": "AppEnumerationInternalTests" + }, + { "name": "CtsMatchFlagTestCases" }, { 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/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java index 5b48abb3e1f2..a5969a88008d 100644 --- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java +++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java @@ -135,7 +135,10 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(TelephonyManager.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); - context.registerReceiver(mBroadcastReceiver, filter); + // By default CLOSE_SYSTEM_DIALOGS broadcast is sent only for current user, which is user + // 10 on devices with headless system user enabled. + // In order to receive the broadcast, register the broadcast receiver with UserHandle.ALL. + context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); mHasTelephony = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY); diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java index 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/tracing/TracingServiceProxy.java b/services/core/java/com/android/server/tracing/TracingServiceProxy.java index 8f227489740f..ff2f08bc4a50 100644 --- a/services/core/java/com/android/server/tracing/TracingServiceProxy.java +++ b/services/core/java/com/android/server/tracing/TracingServiceProxy.java @@ -20,6 +20,7 @@ import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Binder; import android.os.UserHandle; import android.tracing.ITracingServiceProxy; import android.util.Log; @@ -30,6 +31,8 @@ import com.android.server.SystemService; * TracingServiceProxy is the system_server intermediary between the Perfetto tracing daemon and the * system tracing app Traceur. * + * Access to this service is restricted via SELinux. Normal apps do not have access. + * * @hide */ public class TracingServiceProxy extends SystemService { @@ -87,11 +90,15 @@ public class TracingServiceProxy extends SystemService { intent.setAction(INTENT_ACTION_NOTIFY_SESSION_STOPPED); } + final long identity = Binder.clearCallingIdentity(); try { mContext.startForegroundServiceAsUser(intent, UserHandle.SYSTEM); } catch (RuntimeException e) { Log.e(TAG, "Failed to notifyTraceSessionEnded", e); + } finally { + Binder.restoreCallingIdentity(identity); } + } catch (NameNotFoundException e) { Log.e(TAG, "Failed to locate Traceur", e); } diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java index 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 4a07c1ac1e39..885f0e4c78ab 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -19,7 +19,10 @@ package com.android.server.vibrator; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.IUidObserver; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.res.Resources; import android.database.ContentObserver; import android.media.AudioManager; @@ -61,6 +64,9 @@ final class VibrationSettings { private final SettingsObserver mSettingObserver; @VisibleForTesting final UidObserver mUidObserver; + @VisibleForTesting + final UserObserver mUserReceiver; + @GuardedBy("mLock") private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>(); @@ -94,6 +100,7 @@ final class VibrationSettings { mContext = context; mSettingObserver = new SettingsObserver(handler); mUidObserver = new UidObserver(); + mUserReceiver = new UserObserver(); VibrationEffect clickEffect = createEffectFromResource( com.android.internal.R.array.config_virtualKeyVibePattern); @@ -150,6 +157,7 @@ final class VibrationSettings { } }); + mContext.registerReceiver(mUserReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED)); registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES)); registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING)); registerSettingsObserver(Settings.Global.getUriFor(Settings.Global.APPLY_RAMPING_RINGER)); @@ -345,6 +353,7 @@ final class VibrationSettings { + ", mLowPowerMode=" + mLowPowerMode + ", mZenMode=" + Settings.Global.zenModeToString(mZenMode) + ", mProcStatesCache=" + mUidObserver.mProcStatesCache + + ", mHapticChannelMaxVibrationAmplitude=" + getHapticChannelMaxVibrationAmplitude() + ", mHapticFeedbackIntensity=" + intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_TOUCH)) + ", mHapticFeedbackDefaultIntensity=" @@ -403,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); @@ -457,6 +472,17 @@ final class VibrationSettings { } } + /** Implementation of {@link BroadcastReceiver} to update settings on current user change. */ + @VisibleForTesting + final class UserObserver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) { + updateSettings(); + } + } + } + /** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */ @VisibleForTesting final class UidObserver extends IUidObserver.Stub { diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java index 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..771332071756 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; @@ -403,20 +405,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub needsExtraction = wallpaper.primaryColors == null; } - // Let's notify the current values, it's fine if it's null, it just means - // that we don't know yet. - notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId); - if (needsExtraction) { extractColors(wallpaper); - synchronized (mLock) { - // Don't need to notify if nothing changed. - if (wallpaper.primaryColors == null) { - return; - } - } - notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId); } + notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId); } private static <T extends IInterface> boolean emptyCallbackList(RemoteCallbackList<T> list) { @@ -3140,7 +3132,9 @@ 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 +3173,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 +3417,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 +3445,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/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index ac0665a37c0b..a24319f7a98c 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -1542,6 +1542,52 @@ final class AccessibilityController { mEmbeddedDisplayIdList.add(displayId); } + boolean shellRootIsAbove(WindowState windowState, ShellRoot shellRoot) { + int wsLayer = mService.mPolicy.getWindowLayerLw(windowState); + int shellLayer = mService.mPolicy.getWindowLayerFromTypeLw(shellRoot.getWindowType(), + true); + return shellLayer >= wsLayer; + } + + int addShellRootsIfAbove(WindowState windowState, ArrayList<ShellRoot> shellRoots, + int shellRootIndex, List<WindowInfo> windows, Set<IBinder> addedWindows, + Region unaccountedSpace, boolean focusedWindowAdded) { + while (shellRootIndex < shellRoots.size() + && shellRootIsAbove(windowState, shellRoots.get(shellRootIndex))) { + ShellRoot shellRoot = shellRoots.get(shellRootIndex); + shellRootIndex++; + final WindowInfo info = shellRoot.getWindowInfo(); + if (info == null) { + continue; + } + + info.layer = addedWindows.size(); + windows.add(info); + addedWindows.add(info.token); + unaccountedSpace.op(info.regionInScreen, unaccountedSpace, + Region.Op.REVERSE_DIFFERENCE); + if (unaccountedSpace.isEmpty() && focusedWindowAdded) { + break; + } + } + return shellRootIndex; + } + + private ArrayList<ShellRoot> getSortedShellRoots( + SparseArray<ShellRoot> originalShellRoots) { + ArrayList<ShellRoot> sortedShellRoots = new ArrayList<>(originalShellRoots.size()); + for (int i = originalShellRoots.size() - 1; i >= 0; --i) { + sortedShellRoots.add(originalShellRoots.valueAt(i)); + } + + sortedShellRoots.sort((left, right) -> + mService.mPolicy.getWindowLayerFromTypeLw(right.getWindowType(), true) + - mService.mPolicy.getWindowLayerFromTypeLw(left.getWindowType(), + true)); + + return sortedShellRoots; + } + /** * Check if windows have changed, and send them to the accessibility subsystem if they have. * @@ -1601,9 +1647,22 @@ final class AccessibilityController { final int visibleWindowCount = visibleWindows.size(); HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>(); + ArrayList<ShellRoot> shellRoots = getSortedShellRoots(dc.mShellRoots); + // Iterate until we figure out what is touchable for the entire screen. + int shellRootIndex = 0; for (int i = visibleWindowCount - 1; i >= 0; i--) { final WindowState windowState = visibleWindows.valueAt(i); + int prevShellRootIndex = shellRootIndex; + shellRootIndex = addShellRootsIfAbove(windowState, shellRoots, shellRootIndex, + windows, addedWindows, unaccountedSpace, focusedWindowAdded); + + // If a Shell Root was added, it could have accounted for all the space already. + if (shellRootIndex > prevShellRootIndex && unaccountedSpace.isEmpty() + && focusedWindowAdded) { + break; + } + final Region regionInScreen = new Region(); computeWindowRegionInScreen(windowState, regionInScreen); @@ -1627,16 +1686,6 @@ final class AccessibilityController { } } - for (int i = dc.mShellRoots.size() - 1; i >= 0; --i) { - final WindowInfo info = dc.mShellRoots.valueAt(i).getWindowInfo(); - if (info == null) { - continue; - } - info.layer = addedWindows.size(); - windows.add(info); - addedWindows.add(info.token); - } - // Remove child/parent references to windows that were not added. final int windowCount = windows.size(); for (int i = 0; i < windowCount; i++) { diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 14f6fb3ed99a..f6217bc2ffd2 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -206,8 +206,8 @@ class ActivityMetricsLogger { * observer to identify which callbacks belong to a launch event. */ final long mTransitionStartTimeNs; - /** The device uptime in seconds when this transition info is created. */ - final int mCurrentTransitionDeviceUptime; + /** The device uptime in millis when this transition info is created. */ + final long mTransitionDeviceUptimeMs; /** The type can be cold (new process), warm (new activity), or hot (bring to front). */ final int mTransitionType; /** Whether the process was already running when the transition started. */ @@ -277,8 +277,7 @@ class ActivityMetricsLogger { mTransitionType = transitionType; mProcessRunning = processRunning; mProcessSwitch = processSwitch; - mCurrentTransitionDeviceUptime = - (int) TimeUnit.MILLISECONDS.toSeconds(launchingState.mCurrentUpTimeMs); + mTransitionDeviceUptimeMs = launchingState.mCurrentUpTimeMs; setLatestLaunchedActivity(r); launchingState.mAssociatedTransitionInfo = this; if (options != null) { @@ -908,7 +907,7 @@ class ActivityMetricsLogger { final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info); if (info.isInterestingToLoggerAndObserver()) { mLoggerHandler.post(() -> logAppTransition( - info.mCurrentTransitionDeviceUptime, info.mCurrentTransitionDelayMs, + info.mTransitionDeviceUptimeMs, info.mCurrentTransitionDelayMs, infoSnapshot, isHibernating)); } mLoggerHandler.post(() -> logAppDisplayed(infoSnapshot)); @@ -920,7 +919,7 @@ class ActivityMetricsLogger { } // This gets called on another thread without holding the activity manager lock. - private void logAppTransition(int currentTransitionDeviceUptime, int currentTransitionDelayMs, + private void logAppTransition(long transitionDeviceUptimeMs, int currentTransitionDelayMs, TransitionInfoSnapshot info, boolean isHibernating) { final LogMaker builder = new LogMaker(APP_TRANSITION); builder.setPackageName(info.packageName); @@ -937,7 +936,7 @@ class ActivityMetricsLogger { } builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL, isInstantApp ? 1 : 0); builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS, - currentTransitionDeviceUptime); + TimeUnit.MILLISECONDS.toSeconds(transitionDeviceUptimeMs)); builder.addTaggedData(APP_TRANSITION_DELAY_MS, currentTransitionDelayMs); builder.setSubtype(info.reason); if (info.startingWindowDelayMs != INVALID_DELAY) { @@ -972,7 +971,7 @@ class ActivityMetricsLogger { info.launchedActivityName, info.launchedActivityLaunchedFromPackage, isInstantApp, - currentTransitionDeviceUptime * 1000, + transitionDeviceUptimeMs, info.reason, currentTransitionDelayMs, info.startingWindowDelayMs, diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 542ec809c484..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; @@ -647,6 +650,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private boolean mLastContainsDismissKeyguardWindow; private boolean mLastContainsTurnScreenOnWindow; + /** Whether the IME is showing when transitioning away from this activity. */ + boolean mLastImeShown; + /** * A flag to determine if this AR is in the process of closing or entering PIP. This is needed * to help AR know that the app is in the process of closing but hasn't yet started closing on @@ -674,8 +680,6 @@ 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 @@ -1221,6 +1225,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A TopResumedActivityChangeItem.obtain(onTop)); } catch (RemoteException e) { // If process died, whatever. + Slog.w(TAG, "Failed to send top-resumed=" + onTop + " to " + this, e); return false; } return true; @@ -1784,17 +1789,19 @@ 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; } @@ -1831,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. @@ -1868,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; } @@ -4721,6 +4732,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A setClientVisible(visible); } + if (!visible) { + final InsetsControlTarget imeInputTarget = mDisplayContent.getImeTarget( + DisplayContent.IME_TARGET_INPUT); + mLastImeShown = imeInputTarget != null && imeInputTarget.getWindow() != null + && imeInputTarget.getWindow().mActivityRecord == this + && mDisplayContent.mInputMethodWindow != null + && mDisplayContent.mInputMethodWindow.isVisible(); + } + final DisplayContent displayContent = getDisplayContent(); if (!displayContent.mClosingApps.contains(this) && !displayContent.mOpeningApps.contains(this)) { @@ -4860,6 +4880,12 @@ 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); @@ -5764,10 +5790,24 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void onStartingWindowDrawn() { + boolean wasTaskVisible = false; if (task != null) { mSplashScreenStyleEmpty = true; + wasTaskVisible = task.getHasBeenVisible(); task.setHasBeenVisible(true); } + + // The transition may not be executed if the starting process hasn't attached. But if the + // starting window is drawn, the transition can start earlier. Exclude finishing and bubble + // because it may be a trampoline. + if (!wasTaskVisible && mStartingData != null && !finishing && !mLaunchedFromBubble + && !mDisplayContent.mAppTransition.isReady() + && !mDisplayContent.mAppTransition.isRunning()) { + // The pending transition state will be cleared after the transition is started, so + // save the state for launching the client later (used by LaunchActivityItem). + mStartingData.mIsTransitionForward = mDisplayContent.isNextTransitionForward(); + mDisplayContent.executeAppTransition(); + } } /** Called when the windows associated app window container are drawn. */ @@ -6251,15 +6291,21 @@ 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; } @@ -6480,6 +6526,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A || dc.mChangingContainers.contains(this)); } + boolean isTransitionForward() { + return (mStartingData != null && mStartingData.mIsTransitionForward) + || mDisplayContent.isNextTransitionForward(); + } + private int getAnimationLayer() { // The leash is parented to the animation layer. We need to preserve the z-order by using // the prefix order index, but we boost if necessary. @@ -7234,41 +7285,48 @@ final 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) { @@ -7310,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 @@ -7372,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 @@ -7445,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) { @@ -7681,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. @@ -7689,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; } @@ -7707,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: @@ -7748,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); } } @@ -7774,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; } @@ -8081,8 +8156,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A preserveWindow); final ActivityLifecycleItem lifecycleItem; if (andResume) { - lifecycleItem = ResumeActivityItem.obtain( - mDisplayContent.isNextTransitionForward()); + lifecycleItem = ResumeActivityItem.obtain(isTransitionForward()); } else { lifecycleItem = PauseActivityItem.obtain(); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 7fe0f5be287c..d3d1c1ca6a2b 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -836,7 +836,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { final ClientTransaction clientTransaction = ClientTransaction.obtain( proc.getThread(), r.appToken); - final DisplayContent dc = r.mDisplayContent; + final boolean isTransitionForward = r.isTransitionForward(); clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent), System.identityHashCode(r), r.info, // TODO: Have this take the merged configuration instead of separate global @@ -845,7 +845,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { mergedConfiguration.getOverrideConfiguration(), r.compat, r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(), r.getSavedState(), r.getPersistentSavedState(), results, newIntents, - r.takeOptions(), dc.isNextTransitionForward(), + r.takeOptions(), isTransitionForward, proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController, r.createFixedRotationAdjustmentsIfNeeded(), r.shareableActivityToken, r.getLaunchedFromBubble())); @@ -853,7 +853,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // Set desired final state. final ActivityLifecycleItem lifecycleItem; if (andResume) { - lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward()); + lifecycleItem = ResumeActivityItem.obtain(isTransitionForward); } else { lifecycleItem = PauseActivityItem.obtain(); } diff --git a/services/core/java/com/android/server/wm/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/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index e0bae9d8de32..3dbc517af45c 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -63,6 +63,8 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS; @@ -208,6 +210,7 @@ import android.view.Surface.Rotation; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.SurfaceSession; +import android.view.View; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowManager.DisplayImePolicy; @@ -1549,9 +1552,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // to cover the activity configuration change. return false; } - if ((r.mStartingData != null && r.mStartingData.hasImeSurface()) - || (mInsetsStateController.getImeSourceProvider() - .getSource().getVisibleFrame() != null)) { + if (r.attachedToProcess() && mayImeShowOnLaunchingActivity(r)) { // Currently it is unknown that when will IME window be ready. Reject the case to // avoid flickering by showing IME in inconsistent orientation. return false; @@ -1607,6 +1608,24 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return true; } + /** Returns {@code true} if the IME is possible to show on the launching activity. */ + private boolean mayImeShowOnLaunchingActivity(@NonNull ActivityRecord r) { + final WindowState win = r.findMainWindow(); + if (win == null) { + return false; + } + // See InputMethodManagerService#shouldRestoreImeVisibility that we expecting the IME + // should be hidden when the window set the hidden softInputMode. + final int softInputMode = win.mAttrs.softInputMode; + switch (softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) { + case SOFT_INPUT_STATE_ALWAYS_HIDDEN: + case SOFT_INPUT_STATE_HIDDEN: + return false; + } + return r.mLastImeShown && mInputMethodWindow != null && mInputMethodWindow.mHasSurface + && mInputMethodWindow.mViewVisibility == View.VISIBLE; + } + /** Returns {@code true} if the top activity is transformed with the new rotation of display. */ boolean hasTopFixedRotationLaunchingApp() { return mFixedRotationLaunchingApp != null @@ -3814,7 +3833,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // 4. Update the IME control target to apply any inset change and animation. // 5. Reparent the IME container surface to either the input target app, or the IME window // parent. - updateImeControlTarget(); + updateImeControlTarget(true /* forceUpdateImeParent */); } @VisibleForTesting @@ -3946,12 +3965,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } void updateImeControlTarget() { + updateImeControlTarget(false /* forceUpdateImeParent */); + } + + void updateImeControlTarget(boolean forceUpdateImeParent) { InsetsControlTarget prevImeControlTarget = mImeControlTarget; mImeControlTarget = computeImeControlTarget(); mInsetsStateController.onImeControlTargetChanged(mImeControlTarget); - // Update Ime parent when IME insets leash created, which is the best time that default - // IME visibility has been settled down after IME control target changed. - if (prevImeControlTarget != mImeControlTarget) { + // Update Ime parent when IME insets leash created or the new IME layering target might + // updated from setImeLayeringTarget, which is the best time that default IME visibility + // has been settled down after IME control target changed. + if (prevImeControlTarget != mImeControlTarget || forceUpdateImeParent) { updateImeParent(); } @@ -5468,6 +5492,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } boolean updateDisplayOverrideConfigurationLocked() { + // Preemptively cancel the running recents animation -- SysUI can't currently handle this + // case properly since the signals it receives all happen post-change + final RecentsAnimationController recentsAnimationController = + mWmService.getRecentsAnimationController(); + if (recentsAnimationController != null) { + recentsAnimationController.cancelAnimationForDisplayChange(); + } + Configuration values = new Configuration(); computeScreenConfiguration(values); diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index c8f2777cc172..73d6cecd9155 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -480,6 +480,16 @@ public class DisplayRotation { return false; } + // Preemptively cancel the running recents animation -- SysUI can't currently handle this + // case properly since the signals it receives all happen post-change. We do this earlier + // in the rotation flow, since DisplayContent.updateDisplayOverrideConfigurationLocked seems + // to happen too late. + final RecentsAnimationController recentsAnimationController = + mService.getRecentsAnimationController(); + if (recentsAnimationController != null) { + recentsAnimationController.cancelAnimationForDisplayChange(); + } + final Transition t = (useShellTransitions && !mService.mAtmService.getTransitionController().isCollecting()) ? mService.mAtmService.getTransitionController().createTransition(TRANSIT_CHANGE) diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 5362771ed286..a7e633527b54 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -221,7 +221,7 @@ public class RecentsAnimationController implements DeathRecipient { final ArraySet<Task> tasks = Sets.newArraySet(task); snapshotController.snapshotTasks(tasks); snapshotController.addSkipClosingAppSnapshotTasks(tasks); - return snapshotController.getSnapshot(taskId, 0 /* userId */, + return snapshotController.getSnapshot(taskId, task.mUserId, false /* restoreFromDisk */, false /* isLowResolution */); } } @@ -353,18 +353,23 @@ public class RecentsAnimationController implements DeathRecipient { @Override public void cleanupScreenshot() { - synchronized (mService.mGlobalLock) { - if (mRecentScreenshotAnimator != null) { - mRecentScreenshotAnimator.cancelAnimation(); - mRecentScreenshotAnimator = null; + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mService.mGlobalLock) { + if (mRecentScreenshotAnimator != null) { + mRecentScreenshotAnimator.cancelAnimation(); + mRecentScreenshotAnimator = null; + } } + } finally { + Binder.restoreCallingIdentity(token); } } @Override public void setWillFinishToHome(boolean willFinishToHome) { synchronized (mService.getWindowManagerLock()) { - mWillFinishToHome = willFinishToHome; + RecentsAnimationController.this.setWillFinishToHome(willFinishToHome); } } @@ -513,7 +518,6 @@ public class RecentsAnimationController implements DeathRecipient { || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; } - @VisibleForTesting AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) { return addAnimation(task, isRecentTaskInvisible, false /* hidden */, @@ -816,6 +820,18 @@ public class RecentsAnimationController implements DeathRecipient { cancelAnimation(REORDER_KEEP_IN_PLACE, screenshot, "rootTaskOrderChanged"); } + /** + * If there is a recents animation running, we need to cancel the animation and snapshot the + * tasks before the change (to ensure they are captured at the right configuration) + */ + public void cancelAnimationForDisplayChange() { + if (mCanceled) { + return; + } + cancelAnimation(mWillFinishToHome ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION, + true /* screenshot */, "cancelAnimationForDisplayChange"); + } + private void cancelAnimation(@ReorderMode int reorderMode, boolean screenshot, String reason) { ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "cancelAnimation(): reason=%s", reason); synchronized (mService.getWindowManagerLock()) { @@ -826,7 +842,7 @@ public class RecentsAnimationController implements DeathRecipient { mService.mH.removeCallbacks(mFailsafeRunnable); mCanceled = true; - if (screenshot) { + if (screenshot && !mPendingAnimations.isEmpty()) { // Screen shot previous task when next task starts transition and notify the runner. // We will actually finish the animation once the runner calls cleanUpScreenshot(). final Task task = mPendingAnimations.get(0).mTask; @@ -853,6 +869,11 @@ public class RecentsAnimationController implements DeathRecipient { } } + @VisibleForTesting + void setWillFinishToHome(boolean willFinishToHome) { + mWillFinishToHome = willFinishToHome; + } + /** * Cancel recents animation when the next app transition starts. * <p> @@ -905,7 +926,8 @@ public class RecentsAnimationController implements DeathRecipient { return null; } - final TaskScreenshotAnimatable animatable = new TaskScreenshotAnimatable(mService.mSurfaceControlFactory, task, + final TaskScreenshotAnimatable animatable = new TaskScreenshotAnimatable( + mService.mSurfaceControlFactory, task, new SurfaceControl.ScreenshotHardwareBuffer(taskSnapshot.getHardwareBuffer(), taskSnapshot.getColorSpace(), false /* containsSecureLayers */)); mRecentScreenshotAnimator = new SurfaceAnimator( @@ -1006,7 +1028,16 @@ public class RecentsAnimationController implements DeathRecipient { @Override public void binderDied() { - cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied"); + if (!mCanceled) { + cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied"); + } else { + synchronized (mService.getWindowManagerLock()) { + if (mRecentScreenshotAnimator != null) { + mRecentScreenshotAnimator.cancelAnimation(); + mRecentScreenshotAnimator = null; + } + } + } synchronized (mService.getWindowManagerLock()) { // Clear associated input consumers on runner death 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/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java index b56e76d91370..be6a5d2fe27b 100644 --- a/services/core/java/com/android/server/wm/ShellRoot.java +++ b/services/core/java/com/android/server/wm/ShellRoot.java @@ -50,6 +50,7 @@ public class ShellRoot { private SurfaceControl mSurfaceControl = null; private IWindow mAccessibilityWindow; private IBinder.DeathRecipient mAccessibilityWindowDeath; + private int mWindowType; ShellRoot(@NonNull IWindow client, @NonNull DisplayContent dc, @WindowManager.ShellRootLayer final int shellRootLayer) { @@ -64,19 +65,18 @@ public class ShellRoot { return; } mClient = client; - int windowType; switch (shellRootLayer) { case SHELL_ROOT_LAYER_DIVIDER: - windowType = TYPE_DOCK_DIVIDER; + mWindowType = TYPE_DOCK_DIVIDER; break; case SHELL_ROOT_LAYER_PIP: - windowType = TYPE_APPLICATION_OVERLAY; + mWindowType = TYPE_APPLICATION_OVERLAY; break; default: throw new IllegalArgumentException(shellRootLayer + " is not an acceptable shell root layer."); } - mToken = new WindowToken.Builder(dc.mWmService, client.asBinder(), windowType) + mToken = new WindowToken.Builder(dc.mWmService, client.asBinder(), mWindowType) .setDisplayContent(dc) .setPersistOnEmpty(true) .setOwnerCanManageAppTokens(true) @@ -89,6 +89,10 @@ public class ShellRoot { mToken.getPendingTransaction().show(mSurfaceControl); } + int getWindowType() { + return mWindowType; + } + void clear() { if (mClient != null) { mClient.asBinder().unlinkToDeath(mDeathRecipient, 0); diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java index 59de43ac95a3..3f9c93bbcfbe 100644 --- a/services/core/java/com/android/server/wm/StartingData.java +++ b/services/core/java/com/android/server/wm/StartingData.java @@ -26,6 +26,12 @@ public abstract class StartingData { protected final WindowManagerService mService; protected final int mTypeParams; + /** + * Tell whether the launching activity should use + * {@link android.view.WindowManager.LayoutParams#SOFT_INPUT_IS_FORWARD_NAVIGATION}. + */ + boolean mIsTransitionForward; + protected StartingData(WindowManagerService service, int typeParams) { mService = service; mTypeParams = typeParams; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 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/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 9ffb2b176915..0000f95e7849 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -443,7 +443,8 @@ class TaskSnapshotController { } else { excludeLayers = new SurfaceControl[0]; } - builder.setHasImeSurface(!excludeIme && imeWindow != null && imeWindow.isDrawn()); + builder.setHasImeSurface(!excludeIme && imeWindow != null && imeWindow.isVisible()); + final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = SurfaceControl.captureLayersExcluding( task.getSurfaceControl(), mTmpRect, scaleFraction, diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index e9dd521670a9..147260b9e912 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -34,6 +34,7 @@ import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEME import static android.content.pm.PackageManager.FEATURE_PC; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; +import static android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE; import static android.os.Process.SYSTEM_UID; import static android.os.Process.myPid; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; @@ -2463,6 +2464,17 @@ 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 */); } @@ -2534,12 +2546,13 @@ public class WindowManagerService extends IWindowManager.Stub // We will leave the critical section before returning the leash to the client, // so we need to copy the leash to prevent others release the one that we are // about to return. - // TODO: We will have an extra copy if the client is not local. - // For now, we rely on GC to release it. - // Maybe we can modify InsetsSourceControl.writeToParcel so it can release - // the extra leash as soon as possible. - outControls[i] = controls[i] != null - ? new InsetsSourceControl(controls[i]) : null; + if (controls[i] != null) { + // This source control is an extra copy if the client is not local. By setting + // PARCELABLE_WRITE_RETURN_VALUE, the leash will be released at the end of + // SurfaceControl.writeToParcel. + outControls[i] = new InsetsSourceControl(controls[i]); + outControls[i].setParcelableFlags(PARCELABLE_WRITE_RETURN_VALUE); + } } } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 07ac73b05706..bba50a75a3b9 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2568,6 +2568,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP private void setupWindowForRemoveOnExit() { mRemoveOnExit = true; setDisplayLayoutNeeded(); + getDisplayContent().getDisplayPolicy().removeWindowLw(this); // Request a focus update as this window's input channel is already gone. Otherwise // we could have no focused window in input manager. final boolean focusChanged = mWmService.updateFocusedWindowLocked( 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_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index e13ef99e17dd..a94ad4ad7ef2 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -997,10 +997,7 @@ void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration } void NativeInputManager::displayRemoved(JNIEnv* env, int32_t displayId) { - // Set an empty list to remove all handles from the specific display. - std::vector<sp<InputWindowHandle>> windowHandles; - mInputManager->getDispatcher()->setInputWindows({{displayId, windowHandles}}); - mInputManager->getDispatcher()->setFocusedApplication(displayId, nullptr); + mInputManager->getDispatcher()->displayRemoved(displayId); } void NativeInputManager::setFocusedApplication(JNIEnv* env, int32_t displayId, diff --git a/services/core/jni/com_android_server_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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 25d37695267b..78ad59f8c01b 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -8030,8 +8030,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { inForeground = true; receiverComponent = resolveDelegateReceiver( DELEGATION_SECURITY_LOGGING, action, userId); - // STOPSHIP(b/185004808): remove excessive log. - Slogf.d(LOG_TAG, "Delegate for security logs broadcast: " + receiverComponent); } if (receiverComponent == null) { receiverComponent = getOwnerComponent(userId); @@ -17567,10 +17565,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isUsbDataSignalingEnabled(String packageName) { + final CallerIdentity caller = getCallerIdentity(packageName); synchronized (getLockObject()) { - final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked( - getCallerIdentity(packageName)); - return admin.mUsbDataSignalingEnabled; + // If the caller is an admin, return the policy set by itself. Otherwise + // return the device-wide policy. + if (isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)) { + return getProfileOwnerOrDeviceOwnerLocked(caller).mUsbDataSignalingEnabled; + } else { + return isUsbDataSignalingEnabledInternalLocked(); + } } } @@ -17580,12 +17583,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization(isSystemUid(caller)); synchronized (getLockObject()) { - final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked( - UserHandle.USER_SYSTEM); - return admin == null || admin.mUsbDataSignalingEnabled; + return isUsbDataSignalingEnabledInternalLocked(); } } + private boolean isUsbDataSignalingEnabledInternalLocked() { + final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked( + UserHandle.USER_SYSTEM); + return admin == null || admin.mUsbDataSignalingEnabled; + } + @Override public boolean canUsbDataSignalingBeDisabled() { return mInjector.binderWithCleanCallingIdentity(() -> diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java index 0741c81bb528..c29de905d370 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java @@ -64,7 +64,7 @@ class SecurityLogMonitor implements Runnable { mLastForceNanos = System.nanoTime(); } - private static final boolean DEBUG = true; // STOPSHIP if true. + private static final boolean DEBUG = false; // STOPSHIP if true. private static final String TAG = "SecurityLogMonitor"; /** * Each log entry can hold up to 4K bytes (but as of {@link android.os.Build.VERSION_CODES#N} @@ -427,7 +427,7 @@ class SecurityLogMonitor implements Runnable { while (!Thread.currentThread().isInterrupted()) { try { final boolean force = mForceSemaphore.tryAcquire(POLLING_INTERVAL_MS, MILLISECONDS); - if (DEBUG) Slog.d(TAG, "Retrieving next batch, force=" + force); + getNextBatch(newLogs); mLock.lockInterruptibly(); @@ -469,11 +469,6 @@ class SecurityLogMonitor implements Runnable { return; } final int logSize = mPendingLogs.size(); - if (DEBUG) { - Slog.d(TAG, String.format( - "notifyDeviceOwnerOrProfileOwnerIfNeeded, size: %d now: %d next: %d", - logSize, SystemClock.elapsedRealtime(), mNextAllowedRetrievalTimeMillis)); - } if (logSize >= BUFFER_ENTRIES_NOTIFICATION_LEVEL || (force && logSize > 0)) { // Allow DO to retrieve logs if too many pending logs or if forced. if (!mAllowedToRetrieve) { @@ -512,7 +507,6 @@ class SecurityLogMonitor implements Runnable { synchronized (mForceSemaphore) { final long toWaitNanos = mLastForceNanos + FORCE_FETCH_THROTTLE_NS - nowNanos; if (toWaitNanos > 0) { - if (DEBUG) Slog.d(TAG, "Forcing security logs throttled"); return NANOSECONDS.toMillis(toWaitNanos) + 1; // Round up. } mLastForceNanos = nowNanos; @@ -521,7 +515,6 @@ class SecurityLogMonitor implements Runnable { if (mForceSemaphore.availablePermits() == 0) { mForceSemaphore.release(); } - if (DEBUG) Slog.d(TAG, "Forcing security logs semaphore released"); return 0; } } diff --git a/services/net/Android.bp b/services/net/Android.bp index f92db86bb880..a822257e1a74 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -37,15 +37,7 @@ java_library { name: "services.net-module-wifi", srcs: [ ":framework-services-net-module-wifi-shared-srcs", - ":net-module-utils-srcs", ":net-utils-services-common-srcs", - "java/android/net/ip/IpClientCallbacks.java", - "java/android/net/ip/IpClientManager.java", - "java/android/net/ip/IpClientUtil.java", - "java/android/net/util/KeepalivePacketDataUtil.java", - "java/android/net/util/NetworkConstants.java", - "java/android/net/IpMemoryStore.java", - "java/android/net/NetworkMonitorManager.java", ], sdk_version: "module_current", min_sdk_version: "30", @@ -59,7 +51,6 @@ 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", ], @@ -89,11 +80,7 @@ filegroup { filegroup { name: "services-connectivity-shared-srcs", srcs: [ - // TODO: move to networkstack-client - "java/android/net/IpMemoryStore.java", - "java/android/net/NetworkMonitorManager.java", // TODO: move to libs/net - "java/android/net/util/KeepalivePacketDataUtil.java", "java/android/net/util/NetworkConstants.java", ], } diff --git a/services/net/java/android/net/IpMemoryStore.java b/services/net/java/android/net/IpMemoryStore.java deleted file mode 100644 index 8df2e0d08e0e..000000000000 --- a/services/net/java/android/net/IpMemoryStore.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.annotation.NonNull; -import android.content.Context; -import android.net.networkstack.ModuleNetworkStackClient; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; - -/** - * Manager class used to communicate with the ip memory store service in the network stack, - * which is running in a separate module. - * @hide -*/ -public class IpMemoryStore extends IpMemoryStoreClient { - private static final String TAG = IpMemoryStore.class.getSimpleName(); - @NonNull private final CompletableFuture<IIpMemoryStore> mService; - @NonNull private final AtomicReference<CompletableFuture<IIpMemoryStore>> mTailNode; - - public IpMemoryStore(@NonNull final Context context) { - super(context); - mService = new CompletableFuture<>(); - mTailNode = new AtomicReference<CompletableFuture<IIpMemoryStore>>(mService); - getModuleNetworkStackClient(context).fetchIpMemoryStore( - new IIpMemoryStoreCallbacks.Stub() { - @Override - public void onIpMemoryStoreFetched(@NonNull final IIpMemoryStore memoryStore) { - mService.complete(memoryStore); - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - - @Override - public String getInterfaceHash() { - return this.HASH; - } - }); - } - - /* - * If the IpMemoryStore is ready, this function will run the request synchronously. - * Otherwise, it will enqueue the requests for execution immediately after the - * service becomes ready. The requests are guaranteed to be executed in the order - * they are sumbitted. - */ - @Override - protected void runWhenServiceReady(Consumer<IIpMemoryStore> cb) throws ExecutionException { - mTailNode.getAndUpdate(future -> future.handle((store, exception) -> { - if (exception != null) { - // this should never happens since we also catch the exception below - Log.wtf(TAG, "Error fetching IpMemoryStore", exception); - return store; - } - - try { - cb.accept(store); - } catch (Exception e) { - Log.wtf(TAG, "Exception occured: " + e.getMessage()); - } - return store; - })); - } - - @VisibleForTesting - protected ModuleNetworkStackClient getModuleNetworkStackClient(Context context) { - return ModuleNetworkStackClient.getInstance(context); - } - - /** Gets an instance of the memory store */ - @NonNull - public static IpMemoryStore getMemoryStore(final Context context) { - return new IpMemoryStore(context); - } -} diff --git a/services/net/java/android/net/NetworkMonitorManager.java b/services/net/java/android/net/NetworkMonitorManager.java deleted file mode 100644 index 0f669817f52e..000000000000 --- a/services/net/java/android/net/NetworkMonitorManager.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.annotation.Hide; -import android.annotation.NonNull; -import android.os.Binder; -import android.os.RemoteException; -import android.util.Log; - -/** - * A convenience wrapper for INetworkMonitor. - * - * Wraps INetworkMonitor calls, making them a bit more friendly to use. Currently handles: - * - Clearing calling identity - * - Ignoring RemoteExceptions - * - Converting to stable parcelables - * - * By design, all methods on INetworkMonitor are asynchronous oneway IPCs and are thus void. All the - * wrapper methods in this class return a boolean that callers can use to determine whether - * RemoteException was thrown. - */ -@Hide -public class NetworkMonitorManager { - - @NonNull private final INetworkMonitor mNetworkMonitor; - @NonNull private final String mTag; - - public NetworkMonitorManager(@NonNull INetworkMonitor networkMonitorManager, - @NonNull String tag) { - mNetworkMonitor = networkMonitorManager; - mTag = tag; - } - - public NetworkMonitorManager(@NonNull INetworkMonitor networkMonitorManager) { - this(networkMonitorManager, NetworkMonitorManager.class.getSimpleName()); - } - - private void log(String s, Throwable e) { - Log.e(mTag, s, e); - } - - // CHECKSTYLE:OFF Generated code - - public boolean start() { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.start(); - return true; - } catch (RemoteException e) { - log("Error in start", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean launchCaptivePortalApp() { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.launchCaptivePortalApp(); - return true; - } catch (RemoteException e) { - log("Error in launchCaptivePortalApp", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean notifyCaptivePortalAppFinished(int response) { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.notifyCaptivePortalAppFinished(response); - return true; - } catch (RemoteException e) { - log("Error in notifyCaptivePortalAppFinished", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean setAcceptPartialConnectivity() { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.setAcceptPartialConnectivity(); - return true; - } catch (RemoteException e) { - log("Error in setAcceptPartialConnectivity", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean forceReevaluation(int uid) { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.forceReevaluation(uid); - return true; - } catch (RemoteException e) { - log("Error in forceReevaluation", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean notifyPrivateDnsChanged(PrivateDnsConfigParcel config) { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.notifyPrivateDnsChanged(config); - return true; - } catch (RemoteException e) { - log("Error in notifyPrivateDnsChanged", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean notifyDnsResponse(int returnCode) { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.notifyDnsResponse(returnCode); - return true; - } catch (RemoteException e) { - log("Error in notifyDnsResponse", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean notifyNetworkConnected(LinkProperties lp, NetworkCapabilities nc) { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.notifyNetworkConnected(lp, nc); - return true; - } catch (RemoteException e) { - log("Error in notifyNetworkConnected", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean notifyNetworkDisconnected() { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.notifyNetworkDisconnected(); - return true; - } catch (RemoteException e) { - log("Error in notifyNetworkDisconnected", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean notifyLinkPropertiesChanged(LinkProperties lp) { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.notifyLinkPropertiesChanged(lp); - return true; - } catch (RemoteException e) { - log("Error in notifyLinkPropertiesChanged", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - public boolean notifyNetworkCapabilitiesChanged(NetworkCapabilities nc) { - final long token = Binder.clearCallingIdentity(); - try { - mNetworkMonitor.notifyNetworkCapabilitiesChanged(nc); - return true; - } catch (RemoteException e) { - log("Error in notifyNetworkCapabilitiesChanged", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - // CHECKSTYLE:ON Generated code -} diff --git a/services/net/java/android/net/ip/IpClientCallbacks.java b/services/net/java/android/net/ip/IpClientCallbacks.java deleted file mode 100644 index b17fcaa132a1..000000000000 --- a/services/net/java/android/net/ip/IpClientCallbacks.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import android.net.DhcpResultsParcelable; -import android.net.Layer2PacketParcelable; -import android.net.LinkProperties; - -import java.util.List; - -/** - * Callbacks for handling IpClient events. - * - * This is a convenience class to allow clients not to override all methods of IIpClientCallbacks, - * and avoid unparceling arguments. - * These methods are called asynchronously on a Binder thread, as IpClient lives in a different - * process. - * @hide - */ -public class IpClientCallbacks { - - /** - * Callback called upon IpClient creation. - * - * @param ipClient The Binder token to communicate with IpClient. - */ - public void onIpClientCreated(IIpClient ipClient) {} - - /** - * Callback called prior to DHCP discovery/renewal. - * - * <p>In order to receive onPreDhcpAction(), call #withPreDhcpAction() when constructing a - * ProvisioningConfiguration. - * - * <p>Implementations of onPreDhcpAction() must call IpClient#completedPreDhcpAction() to - * indicate that DHCP is clear to proceed. - */ - public void onPreDhcpAction() {} - - /** - * Callback called after DHCP discovery/renewal. - */ - public void onPostDhcpAction() {} - - /** - * Callback called when new DHCP results are available. - * - * <p>This is purely advisory and not an indication of provisioning success or failure. This is - * only here for callers that want to expose DHCPv4 results to other APIs - * (e.g., WifiInfo#setInetAddress). - * - * <p>DHCPv4 or static IPv4 configuration failure or success can be determined by whether or not - * the passed-in DhcpResults object is null. - */ - public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) { - // In general callbacks would not use a parcelable directly (DhcpResultsParcelable), and - // would use a wrapper instead, because of the lack of safety of stable parcelables. But - // there are already two classes in the tree for DHCP information: DhcpInfo and DhcpResults, - // and neither of them exposes an appropriate API (they are bags of mutable fields and can't - // be changed because they are public API and @UnsupportedAppUsage, being no better than the - // stable parcelable). Adding a third class would cost more than the gain considering that - // the only client of this callback is WiFi, which will end up converting the results to - // DhcpInfo anyway. - } - - /** - * Indicates that provisioning was successful. - */ - public void onProvisioningSuccess(LinkProperties newLp) {} - - /** - * Indicates that provisioning failed. - */ - public void onProvisioningFailure(LinkProperties newLp) {} - - /** - * Invoked on LinkProperties changes. - */ - public void onLinkPropertiesChange(LinkProperties newLp) {} - - /**Called when the internal IpReachabilityMonitor (if enabled) has - * detected the loss of a critical number of required neighbors. - */ - public void onReachabilityLost(String logMsg) {} - - /** - * Called when the IpClient state machine terminates. - */ - public void onQuit() {} - - /** - * Called to indicate that a new APF program must be installed to filter incoming packets. - */ - public void installPacketFilter(byte[] filter) {} - - /** - * Called to indicate that the APF Program & data buffer must be read asynchronously from the - * wifi driver. - * - * <p>Due to Wifi HAL limitations, the current implementation only supports dumping the entire - * buffer. In response to this request, the driver returns the data buffer asynchronously - * by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message. - */ - public void startReadPacketFilter() {} - - /** - * If multicast filtering cannot be accomplished with APF, this function will be called to - * actuate multicast filtering using another means. - */ - public void setFallbackMulticastFilter(boolean enabled) {} - - /** - * Enabled/disable Neighbor Discover offload functionality. This is called, for example, - * whenever 464xlat is being started or stopped. - */ - public void setNeighborDiscoveryOffload(boolean enable) {} - - /** - * Invoked on starting preconnection process. - */ - public void onPreconnectionStart(List<Layer2PacketParcelable> packets) {} -} diff --git a/services/net/java/android/net/ip/IpClientManager.java b/services/net/java/android/net/ip/IpClientManager.java deleted file mode 100644 index b45405f39f0f..000000000000 --- a/services/net/java/android/net/ip/IpClientManager.java +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import android.annotation.Hide; -import android.annotation.NonNull; -import android.net.NattKeepalivePacketData; -import android.net.ProxyInfo; -import android.net.TcpKeepalivePacketData; -import android.net.TcpKeepalivePacketDataParcelable; -import android.net.shared.Layer2Information; -import android.net.shared.ProvisioningConfiguration; -import android.net.util.KeepalivePacketDataUtil; -import android.os.Binder; -import android.os.RemoteException; -import android.util.Log; - -/** - * A convenience wrapper for IpClient. - * - * Wraps IIpClient calls, making them a bit more friendly to use. Currently handles: - * - Clearing calling identity - * - Ignoring RemoteExceptions - * - Converting to stable parcelables - * - * By design, all methods on IIpClient are asynchronous oneway IPCs and are thus void. All the - * wrapper methods in this class return a boolean that callers can use to determine whether - * RemoteException was thrown. - */ -@Hide -public class IpClientManager { - @NonNull private final IIpClient mIpClient; - @NonNull private final String mTag; - - public IpClientManager(@NonNull IIpClient ipClient, @NonNull String tag) { - mIpClient = ipClient; - mTag = tag; - } - - public IpClientManager(@NonNull IIpClient ipClient) { - this(ipClient, IpClientManager.class.getSimpleName()); - } - - private void log(String s, Throwable e) { - Log.e(mTag, s, e); - } - - /** - * For clients using {@link ProvisioningConfiguration.Builder#withPreDhcpAction()}, must be - * called after {@link IIpClientCallbacks#onPreDhcpAction} to indicate that DHCP is clear to - * proceed. - */ - public boolean completedPreDhcpAction() { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.completedPreDhcpAction(); - return true; - } catch (RemoteException e) { - log("Error completing PreDhcpAction", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Confirm the provisioning configuration. - */ - public boolean confirmConfiguration() { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.confirmConfiguration(); - return true; - } catch (RemoteException e) { - log("Error confirming IpClient configuration", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Indicate that packet filter read is complete. - */ - public boolean readPacketFilterComplete(byte[] data) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.readPacketFilterComplete(data); - return true; - } catch (RemoteException e) { - log("Error notifying IpClient of packet filter read", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Shut down this IpClient instance altogether. - */ - public boolean shutdown() { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.shutdown(); - return true; - } catch (RemoteException e) { - log("Error shutting down IpClient", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Start provisioning with the provided parameters. - */ - public boolean startProvisioning(ProvisioningConfiguration prov) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.startProvisioning(prov.toStableParcelable()); - return true; - } catch (RemoteException e) { - log("Error starting IpClient provisioning", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Stop this IpClient. - * - * <p>This does not shut down the StateMachine itself, which is handled by {@link #shutdown()}. - */ - public boolean stop() { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.stop(); - return true; - } catch (RemoteException e) { - log("Error stopping IpClient", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Set the TCP buffer sizes to use. - * - * This may be called, repeatedly, at any time before or after a call to - * #startProvisioning(). The setting is cleared upon calling #stop(). - */ - public boolean setTcpBufferSizes(String tcpBufferSizes) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.setTcpBufferSizes(tcpBufferSizes); - return true; - } catch (RemoteException e) { - log("Error setting IpClient TCP buffer sizes", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Set the HTTP Proxy configuration to use. - * - * This may be called, repeatedly, at any time before or after a call to - * #startProvisioning(). The setting is cleared upon calling #stop(). - */ - public boolean setHttpProxy(ProxyInfo proxyInfo) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.setHttpProxy(proxyInfo); - return true; - } catch (RemoteException e) { - log("Error setting IpClient proxy", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Enable or disable the multicast filter. Attempts to use APF to accomplish the filtering, - * if not, Callback.setFallbackMulticastFilter() is called. - */ - public boolean setMulticastFilter(boolean enabled) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.setMulticastFilter(enabled); - return true; - } catch (RemoteException e) { - log("Error setting multicast filter", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Add a TCP keepalive packet filter before setting up keepalive offload. - */ - public boolean addKeepalivePacketFilter(int slot, TcpKeepalivePacketData pkt) { - return addKeepalivePacketFilter(slot, KeepalivePacketDataUtil.toStableParcelable(pkt)); - } - - /** - * Add a TCP keepalive packet filter before setting up keepalive offload. - * @deprecated This method is for use on pre-S platforms where TcpKeepalivePacketData is not - * system API. On newer platforms use - * addKeepalivePacketFilter(int, TcpKeepalivePacketData) instead. - */ - @Deprecated - public boolean addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.addKeepalivePacketFilter(slot, pkt); - return true; - } catch (RemoteException e) { - log("Error adding Keepalive Packet Filter ", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Add a NAT-T keepalive packet filter before setting up keepalive offload. - */ - public boolean addKeepalivePacketFilter(int slot, NattKeepalivePacketData pkt) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.addNattKeepalivePacketFilter( - slot, KeepalivePacketDataUtil.toStableParcelable(pkt)); - return true; - } catch (RemoteException e) { - log("Error adding NAT-T Keepalive Packet Filter ", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Remove a keepalive packet filter after stopping keepalive offload. - */ - public boolean removeKeepalivePacketFilter(int slot) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.removeKeepalivePacketFilter(slot); - return true; - } catch (RemoteException e) { - log("Error removing Keepalive Packet Filter ", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Set the L2 key and group hint for storing info into the memory store. - */ - public boolean setL2KeyAndGroupHint(String l2Key, String groupHint) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.setL2KeyAndGroupHint(l2Key, groupHint); - return true; - } catch (RemoteException e) { - log("Failed setL2KeyAndGroupHint", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Notify IpClient that preconnection is complete and that the link is ready for use. - * The success parameter indicates whether the packets passed in by 'onPreconnectionStart' - * were successfully sent to the network or not. - */ - public boolean notifyPreconnectionComplete(boolean success) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.notifyPreconnectionComplete(success); - return true; - } catch (RemoteException e) { - log("Error notifying IpClient Preconnection completed", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } - - /** - * Update the bssid, L2 key and group hint layer2 information. - */ - public boolean updateLayer2Information(Layer2Information info) { - final long token = Binder.clearCallingIdentity(); - try { - mIpClient.updateLayer2Information(info.toStableParcelable()); - return true; - } catch (RemoteException e) { - log("Error updating layer2 information", e); - return false; - } finally { - Binder.restoreCallingIdentity(token); - } - } -} diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java deleted file mode 100644 index 426614ec2f53..000000000000 --- a/services/net/java/android/net/ip/IpClientUtil.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import android.content.Context; -import android.net.DhcpResultsParcelable; -import android.net.Layer2PacketParcelable; -import android.net.LinkProperties; -import android.net.networkstack.ModuleNetworkStackClient; -import android.os.ConditionVariable; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.List; - - -/** - * Utilities and wrappers to simplify communication with IpClient, which lives in the NetworkStack - * process. - * - * @hide - */ -public class IpClientUtil { - // TODO: remove with its callers - public static final String DUMP_ARG = "ipclient"; - - /** - * Subclass of {@link IpClientCallbacks} allowing clients to block until provisioning is - * complete with {@link WaitForProvisioningCallbacks#waitForProvisioning()}. - */ - public static class WaitForProvisioningCallbacks extends IpClientCallbacks { - private final ConditionVariable mCV = new ConditionVariable(); - private LinkProperties mCallbackLinkProperties; - - /** - * Block until either {@link #onProvisioningSuccess(LinkProperties)} or - * {@link #onProvisioningFailure(LinkProperties)} is called. - */ - public LinkProperties waitForProvisioning() { - mCV.block(); - return mCallbackLinkProperties; - } - - @Override - public void onProvisioningSuccess(LinkProperties newLp) { - mCallbackLinkProperties = newLp; - mCV.open(); - } - - @Override - public void onProvisioningFailure(LinkProperties newLp) { - mCallbackLinkProperties = null; - mCV.open(); - } - } - - /** - * Create a new IpClient. - * - * <p>This is a convenience method to allow clients to use {@link IpClientCallbacks} instead of - * {@link IIpClientCallbacks}. - * @see {@link ModuleNetworkStackClient#makeIpClient(String, IIpClientCallbacks)} - */ - public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) { - ModuleNetworkStackClient.getInstance(context) - .makeIpClient(ifName, new IpClientCallbacksProxy(callback)); - } - - /** - * Wrapper to relay calls from {@link IIpClientCallbacks} to {@link IpClientCallbacks}. - */ - private static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub { - protected final IpClientCallbacks mCb; - - /** - * Create a new IpClientCallbacksProxy. - */ - public IpClientCallbacksProxy(IpClientCallbacks cb) { - mCb = cb; - } - - @Override - public void onIpClientCreated(IIpClient ipClient) { - mCb.onIpClientCreated(ipClient); - } - - @Override - public void onPreDhcpAction() { - mCb.onPreDhcpAction(); - } - - @Override - public void onPostDhcpAction() { - mCb.onPostDhcpAction(); - } - - // This is purely advisory and not an indication of provisioning - // success or failure. This is only here for callers that want to - // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress). - // DHCPv4 or static IPv4 configuration failure or success can be - // determined by whether or not the passed-in DhcpResults object is - // null or not. - @Override - public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) { - mCb.onNewDhcpResults(dhcpResults); - } - - @Override - public void onProvisioningSuccess(LinkProperties newLp) { - mCb.onProvisioningSuccess(newLp); - } - @Override - public void onProvisioningFailure(LinkProperties newLp) { - mCb.onProvisioningFailure(newLp); - } - - // Invoked on LinkProperties changes. - @Override - public void onLinkPropertiesChange(LinkProperties newLp) { - mCb.onLinkPropertiesChange(newLp); - } - - // Called when the internal IpReachabilityMonitor (if enabled) has - // detected the loss of a critical number of required neighbors. - @Override - public void onReachabilityLost(String logMsg) { - mCb.onReachabilityLost(logMsg); - } - - // Called when the IpClient state machine terminates. - @Override - public void onQuit() { - mCb.onQuit(); - } - - // Install an APF program to filter incoming packets. - @Override - public void installPacketFilter(byte[] filter) { - mCb.installPacketFilter(filter); - } - - // Asynchronously read back the APF program & data buffer from the wifi driver. - // Due to Wifi HAL limitations, the current implementation only supports dumping the entire - // buffer. In response to this request, the driver returns the data buffer asynchronously - // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message. - @Override - public void startReadPacketFilter() { - mCb.startReadPacketFilter(); - } - - // If multicast filtering cannot be accomplished with APF, this function will be called to - // actuate multicast filtering using another means. - @Override - public void setFallbackMulticastFilter(boolean enabled) { - mCb.setFallbackMulticastFilter(enabled); - } - - // Enabled/disable Neighbor Discover offload functionality. This is - // called, for example, whenever 464xlat is being started or stopped. - @Override - public void setNeighborDiscoveryOffload(boolean enable) { - mCb.setNeighborDiscoveryOffload(enable); - } - - // Invoked on starting preconnection process. - @Override - public void onPreconnectionStart(List<Layer2PacketParcelable> packets) { - mCb.onPreconnectionStart(packets); - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - - @Override - public String getInterfaceHash() { - return this.HASH; - } - } - - /** - * Dump logs for the specified IpClient. - * TODO: remove callers and delete - */ - public static void dumpIpClient( - IIpClient connector, FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("IpClient logs have moved to dumpsys network_stack"); - } -} diff --git a/services/net/java/android/net/util/DhcpResultsCompatUtil.java b/services/net/java/android/net/util/DhcpResultsCompatUtil.java deleted file mode 100644 index fce0834c116e..000000000000 --- a/services/net/java/android/net/util/DhcpResultsCompatUtil.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.util; - -import static android.net.shared.IpConfigurationParcelableUtil.unparcelAddress; - -import android.annotation.Nullable; -import android.net.DhcpResults; -import android.net.DhcpResultsParcelable; - -import java.net.Inet4Address; - -/** - * Compatibility utility for code that still uses DhcpResults. - * - * TODO: remove this class when all usages of DhcpResults (including Wifi in AOSP) are removed. - */ -public class DhcpResultsCompatUtil { - - /** - * Convert a DhcpResultsParcelable to DhcpResults. - * - * contract { - * returns(null) implies p == null - * returnsNotNull() implies p != null - * } - */ - @Nullable - public static DhcpResults fromStableParcelable(@Nullable DhcpResultsParcelable p) { - if (p == null) return null; - final DhcpResults results = new DhcpResults(p.baseConfiguration); - results.leaseDuration = p.leaseDuration; - results.mtu = p.mtu; - results.serverAddress = (Inet4Address) unparcelAddress(p.serverAddress); - results.vendorInfo = p.vendorInfo; - results.serverHostName = p.serverHostName; - results.captivePortalApiUrl = p.captivePortalApiUrl; - return results; - } -} diff --git a/services/net/java/android/net/util/KeepalivePacketDataUtil.java b/services/net/java/android/net/util/KeepalivePacketDataUtil.java deleted file mode 100644 index 566698576026..000000000000 --- a/services/net/java/android/net/util/KeepalivePacketDataUtil.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.util; - -import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.InvalidPacketException; -import android.net.KeepalivePacketData; -import android.net.NattKeepalivePacketData; -import android.net.NattKeepalivePacketDataParcelable; -import android.net.TcpKeepalivePacketData; -import android.net.TcpKeepalivePacketDataParcelable; -import android.os.Build; -import android.system.OsConstants; -import android.util.Log; - -import com.android.net.module.util.IpUtils; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -/** - * Utility class to convert to/from keepalive data parcelables. - * - * TODO: move to networkstack-client library when it is moved to frameworks/libs/net. - * This class cannot go into other shared libraries as it depends on NetworkStack AIDLs. - * @hide - */ -public final class KeepalivePacketDataUtil { - private static final int IPV4_HEADER_LENGTH = 20; - private static final int IPV6_HEADER_LENGTH = 40; - private static final int TCP_HEADER_LENGTH = 20; - - private static final String TAG = KeepalivePacketDataUtil.class.getSimpleName(); - - /** - * Convert a NattKeepalivePacketData to a NattKeepalivePacketDataParcelable. - */ - @NonNull - public static NattKeepalivePacketDataParcelable toStableParcelable( - @NonNull NattKeepalivePacketData pkt) { - final NattKeepalivePacketDataParcelable parcel = new NattKeepalivePacketDataParcelable(); - final InetAddress srcAddress = pkt.getSrcAddress(); - final InetAddress dstAddress = pkt.getDstAddress(); - parcel.srcAddress = srcAddress.getAddress(); - parcel.srcPort = pkt.getSrcPort(); - parcel.dstAddress = dstAddress.getAddress(); - parcel.dstPort = pkt.getDstPort(); - return parcel; - } - - /** - * Convert a TcpKeepalivePacketData to a TcpKeepalivePacketDataParcelable. - */ - @NonNull - public static TcpKeepalivePacketDataParcelable toStableParcelable( - @NonNull TcpKeepalivePacketData pkt) { - final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable(); - final InetAddress srcAddress = pkt.getSrcAddress(); - final InetAddress dstAddress = pkt.getDstAddress(); - parcel.srcAddress = srcAddress.getAddress(); - parcel.srcPort = pkt.getSrcPort(); - parcel.dstAddress = dstAddress.getAddress(); - parcel.dstPort = pkt.getDstPort(); - parcel.seq = pkt.getTcpSeq(); - parcel.ack = pkt.getTcpAck(); - parcel.rcvWnd = pkt.getTcpWindow(); - parcel.rcvWndScale = pkt.getTcpWindowScale(); - parcel.tos = pkt.getIpTos(); - parcel.ttl = pkt.getIpTtl(); - return parcel; - } - - /** - * Factory method to create tcp keepalive packet structure. - * @hide - */ - public static TcpKeepalivePacketData fromStableParcelable( - TcpKeepalivePacketDataParcelable tcpDetails) throws InvalidPacketException { - final byte[] packet; - try { - if ((tcpDetails.srcAddress != null) && (tcpDetails.dstAddress != null) - && (tcpDetails.srcAddress.length == 4 /* V4 IP length */) - && (tcpDetails.dstAddress.length == 4 /* V4 IP length */)) { - packet = buildV4Packet(tcpDetails); - } else { - // TODO: support ipv6 - throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS); - } - return new TcpKeepalivePacketData( - InetAddress.getByAddress(tcpDetails.srcAddress), - tcpDetails.srcPort, - InetAddress.getByAddress(tcpDetails.dstAddress), - tcpDetails.dstPort, - packet, - tcpDetails.seq, tcpDetails.ack, tcpDetails.rcvWnd, tcpDetails.rcvWndScale, - tcpDetails.tos, tcpDetails.ttl); - } catch (UnknownHostException e) { - throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS); - } - - } - - /** - * Build ipv4 tcp keepalive packet, not including the link-layer header. - */ - // TODO : if this code is ever moved to the network stack, factorize constants with the ones - // over there. - private static byte[] buildV4Packet(TcpKeepalivePacketDataParcelable tcpDetails) { - final int length = IPV4_HEADER_LENGTH + TCP_HEADER_LENGTH; - ByteBuffer buf = ByteBuffer.allocate(length); - buf.order(ByteOrder.BIG_ENDIAN); - buf.put((byte) 0x45); // IP version and IHL - buf.put((byte) tcpDetails.tos); // TOS - buf.putShort((short) length); - buf.putInt(0x00004000); // ID, flags=DF, offset - buf.put((byte) tcpDetails.ttl); // TTL - buf.put((byte) OsConstants.IPPROTO_TCP); - final int ipChecksumOffset = buf.position(); - buf.putShort((short) 0); // IP checksum - buf.put(tcpDetails.srcAddress); - buf.put(tcpDetails.dstAddress); - buf.putShort((short) tcpDetails.srcPort); - buf.putShort((short) tcpDetails.dstPort); - buf.putInt(tcpDetails.seq); // Sequence Number - buf.putInt(tcpDetails.ack); // ACK - buf.putShort((short) 0x5010); // TCP length=5, flags=ACK - buf.putShort((short) (tcpDetails.rcvWnd >> tcpDetails.rcvWndScale)); // Window size - final int tcpChecksumOffset = buf.position(); - buf.putShort((short) 0); // TCP checksum - // URG is not set therefore the urgent pointer is zero. - buf.putShort((short) 0); // Urgent pointer - - buf.putShort(ipChecksumOffset, com.android.net.module.util.IpUtils.ipChecksum(buf, 0)); - buf.putShort(tcpChecksumOffset, IpUtils.tcpChecksum( - buf, 0, IPV4_HEADER_LENGTH, TCP_HEADER_LENGTH)); - - return buf.array(); - } - - // TODO: add buildV6Packet. - - /** - * Get a {@link TcpKeepalivePacketDataParcelable} from {@link KeepalivePacketData}, if the - * generic class actually contains TCP keepalive data. - * - * @deprecated This method is used on R platforms where android.net.TcpKeepalivePacketData was - * not yet system API. Newer platforms should use android.net.TcpKeepalivePacketData directly. - * - * @param data A {@link KeepalivePacketData} that may contain TCP keepalive data. - * @return A parcelable containing TCP keepalive data, or null if the input data does not - * contain TCP keepalive data. - */ - @Deprecated - @SuppressWarnings("AndroidFrameworkCompatChange") // API version check used to Log.wtf - @Nullable - public static TcpKeepalivePacketDataParcelable parseTcpKeepalivePacketData( - @Nullable KeepalivePacketData data) { - if (data == null) return null; - - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) { - Log.wtf(TAG, "parseTcpKeepalivePacketData should not be used after R, use " - + "TcpKeepalivePacketData instead."); - } - - // Reconstruct TcpKeepalivePacketData from the packet contained in KeepalivePacketData - final ByteBuffer buffer = ByteBuffer.wrap(data.getPacket()); - buffer.order(ByteOrder.BIG_ENDIAN); - - // Most of the fields are accessible from the KeepalivePacketData superclass: instead of - // using Struct to parse everything, just extract the extra fields necessary for - // TcpKeepalivePacketData. - final int tcpSeq; - final int tcpAck; - final int wndSize; - final int ipTos; - final int ttl; - try { - // This only support IPv4, because TcpKeepalivePacketData only supports IPv4 for R and - // below, and this method should not be used on newer platforms. - tcpSeq = buffer.getInt(IPV4_HEADER_LENGTH + 4); - tcpAck = buffer.getInt(IPV4_HEADER_LENGTH + 8); - wndSize = buffer.getShort(IPV4_HEADER_LENGTH + 14); - ipTos = buffer.get(1); - ttl = buffer.get(8); - } catch (IndexOutOfBoundsException e) { - return null; - } - - final TcpKeepalivePacketDataParcelable p = new TcpKeepalivePacketDataParcelable(); - p.srcAddress = data.getSrcAddress().getAddress(); - p.srcPort = data.getSrcPort(); - p.dstAddress = data.getDstAddress().getAddress(); - p.dstPort = data.getDstPort(); - p.seq = tcpSeq; - p.ack = tcpAck; - // TcpKeepalivePacketData could actually use non-zero wndScale, but this does not affect - // actual functionality as generated packets will be the same (no wndScale option added) - p.rcvWnd = wndSize; - p.rcvWndScale = 0; - p.tos = ipTos; - p.ttl = ttl; - return p; - } -} diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index 9706d7f5f78d..e3e2708eb788 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -37,6 +37,7 @@ import android.os.UserManager; import android.provider.DeviceConfig; import android.util.Log; +import com.android.internal.R; import com.android.server.IoThread; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -302,8 +303,15 @@ public final class ProfcollectForwardingService extends SystemService { return; } + if (!getUploaderEnabledConfig(getContext())) { + return; + } + new Thread(() -> { try { + Context context = getContext(); + final String uploaderPkg = getUploaderPackageName(context); + final String uploaderAction = getUploaderActionName(context); String reportUuid = mIProfcollect.report(); final int profileId = getBBProfileId(); @@ -317,13 +325,12 @@ public final class ProfcollectForwardingService extends SystemService { } Intent uploadIntent = - new Intent("com.google.android.apps.betterbug.intent.action.UPLOAD_PROFILE") - .setPackage("com.google.android.apps.internal.betterbug") + new Intent(uploaderAction) + .setPackage(uploaderPkg) .putExtra("EXTRA_DESTINATION", "PROFCOLLECT") .putExtra("EXTRA_PACKAGE_NAME", getContext().getPackageName()) .putExtra("EXTRA_PROFILE_PATH", reportPath) .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); - Context context = getContext(); List<ResolveInfo> receivers = context.getPackageManager().queryBroadcastReceivers(uploadIntent, 0); @@ -356,4 +363,19 @@ public final class ProfcollectForwardingService extends SystemService { } return UserHandle.USER_SYSTEM; } + + private boolean getUploaderEnabledConfig(Context context) { + return context.getResources().getBoolean( + R.bool.config_profcollectReportUploaderEnabled); + } + + private String getUploaderPackageName(Context context) { + return context.getResources().getString( + R.string.config_defaultProfcollectReportUploaderApp); + } + + private String getUploaderActionName(Context context) { + return context.getResources().getString( + R.string.config_defaultProfcollectReportUploaderAction); + } } diff --git a/services/tests/PackageManagerServiceTests/appenumeration/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp new file mode 100644 index 000000000000..479ef8e5f188 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/appenumeration/Android.bp @@ -0,0 +1,36 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "AppEnumerationInternalTests", + srcs: [ + "src/**/*.java", + ], + static_libs: [ + "compatibility-device-util-axt", + "androidx.test.runner", + "truth-prebuilt", + ], + platform_apis: true, + test_suites: ["device-tests"], +} diff --git a/services/tests/PackageManagerServiceTests/appenumeration/AndroidManifest.xml b/services/tests/PackageManagerServiceTests/appenumeration/AndroidManifest.xml new file mode 100644 index 000000000000..2039aaa20945 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/appenumeration/AndroidManifest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.pm.test.appenumeration"> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.pm.test.appenumeration" + android:label="Package Manager Service Tests for app enumeration"> + </instrumentation> + +</manifest> + diff --git a/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml b/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml new file mode 100644 index 000000000000..6f168a3888b3 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/appenumeration/AndroidTest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2021 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<configuration description="Runs Package Manager Service App Enumeration Tests."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-instrumentation" /> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="AppEnumerationInternalTests.apk" /> + </target_preparer> + + <!-- Create place to store apks --> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="mkdir -p /data/local/tmp/appenumerationtests" /> + <option name="teardown-command" value="rm -rf /data/local/tmp/appenumerationtests"/> + </target_preparer> + + <!-- Load additional APKs onto device --> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> + <option name="push" value="AppEnumerationSyncProviderTestApp.apk->/data/local/tmp/appenumerationtests/AppEnumerationSyncProviderTestApp.apk" /> + </target_preparer> + + <option name="test-tag" value="AppEnumerationInternalTest" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.server.pm.test.appenumeration" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false" /> + </test> +</configuration> diff --git a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java new file mode 100644 index 000000000000..933784560410 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/AppEnumerationInternalTests.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.test.appenumeration; + +import static com.android.compatibility.common.util.ShellUtils.runShellCommand; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.AppGlobals; +import android.content.pm.IPackageManager; +import android.content.pm.ProviderInfo; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Application enumeration tests for the internal apis of package manager service. + */ +@RunWith(AndroidJUnit4.class) +public class AppEnumerationInternalTests { + private static final String TEST_DATA_PATH = "/data/local/tmp/appenumerationtests/"; + private static final String SYNC_PROVIDER_APK_PATH = + TEST_DATA_PATH + "AppEnumerationSyncProviderTestApp.apk"; + private static final String SYNC_PROVIDER_PKG_NAME = "com.android.appenumeration.syncprovider"; + private static final String SYNC_PROVIDER_AUTHORITY = SYNC_PROVIDER_PKG_NAME; + + private IPackageManager mIPackageManager; + + @Before + public void setup() { + mIPackageManager = AppGlobals.getPackageManager(); + } + + @After + public void tearDown() throws Exception { + uninstallPackage(SYNC_PROVIDER_PKG_NAME); + } + + @Test + public void querySyncProviders_canSeeForceQueryable() throws Exception { + final List<String> names = new ArrayList<>(); + final List<ProviderInfo> infos = new ArrayList<>(); + installPackage(SYNC_PROVIDER_APK_PATH, true /* forceQueryable */); + mIPackageManager.querySyncProviders(names, infos); + + assertThat(names).contains(SYNC_PROVIDER_AUTHORITY); + assertThat(infos.stream().map(info -> info.packageName).collect(Collectors.toList())) + .contains(SYNC_PROVIDER_PKG_NAME); + } + + @Test + public void querySyncProviders_cannotSeeSyncProvider() throws Exception { + final List<String> names = new ArrayList<>(); + final List<ProviderInfo> infos = new ArrayList<>(); + installPackage(SYNC_PROVIDER_APK_PATH, false /* forceQueryable */); + mIPackageManager.querySyncProviders(names, infos); + + assertThat(names).doesNotContain(SYNC_PROVIDER_AUTHORITY); + assertThat(infos.stream().map(info -> info.packageName).collect(Collectors.toList())) + .doesNotContain(SYNC_PROVIDER_PKG_NAME); + } + + private static void installPackage(String apkPath, boolean forceQueryable) { + final StringBuilder cmd = new StringBuilder("pm install "); + if (forceQueryable) { + cmd.append("--force-queryable "); + } + cmd.append(apkPath); + final String result = runShellCommand(cmd.toString()); + assertThat(result.trim()).contains("Success"); + } + + private static void uninstallPackage(String packageName) { + runShellCommand("pm uninstall " + packageName); + } +} diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp new file mode 100644 index 000000000000..64239b4c6b2d --- /dev/null +++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/Android.bp @@ -0,0 +1,36 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test_helper_app { + name: "AppEnumerationSyncProviderTestApp", + srcs: ["src/**/*.java"], + manifest: "AndroidManifest-syncprovider.xml", + dex_preopt: { + enabled: false, + }, + optimize: { + enabled: false, + }, + test_suites: ["device-tests"], + platform_apis: true, +} diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-syncprovider.xml b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-syncprovider.xml new file mode 100644 index 000000000000..de8439384b54 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/AndroidManifest-syncprovider.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.appenumeration.syncprovider"> + <application> + <provider android:name="com.android.appenumeration.testapp.DummyProvider" + android:authorities="com.android.appenumeration.syncprovider" + android:syncable="true" android:exported="true"> + <intent-filter> + <action android:name="com.android.appenumeration.action.PROVIDER"/> + </intent-filter> + </provider> + </application> +</manifest> diff --git a/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/src/com/android/appenumeration/testapp/DummyProvider.java b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/src/com/android/appenumeration/testapp/DummyProvider.java new file mode 100644 index 000000000000..e8b610969c2f --- /dev/null +++ b/services/tests/PackageManagerServiceTests/appenumeration/test-apps/target/src/com/android/appenumeration/testapp/DummyProvider.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.appenumeration.testapp; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +public class DummyProvider extends ContentProvider { + @Override + public boolean onCreate() { + return true; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public String getType(Uri uri) { + return "text/plain"; + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java index 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/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index 280204dfd481..eab1afbd931e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -1914,11 +1914,11 @@ public class AlarmManagerServiceTest { public void hasScheduleExactAlarmBinderCallChangeDisabled() throws RemoteException { mockChangeEnabled(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, false); - mockExactAlarmPermissionGrant(true, false, MODE_DEFAULT); - assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + mockExactAlarmPermissionGrant(false, true, MODE_DEFAULT); + assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); - mockExactAlarmPermissionGrant(true, true, MODE_ALLOWED); - assertFalse(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); + mockExactAlarmPermissionGrant(true, false, MODE_ERRORED); + assertTrue(mBinder.hasScheduleExactAlarm(TEST_CALLING_PACKAGE, TEST_CALLING_USER)); } private void mockChangeEnabled(long changeId, boolean enabled) { 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/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java index 558fb309ad98..976a588273a7 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java @@ -18,7 +18,11 @@ package com.android.server.pm; import static com.google.common.truth.Truth.assertWithMessage; +import static org.junit.Assert.fail; + import static java.lang.reflect.Modifier.isFinal; +import static java.lang.reflect.Modifier.isPrivate; +import static java.lang.reflect.Modifier.isProtected; import static java.lang.reflect.Modifier.isPublic; import static java.lang.reflect.Modifier.isStatic; @@ -44,9 +48,12 @@ import org.junit.runner.RunWith; import java.io.File; import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.regex.Pattern; @@ -393,6 +400,178 @@ public class PackageManagerServiceTest { Assert.assertEquals(3600000003L, multiPackage[1].timeouts.maxPendingTimeUs); } + // Report an error from the Computer structure validation test. + private void flag(String name, String msg) { + fail(name + " " + msg); + } + + // Return a string that identifies a Method. This is not very efficient but it is not + // called very often. + private String displayName(Method m) { + String r = m.getName(); + String p = Arrays.toString(m.getGenericParameterTypes()) + .replaceAll("([a-zA-Z0-9]+\\.)+", "") + .replace("class ", "") + .replaceAll("^\\[", "(") + .replaceAll("\\]$", ")"); + return r + p; + } + + // Match a method to an array of Methods. Matching is on method signature: name and + // parameter types. If a method in the declared array matches, return it. Otherwise + // return null. + private Method matchMethod(Method m, Method[] declared) { + String n = m.getName(); + Type[] t = m.getGenericParameterTypes(); + for (int i = 0; i < declared.length; i++) { + Method l = declared[i]; + if (l != null && l.getName().equals(n) + && Arrays.equals(l.getGenericParameterTypes(), t)) { + Method result = l; + // Set the method to null since it has been visited already. + declared[i] = null; + return result; + } + } + return null; + } + + // Return the boolean locked value. A null return means the annotation was not + // found. This method will fail if the annotation is found but is not one of the + // known constants. + private Boolean getOverride(Method m) { + final String name = "Computer." + displayName(m); + final PackageManagerService.Computer.LiveImplementation annotation = + m.getAnnotation(PackageManagerService.Computer.LiveImplementation.class); + if (annotation == null) { + return null; + } + final int override = annotation.override(); + if (override == PackageManagerService.Computer.LiveImplementation.MANDATORY) { + return true; + } else if (override == PackageManagerService.Computer.LiveImplementation.NOT_ALLOWED) { + return false; + } else { + flag(name, "invalid Live value: " + override); + return null; + } + } + + @Test + public void testComputerStructure() { + // Verify that Copmuter methods are properly annotated and that ComputerLocked is + // properly populated per annotations. + // Call PackageManagerService.validateComputer(); + Class base = PackageManagerService.Computer.class; + + HashMap<Method, Boolean> methodType = new HashMap<>(); + + // Verify that all Computer methods are annotated and that the annotation + // parameter locked() is valid. + for (Method m : base.getDeclaredMethods()) { + final String name = "Computer." + displayName(m); + Boolean override = getOverride(m); + if (override == null) { + flag(name, "missing required Live annotation"); + } + methodType.put(m, override); + } + + Class coreClass = PackageManagerService.ComputerEngine.class; + final Method[] coreMethods = coreClass.getDeclaredMethods(); + + // Examine every method in the core. If it inherits from a base method it must be + // "public final" if the base is NOT_ALLOWED or "public" if the base is MANDATORY. + // If the core method does not inherit from the base then it must be either + // private or protected. + for (Method m : base.getDeclaredMethods()) { + String name = "Computer." + displayName(m); + final boolean locked = methodType.get(m); + final Method core = matchMethod(m, coreMethods); + if (core == null) { + flag(name, "not overridden in ComputerEngine"); + continue; + } + name = "ComputerEngine." + displayName(m); + final int modifiers = core.getModifiers(); + if (!locked) { + if (!isPublic(modifiers)) { + flag(name, "is not public"); + } + if (!isFinal(modifiers)) { + flag(name, "is not final"); + } + } + } + // Any methods left in the coreMethods array must be private or protected. + // Protected methods must be overridden (and final) in the live list. + Method[] coreHelpers = new Method[coreMethods.length]; + int coreIndex = 0; + for (Method m : coreMethods) { + if (m != null) { + final String name = "ComputerEngine." + displayName(m); + final int modifiers = m.getModifiers(); + if (isPrivate(modifiers)) { + // Okay + } else if (isProtected(modifiers)) { + coreHelpers[coreIndex++] = m; + } else { + flag(name, "is neither private nor protected"); + } + } + } + + Class liveClass = PackageManagerService.ComputerLocked.class; + final Method[] liveMethods = liveClass.getDeclaredMethods(); + + // Examine every method in the live list. Every method must be final and must + // inherit either from base or core. If the method inherits from a base method + // then the base must be MANDATORY. + for (Method m : base.getDeclaredMethods()) { + String name = "Computer." + displayName(m); + final boolean locked = methodType.get(m); + final Method live = matchMethod(m, liveMethods); + if (live == null) { + if (locked) { + flag(name, "not overridden in ComputerLocked"); + } + continue; + } + if (!locked) { + flag(name, "improperly overridden in ComputerLocked"); + continue; + } + + name = "ComputerLocked." + displayName(m); + final int modifiers = live.getModifiers(); + if (!locked) { + if (!isPublic(modifiers)) { + flag(name, "is not public"); + } + if (!isFinal(modifiers)) { + flag(name, "is not final"); + } + } + } + for (Method m : coreHelpers) { + if (m == null) { + continue; + } + String name = "ComputerLocked." + displayName(m); + final Method live = matchMethod(m, liveMethods); + if (live == null) { + flag(name, "is not overridden in ComputerLocked"); + continue; + } + } + for (Method m : liveMethods) { + if (m != null) { + String name = "ComputerLocked." + displayName(m); + flag(name, "illegal local method"); + } + } + } + private static PerPackageReadTimeouts[] getPerPackageReadTimeouts(String knownDigestersList) { final String defaultTimeouts = "3600000001:3600000002:3600000003"; List<PerPackageReadTimeouts> result = PerPackageReadTimeouts.parseDigestersList( diff --git a/services/tests/servicestests/src/com/android/server/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/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index a24691791938..4c157c0e0a73 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -1179,6 +1179,36 @@ public class AppStandbyControllerTests { assertBucket(STANDBY_BUCKET_RESTRICTED); } + /** + * Test that an app that "timed out" into the RESTRICTED bucket can be raised out by system + * interaction. + */ + @Test + public void testSystemInteractionOverridesRestrictedTimeout() throws Exception { + reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); + assertBucket(STANDBY_BUCKET_ACTIVE); + + // Long enough that it could have timed out into RESTRICTED. + mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4; + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_RESTRICTED); + + // Report system interaction. + mInjector.mElapsedRealtime += 1000; + reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1); + + // Ensure that it's raised out of RESTRICTED for the system interaction elevation duration. + assertBucket(STANDBY_BUCKET_ACTIVE); + mInjector.mElapsedRealtime += 1000; + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_ACTIVE); + + // Elevation duration over. Should fall back down. + mInjector.mElapsedRealtime += 10 * MINUTE_MS; + mController.checkIdleStates(USER_ID); + assertBucket(STANDBY_BUCKET_RESTRICTED); + } + @Test public void testRestrictedBucketDisabled() throws Exception { mInjector.mIsRestrictedBucketEnabled = false; diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java index 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/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java index 855012459bd6..7d24a2f5845e 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java @@ -33,6 +33,7 @@ import android.app.ActivityManager; import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.media.AudioManager; import android.os.Handler; import android.os.PowerManagerInternal; @@ -69,6 +70,7 @@ import org.mockito.junit.MockitoRule; public class VibrationSettingsTest { private static final int UID = 1; + private static final int USER_OPERATION_TIMEOUT_MILLIS = 60_000; // 1 min private static final PowerSaveState NORMAL_POWER_STATE = new PowerSaveState.Builder().build(); private static final PowerSaveState LOW_POWER_STATE = new PowerSaveState.Builder() .setBatterySaverEnabled(true).build(); @@ -408,6 +410,25 @@ public class VibrationSettingsTest { } @Test + public void getCurrentIntensity_updateTriggeredAfterUserSwitched() { + mFakeVibrator.setDefaultRingVibrationIntensity(Vibrator.VIBRATION_INTENSITY_OFF); + setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, + Vibrator.VIBRATION_INTENSITY_HIGH); + assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH, + mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE)); + + // Switching user is not working with FakeSettingsProvider. + // Testing the broadcast flow manually. + Settings.System.putIntForUser(mContextSpy.getContentResolver(), + Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW, + UserHandle.USER_CURRENT); + mVibrationSettings.mUserReceiver.onReceive(mContextSpy, + new Intent(Intent.ACTION_USER_SWITCHED)); + assertEquals(Vibrator.VIBRATION_INTENSITY_LOW, + mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE)); + } + + @Test public void getFallbackEffect_returnsEffectsFromSettings() { assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_TICK)); assertNotNull(mVibrationSettings.getFallbackEffect(VibrationEffect.EFFECT_TEXTURE_TICK)); diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java index 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..bf0ed713e4c3 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -90,7 +90,7 @@ import android.os.Bundle; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.UserHandle; -import android.provider.Settings; +import android.os.UserManager; import android.provider.Settings.Global; import android.provider.Settings.Secure; import android.service.notification.ConversationChannelWrapper; @@ -137,6 +137,7 @@ import java.util.concurrent.ThreadLocalRandom; @RunWith(AndroidJUnit4.class) public class PreferencesHelperTest extends UiServiceTestCase { private static final int UID_N_MR1 = 0; + private static final int UID_HEADLESS = 1000000; private static final UserHandle USER = UserHandle.of(0); private static final int UID_O = 1111; private static final int UID_P = 2222; @@ -1495,11 +1496,13 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testCreateAndDeleteCanChannelsBypassDnd() throws Exception { + int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1; + // create notification channel that can't bypass dnd // expected result: areChannelsBypassingDnd = false // setNotificationPolicy isn't called since areChannelsBypassingDnd was already false NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW); - mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false); + mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false); assertFalse(mHelper.areChannelsBypassingDnd()); verify(mMockZenModeHelper, never()).setNotificationPolicy(any()); resetZenModeHelper(); @@ -1508,18 +1511,18 @@ public class PreferencesHelperTest extends UiServiceTestCase { // expected result: areChannelsBypassingDnd = true NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW); channel2.setBypassDnd(true); - mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, true, true); + mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true); assertTrue(mHelper.areChannelsBypassingDnd()); verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any()); resetZenModeHelper(); // delete channels - mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel.getId()); + mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel.getId()); assertTrue(mHelper.areChannelsBypassingDnd()); // channel2 can still bypass DND verify(mMockZenModeHelper, never()).setNotificationPolicy(any()); resetZenModeHelper(); - mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2.getId()); + mHelper.deleteNotificationChannel(PKG_N_MR1, uid, channel2.getId()); assertFalse(mHelper.areChannelsBypassingDnd()); verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any()); resetZenModeHelper(); @@ -1527,11 +1530,13 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testUpdateCanChannelsBypassDnd() throws Exception { + int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1; + // create notification channel that can't bypass dnd // expected result: areChannelsBypassingDnd = false // setNotificationPolicy isn't called since areChannelsBypassingDnd was already false NotificationChannel channel = new NotificationChannel("id1", "name1", IMPORTANCE_LOW); - mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false); + mHelper.createNotificationChannel(PKG_N_MR1, uid, channel, true, false); assertFalse(mHelper.areChannelsBypassingDnd()); verify(mMockZenModeHelper, never()).setNotificationPolicy(any()); resetZenModeHelper(); @@ -1539,7 +1544,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { // update channel so it CAN bypass dnd: // expected result: areChannelsBypassingDnd = true channel.setBypassDnd(true); - mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true); + mHelper.updateNotificationChannel(PKG_N_MR1, uid, channel, true); assertTrue(mHelper.areChannelsBypassingDnd()); verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any()); resetZenModeHelper(); @@ -1547,7 +1552,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { // update channel so it can't bypass dnd: // expected result: areChannelsBypassingDnd = false channel.setBypassDnd(false); - mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true); + mHelper.updateNotificationChannel(PKG_N_MR1, uid, channel, true); assertFalse(mHelper.areChannelsBypassingDnd()); verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any()); resetZenModeHelper(); @@ -3333,6 +3338,17 @@ 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/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index 39a59c9e5064..afd0ecf0fdee 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -655,6 +655,36 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { assertFalse(win1.mHasSurface); } + @Test + public void testCancelForRotation_ReorderToTop() throws Exception { + mWm.setRecentsAnimationController(mController); + final ActivityRecord activity = createActivityRecord(mDefaultDisplay); + final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1"); + activity.addWindow(win1); + + mController.addAnimation(activity.getTask(), false /* isRecentTaskInvisible */); + mController.setWillFinishToHome(true); + mController.cancelAnimationForDisplayChange(); + + verify(mMockRunner).onAnimationCanceled(any()); + verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_TOP, false); + } + + @Test + public void testCancelForRotation_ReorderToOriginalPosition() throws Exception { + mWm.setRecentsAnimationController(mController); + final ActivityRecord activity = createActivityRecord(mDefaultDisplay); + final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1"); + activity.addWindow(win1); + + mController.addAnimation(activity.getTask(), false /* isRecentTaskInvisible */); + mController.setWillFinishToHome(false); + mController.cancelAnimationForDisplayChange(); + + verify(mMockRunner).onAnimationCanceled(any()); + verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_ORIGINAL_POSITION, false); + } + private ActivityRecord createHomeActivity() { final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService) .setParentTask(mRootHomeTask) diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 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/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java index e9e201327aa4..97afc165624a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -214,8 +214,8 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { spyOn(mDisplayContent); spyOn(mDisplayContent.mInputMethodWindow); when(task.getDisplayContent().shouldImeAttachedToApp()).thenReturn(true); - // Intentionally set the IME window is in drawn state. - doReturn(true).when(mDisplayContent.mInputMethodWindow).isDrawn(); + // Intentionally set the IME window is in visible state. + doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible(); // Verify no NPE happens when calling createTaskSnapshot. try { final TaskSnapshot.Builder builder = new TaskSnapshot.Builder(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index 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); |